| // Copyright 2019 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package acme |
| |
| import ( |
| "context" |
| "encoding/json" |
| "fmt" |
| "net/http" |
| ) |
| |
| // DeactivateReg permanently disables an existing account associated with c.Key. |
| // A deactivated account can no longer request certificate issuance or access |
| // resources related to the account, such as orders or authorizations. |
| // |
| // It works only with RFC8555 compliant CAs. |
| func (c *Client) DeactivateReg(ctx context.Context) error { |
| url := string(c.accountKID(ctx)) |
| if url == "" { |
| return ErrNoAccount |
| } |
| req := json.RawMessage(`{"status": "deactivated"}`) |
| res, err := c.post(ctx, nil, url, req, wantStatus(http.StatusOK)) |
| if err != nil { |
| return err |
| } |
| res.Body.Close() |
| return nil |
| } |
| |
| // registerRFC is quivalent to c.Register but for RFC-compliant CAs. |
| // It expects c.Discover to have already been called. |
| // TODO: Implement externalAccountBinding. |
| func (c *Client) registerRFC(ctx context.Context, acct *Account, prompt func(tosURL string) bool) (*Account, error) { |
| c.cacheMu.Lock() // guard c.kid access |
| defer c.cacheMu.Unlock() |
| |
| req := struct { |
| TermsAgreed bool `json:"termsOfServiceAgreed,omitempty"` |
| Contact []string `json:"contact,omitempty"` |
| }{ |
| Contact: acct.Contact, |
| } |
| if c.dir.Terms != "" { |
| req.TermsAgreed = prompt(c.dir.Terms) |
| } |
| res, err := c.post(ctx, c.Key, c.dir.RegURL, req, wantStatus( |
| http.StatusOK, // account with this key already registered |
| http.StatusCreated, // new account created |
| )) |
| if err != nil { |
| return nil, err |
| } |
| |
| defer res.Body.Close() |
| a, err := responseAccount(res) |
| if err != nil { |
| return nil, err |
| } |
| // Cache Account URL even if we return an error to the caller. |
| // It is by all means a valid and usable "kid" value for future requests. |
| c.kid = keyID(a.URI) |
| if res.StatusCode == http.StatusOK { |
| return nil, ErrAccountAlreadyExists |
| } |
| return a, nil |
| } |
| |
| // updateGegRFC is equivalent to c.UpdateReg but for RFC-compliant CAs. |
| // It expects c.Discover to have already been called. |
| func (c *Client) updateRegRFC(ctx context.Context, a *Account) (*Account, error) { |
| url := string(c.accountKID(ctx)) |
| if url == "" { |
| return nil, ErrNoAccount |
| } |
| req := struct { |
| Contact []string `json:"contact,omitempty"` |
| }{ |
| Contact: a.Contact, |
| } |
| res, err := c.post(ctx, nil, url, req, wantStatus(http.StatusOK)) |
| if err != nil { |
| return nil, err |
| } |
| defer res.Body.Close() |
| return responseAccount(res) |
| } |
| |
| // getGegRFC is equivalent to c.GetReg but for RFC-compliant CAs. |
| // It expects c.Discover to have already been called. |
| func (c *Client) getRegRFC(ctx context.Context) (*Account, error) { |
| req := json.RawMessage(`{"onlyReturnExisting": true}`) |
| res, err := c.post(ctx, c.Key, c.dir.RegURL, req, wantStatus(http.StatusOK)) |
| if e, ok := err.(*Error); ok && e.ProblemType == "urn:ietf:params:acme:error:accountDoesNotExist" { |
| return nil, ErrNoAccount |
| } |
| if err != nil { |
| return nil, err |
| } |
| |
| defer res.Body.Close() |
| return responseAccount(res) |
| } |
| |
| func responseAccount(res *http.Response) (*Account, error) { |
| var v struct { |
| Status string |
| Contact []string |
| Orders string |
| } |
| if err := json.NewDecoder(res.Body).Decode(&v); err != nil { |
| return nil, fmt.Errorf("acme: invalid response: %v", err) |
| } |
| return &Account{ |
| URI: res.Header.Get("Location"), |
| Status: v.Status, |
| Contact: v.Contact, |
| OrdersURL: v.Orders, |
| }, nil |
| } |