123 lines
3.4 KiB
Go
123 lines
3.4 KiB
Go
// 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
|
|
}
|