Refactor retrieval of provisioner into middleware

This commit is contained in:
Herman Slatman 2021-10-08 14:29:44 +02:00
parent 0afea2e957
commit f34d68897a
No known key found for this signature in database
GPG key ID: F4D8A44EA0A75A4F
3 changed files with 32 additions and 48 deletions

View file

@ -15,15 +15,11 @@ import (
// CreateExternalAccountKeyRequest is the type for POST /admin/acme/eab requests
type CreateExternalAccountKeyRequest struct {
Provisioner string `json:"provisioner"`
Reference string `json:"reference"`
}
// Validate validates a new ACME EAB Key request body.
func (r *CreateExternalAccountKeyRequest) Validate() error {
if r.Provisioner == "" {
return admin.NewError(admin.ErrorBadRequestType, "provisioner name cannot be empty")
}
return nil
}
@ -33,11 +29,26 @@ type GetExternalAccountKeysResponse struct {
NextCursor string `json:"nextCursor"`
}
// requireEABEnabled is a middleware that ensures ACME EAB is enabled
// before serving requests that act on ACME EAB credentials.
func (h *Handler) requireEABEnabled(next nextHTTP) nextHTTP {
return func(w http.ResponseWriter, r *http.Request) {
provisioner := chi.URLParam(r, "prov")
eabEnabled, err := h.provisionerHasEABEnabled(r.Context(), provisioner)
if err != nil {
api.WriteError(w, err)
return
}
if !eabEnabled {
api.WriteError(w, admin.NewError(admin.ErrorBadRequestType, "ACME EAB not enabled for provisioner %s", provisioner))
return
}
next(w, r)
}
}
// provisionerHasEABEnabled determines if the "requireEAB" setting for an ACME
// provisioner is set to true and thus has EAB enabled.
// TODO: rewrite this into a middleware for the ACME handlers? This probably requires
// ensuring that all the ACME EAB APIs that need the middleware work the same in terms
// of specifying the provisioner; probably a bit of refactoring required.
func (h *Handler) provisionerHasEABEnabled(ctx context.Context, provisionerName string) (bool, error) {
var (
p provisioner.Interface
@ -78,22 +89,12 @@ func (h *Handler) CreateExternalAccountKey(w http.ResponseWriter, r *http.Reques
return
}
provisioner := body.Provisioner
provisioner := chi.URLParam(r, "prov")
reference := body.Reference
eabEnabled, err := h.provisionerHasEABEnabled(r.Context(), provisioner)
if err != nil {
api.WriteError(w, err)
return
}
if !eabEnabled {
api.WriteError(w, admin.NewError(admin.ErrorBadRequestType, "ACME EAB not enabled for provisioner %s", provisioner))
return
}
if reference != "" {
k, err := h.acmeDB.GetExternalAccountKeyByReference(r.Context(), provisioner, reference)
// retrieving an EAB key from DB results in error if it doesn't exist, which is what we're looking for
if err == nil || k != nil {
err := admin.NewError(admin.ErrorBadRequestType, "an ACME EAB key for provisioner %s with reference %s already exists", provisioner, reference)
err.Status = 409
@ -123,17 +124,6 @@ func (h *Handler) DeleteExternalAccountKey(w http.ResponseWriter, r *http.Reques
provisioner := chi.URLParam(r, "prov")
keyID := chi.URLParam(r, "id")
eabEnabled, err := h.provisionerHasEABEnabled(r.Context(), provisioner)
if err != nil {
api.WriteError(w, err)
return
}
if !eabEnabled {
api.WriteError(w, admin.NewError(admin.ErrorBadRequestType, "ACME EAB not enabled for provisioner %s", provisioner))
return
}
if err := h.acmeDB.DeleteExternalAccountKey(r.Context(), provisioner, keyID); err != nil {
api.WriteError(w, admin.WrapErrorISE(err, "error deleting ACME EAB Key %s", keyID))
return
@ -147,17 +137,6 @@ func (h *Handler) GetExternalAccountKeys(w http.ResponseWriter, r *http.Request)
prov := chi.URLParam(r, "prov")
reference := chi.URLParam(r, "ref")
eabEnabled, err := h.provisionerHasEABEnabled(r.Context(), prov)
if err != nil {
api.WriteError(w, err)
return
}
if !eabEnabled {
api.WriteError(w, admin.NewError(admin.ErrorBadRequestType, "ACME EAB not enabled for provisioner %s", prov))
return
}
// TODO: support paging properly? It'll probably leak to the DB layer, as we have to loop through all keys
// cursor, limit, err := api.ParseCursor(r)
// if err != nil {
@ -169,6 +148,7 @@ func (h *Handler) GetExternalAccountKeys(w http.ResponseWriter, r *http.Request)
var (
key *acme.ExternalAccountKey
keys []*acme.ExternalAccountKey
err error
)
if reference != "" {
key, err = h.acmeDB.GetExternalAccountKeyByReference(r.Context(), prov, reference)

View file

@ -29,6 +29,10 @@ func (h *Handler) Route(r api.Router) {
return h.extractAuthorizeTokenAdmin(h.requireAPIEnabled(next))
}
requireEABEnabled := func(next nextHTTP) nextHTTP {
return h.requireEABEnabled(next)
}
// Provisioners
r.MethodFunc("GET", "/provisioners/{name}", authnz(h.GetProvisioner))
r.MethodFunc("GET", "/provisioners", authnz(h.GetProvisioners))
@ -44,8 +48,8 @@ func (h *Handler) Route(r api.Router) {
r.MethodFunc("DELETE", "/admins/{id}", authnz(h.DeleteAdmin))
// ACME External Account Binding Keys
r.MethodFunc("GET", "/acme/eab/{prov}/{ref}", authnz(h.GetExternalAccountKeys))
r.MethodFunc("GET", "/acme/eab/{prov}", authnz(h.GetExternalAccountKeys))
r.MethodFunc("POST", "/acme/eab", authnz(h.CreateExternalAccountKey))
r.MethodFunc("DELETE", "/acme/eab/{prov}/{id}", authnz(h.DeleteExternalAccountKey))
r.MethodFunc("GET", "/acme/eab/{prov}/{ref}", authnz(requireEABEnabled(h.GetExternalAccountKeys)))
r.MethodFunc("GET", "/acme/eab/{prov}", authnz(requireEABEnabled(h.GetExternalAccountKeys)))
r.MethodFunc("POST", "/acme/eab/{prov}", authnz(requireEABEnabled(h.CreateExternalAccountKey)))
r.MethodFunc("DELETE", "/acme/eab/{prov}/{id}", authnz(requireEABEnabled(h.DeleteExternalAccountKey)))
}

View file

@ -602,13 +602,13 @@ retry:
}
// CreateExternalAccountKey performs the POST /admin/acme/eab request to the CA.
func (c *AdminClient) CreateExternalAccountKey(eakRequest *adminAPI.CreateExternalAccountKeyRequest) (*linkedca.EABKey, error) {
func (c *AdminClient) CreateExternalAccountKey(provisionerName string, eakRequest *adminAPI.CreateExternalAccountKeyRequest) (*linkedca.EABKey, error) {
var retried bool
body, err := json.Marshal(eakRequest)
if err != nil {
return nil, errs.Wrap(http.StatusInternalServerError, err, "error marshaling request")
}
u := c.endpoint.ResolveReference(&url.URL{Path: path.Join(adminURLPrefix, "acme/eab")})
u := c.endpoint.ResolveReference(&url.URL{Path: path.Join(adminURLPrefix, "acme/eab/", provisionerName)})
tok, err := c.generateAdminToken(u.Path)
if err != nil {
return nil, errors.Wrapf(err, "error generating admin token")