registar.go raw
1 package registration
2
3 import (
4 "errors"
5 "net/http"
6
7 "github.com/go-acme/lego/v4/acme"
8 "github.com/go-acme/lego/v4/acme/api"
9 "github.com/go-acme/lego/v4/log"
10 )
11
12 const mailTo = "mailto:"
13
14 // Resource represents all important information about a registration
15 // of which the client needs to keep track itself.
16 // WARNING: will be removed in the future (acme.ExtendedAccount), https://github.com/go-acme/lego/issues/855.
17 type Resource struct {
18 Body acme.Account `json:"body"`
19 URI string `json:"uri,omitempty"`
20 }
21
22 type RegisterOptions struct {
23 TermsOfServiceAgreed bool
24 }
25
26 type RegisterEABOptions struct {
27 TermsOfServiceAgreed bool
28 Kid string
29 HmacEncoded string
30 }
31
32 type Registrar struct {
33 core *api.Core
34 user User
35 }
36
37 func NewRegistrar(core *api.Core, user User) *Registrar {
38 return &Registrar{
39 core: core,
40 user: user,
41 }
42 }
43
44 // Register the current account to the ACME server.
45 func (r *Registrar) Register(options RegisterOptions) (*Resource, error) {
46 if r == nil || r.user == nil {
47 return nil, errors.New("acme: cannot register a nil client or user")
48 }
49
50 accMsg := acme.Account{
51 TermsOfServiceAgreed: options.TermsOfServiceAgreed,
52 Contact: []string{},
53 }
54
55 if r.user.GetEmail() != "" {
56 log.Infof("acme: Registering account for %s", r.user.GetEmail())
57 accMsg.Contact = []string{mailTo + r.user.GetEmail()}
58 }
59
60 account, err := r.core.Accounts.New(accMsg)
61 if err != nil {
62 // seems impossible
63 errorDetails := &acme.ProblemDetails{}
64 if !errors.As(err, &errorDetails) || errorDetails.HTTPStatus != http.StatusConflict {
65 return nil, err
66 }
67 }
68
69 return &Resource{URI: account.Location, Body: account.Account}, nil
70 }
71
72 // RegisterWithExternalAccountBinding Register the current account to the ACME server.
73 func (r *Registrar) RegisterWithExternalAccountBinding(options RegisterEABOptions) (*Resource, error) {
74 accMsg := acme.Account{
75 TermsOfServiceAgreed: options.TermsOfServiceAgreed,
76 Contact: []string{},
77 }
78
79 if r.user.GetEmail() != "" {
80 log.Infof("acme: Registering account for %s", r.user.GetEmail())
81 accMsg.Contact = []string{mailTo + r.user.GetEmail()}
82 }
83
84 account, err := r.core.Accounts.NewEAB(accMsg, options.Kid, options.HmacEncoded)
85 if err != nil {
86 // seems impossible
87 errorDetails := &acme.ProblemDetails{}
88 if !errors.As(err, &errorDetails) || errorDetails.HTTPStatus != http.StatusConflict {
89 return nil, err
90 }
91 }
92
93 return &Resource{URI: account.Location, Body: account.Account}, nil
94 }
95
96 // QueryRegistration runs a POST request on the client's registration and returns the result.
97 //
98 // This is similar to the Register function,
99 // but acting on an existing registration link and resource.
100 func (r *Registrar) QueryRegistration() (*Resource, error) {
101 if r == nil || r.user == nil || r.user.GetRegistration() == nil {
102 return nil, errors.New("acme: cannot query the registration of a nil client or user")
103 }
104
105 // Log the URL here instead of the email as the email may not be set
106 log.Infof("acme: Querying account for %s", r.user.GetRegistration().URI)
107
108 account, err := r.core.Accounts.Get(r.user.GetRegistration().URI)
109 if err != nil {
110 return nil, err
111 }
112
113 return &Resource{
114 Body: account,
115 // Location: header is not returned so this needs to be populated off of existing URI
116 URI: r.user.GetRegistration().URI,
117 }, nil
118 }
119
120 // UpdateRegistration update the user registration on the ACME server.
121 func (r *Registrar) UpdateRegistration(options RegisterOptions) (*Resource, error) {
122 if r == nil || r.user == nil {
123 return nil, errors.New("acme: cannot update a nil client or user")
124 }
125
126 accMsg := acme.Account{
127 TermsOfServiceAgreed: options.TermsOfServiceAgreed,
128 Contact: []string{},
129 }
130
131 if r.user.GetEmail() != "" {
132 log.Infof("acme: Registering account for %s", r.user.GetEmail())
133 accMsg.Contact = []string{mailTo + r.user.GetEmail()}
134 }
135
136 accountURL := r.user.GetRegistration().URI
137
138 account, err := r.core.Accounts.Update(accountURL, accMsg)
139 if err != nil {
140 return nil, err
141 }
142
143 return &Resource{URI: accountURL, Body: account}, nil
144 }
145
146 // DeleteRegistration deletes the client's user registration from the ACME server.
147 func (r *Registrar) DeleteRegistration() error {
148 if r == nil || r.user == nil {
149 return errors.New("acme: cannot unregister a nil client or user")
150 }
151
152 log.Infof("acme: Deleting account for %s", r.user.GetEmail())
153
154 return r.core.Accounts.Deactivate(r.user.GetRegistration().URI)
155 }
156
157 // ResolveAccountByKey will attempt to look up an account using the given account key
158 // and return its registration resource.
159 func (r *Registrar) ResolveAccountByKey() (*Resource, error) {
160 log.Infof("acme: Trying to resolve account by key")
161
162 accMsg := acme.Account{OnlyReturnExisting: true}
163
164 account, err := r.core.Accounts.New(accMsg)
165 if err != nil {
166 return nil, err
167 }
168
169 return &Resource{URI: account.Location, Body: account.Account}, nil
170 }
171