boulder/vendor/github.com/eggsampler/acme/v3/account.go

145 lines
4.1 KiB
Go

package acme
import (
"crypto"
"encoding/json"
"errors"
"fmt"
"net/http"
"reflect"
)
// NewAccount registers a new account with the acme service
// Note this function is essentially deprecated and only present for backwards compatibility.
// New programs should implement NewAccountOptions instead.
func (c Client) NewAccount(privateKey crypto.Signer, onlyReturnExisting, termsOfServiceAgreed bool, contact ...string) (Account, error) {
var opts []NewAccountOptionFunc
if onlyReturnExisting {
opts = append(opts, NewAcctOptOnlyReturnExisting())
}
if termsOfServiceAgreed {
opts = append(opts, NewAcctOptAgreeTOS())
}
if len(contact) > 0 {
opts = append(opts, NewAcctOptWithContacts(contact...))
}
return c.NewAccountOptions(privateKey, opts...)
}
// NewAccountOptions registers an account with an acme server with the provided options.
func (c Client) NewAccountOptions(privateKey crypto.Signer, options ...NewAccountOptionFunc) (Account, error) {
newAccountReq := NewAccountRequest{}
account := Account{}
for _, opt := range options {
if err := opt(privateKey, &account, &newAccountReq, c); err != nil {
return account, err
}
}
resp, err := c.post(c.dir.NewAccount, "", privateKey, newAccountReq, &account, http.StatusOK, http.StatusCreated)
if err != nil {
return account, err
}
account.URL = resp.Header.Get("Location")
account.PrivateKey = privateKey
if account.Thumbprint == "" {
account.Thumbprint, err = JWKThumbprint(account.PrivateKey.Public())
if err != nil {
return account, fmt.Errorf("acme: error computing account thumbprint: %v", err)
}
}
return account, nil
}
// UpdateAccount updates an existing account with the acme service.
func (c Client) UpdateAccount(account Account, contact ...string) (Account, error) {
var updateAccountReq interface{}
if !reflect.DeepEqual(account.Contact, contact) {
// Only provide a non-nil updateAccountReq when there is an update to be made.
updateAccountReq = struct {
Contact []string `json:"contact,omitempty"`
}{
Contact: contact,
}
} else {
// Otherwise use "" to trigger a POST-as-GET to fetch up-to-date account
// information from the acme service.
updateAccountReq = ""
}
_, err := c.post(account.URL, account.URL, account.PrivateKey, updateAccountReq, &account, http.StatusOK)
if err != nil {
return account, err
}
if account.Thumbprint == "" {
account.Thumbprint, err = JWKThumbprint(account.PrivateKey.Public())
if err != nil {
return account, fmt.Errorf("acme: error computing account thumbprint: %v", err)
}
}
return account, nil
}
// AccountKeyChange rolls over an account to a new key.
func (c Client) AccountKeyChange(account Account, newPrivateKey crypto.Signer) (Account, error) {
oldJwkKeyPub, err := jwkEncode(account.PrivateKey.Public())
if err != nil {
return account, fmt.Errorf("acme: error encoding new private key: %v", err)
}
keyChangeReq := struct {
Account string `json:"account"`
OldKey json.RawMessage `json:"oldKey"`
}{
Account: account.URL,
OldKey: []byte(oldJwkKeyPub),
}
innerJws, err := jwsEncodeJSON(keyChangeReq, newPrivateKey, "", "", c.dir.KeyChange)
if err != nil {
return account, fmt.Errorf("acme: error encoding inner jws: %v", err)
}
if _, err := c.post(c.dir.KeyChange, account.URL, account.PrivateKey, json.RawMessage(innerJws), nil, http.StatusOK); err != nil {
return account, err
}
account.PrivateKey = newPrivateKey
return account, nil
}
// DeactivateAccount deactivates a given account.
func (c Client) DeactivateAccount(account Account) (Account, error) {
deactivateReq := struct {
Status string `json:"status"`
}{
Status: "deactivated",
}
_, err := c.post(account.URL, account.URL, account.PrivateKey, deactivateReq, &account, http.StatusOK)
return account, err
}
// FetchOrderList fetches a list of orders from the account url provided in the account Orders field
func (c Client) FetchOrderList(account Account) (OrderList, error) {
orderList := OrderList{}
if account.Orders == "" {
return orderList, errors.New("no order list for account")
}
_, err := c.post(account.Orders, account.URL, account.PrivateKey, "", &orderList, http.StatusOK)
return orderList, err
}