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