forked from TrueCloudLab/certificates
acme/api: refactored to support api/render.Error
This commit is contained in:
parent
9b6c1f608e
commit
6636e87fc7
5 changed files with 120 additions and 120 deletions
|
@ -7,7 +7,6 @@ import (
|
|||
"github.com/go-chi/chi"
|
||||
|
||||
"github.com/smallstep/certificates/acme"
|
||||
"github.com/smallstep/certificates/api"
|
||||
"github.com/smallstep/certificates/api/render"
|
||||
"github.com/smallstep/certificates/logging"
|
||||
)
|
||||
|
@ -72,23 +71,23 @@ func (h *Handler) NewAccount(w http.ResponseWriter, r *http.Request) {
|
|||
ctx := r.Context()
|
||||
payload, err := payloadFromContext(ctx)
|
||||
if err != nil {
|
||||
api.WriteError(w, err)
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
var nar NewAccountRequest
|
||||
if err := json.Unmarshal(payload.value, &nar); err != nil {
|
||||
api.WriteError(w, acme.WrapError(acme.ErrorMalformedType, err,
|
||||
render.Error(w, acme.WrapError(acme.ErrorMalformedType, err,
|
||||
"failed to unmarshal new-account request payload"))
|
||||
return
|
||||
}
|
||||
if err := nar.Validate(); err != nil {
|
||||
api.WriteError(w, err)
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
prov, err := acmeProvisionerFromContext(ctx)
|
||||
if err != nil {
|
||||
api.WriteError(w, err)
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -98,26 +97,26 @@ func (h *Handler) NewAccount(w http.ResponseWriter, r *http.Request) {
|
|||
acmeErr, ok := err.(*acme.Error)
|
||||
if !ok || acmeErr.Status != http.StatusBadRequest {
|
||||
// Something went wrong ...
|
||||
api.WriteError(w, err)
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
// Account does not exist //
|
||||
if nar.OnlyReturnExisting {
|
||||
api.WriteError(w, acme.NewError(acme.ErrorAccountDoesNotExistType,
|
||||
render.Error(w, acme.NewError(acme.ErrorAccountDoesNotExistType,
|
||||
"account does not exist"))
|
||||
return
|
||||
}
|
||||
|
||||
jwk, err := jwkFromContext(ctx)
|
||||
if err != nil {
|
||||
api.WriteError(w, err)
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
eak, err := h.validateExternalAccountBinding(ctx, &nar)
|
||||
if err != nil {
|
||||
api.WriteError(w, err)
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -127,18 +126,18 @@ func (h *Handler) NewAccount(w http.ResponseWriter, r *http.Request) {
|
|||
Status: acme.StatusValid,
|
||||
}
|
||||
if err := h.db.CreateAccount(ctx, acc); err != nil {
|
||||
api.WriteError(w, acme.WrapErrorISE(err, "error creating account"))
|
||||
render.Error(w, acme.WrapErrorISE(err, "error creating account"))
|
||||
return
|
||||
}
|
||||
|
||||
if eak != nil { // means that we have a (valid) External Account Binding key that should be bound, updated and sent in the response
|
||||
err := eak.BindTo(acc)
|
||||
if err != nil {
|
||||
api.WriteError(w, err)
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
if err := h.db.UpdateExternalAccountKey(ctx, prov.ID, eak); err != nil {
|
||||
api.WriteError(w, acme.WrapErrorISE(err, "error updating external account binding key"))
|
||||
render.Error(w, acme.WrapErrorISE(err, "error updating external account binding key"))
|
||||
return
|
||||
}
|
||||
acc.ExternalAccountBinding = nar.ExternalAccountBinding
|
||||
|
@ -159,12 +158,12 @@ func (h *Handler) GetOrUpdateAccount(w http.ResponseWriter, r *http.Request) {
|
|||
ctx := r.Context()
|
||||
acc, err := accountFromContext(ctx)
|
||||
if err != nil {
|
||||
api.WriteError(w, err)
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
payload, err := payloadFromContext(ctx)
|
||||
if err != nil {
|
||||
api.WriteError(w, err)
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -173,12 +172,12 @@ func (h *Handler) GetOrUpdateAccount(w http.ResponseWriter, r *http.Request) {
|
|||
if !payload.isPostAsGet {
|
||||
var uar UpdateAccountRequest
|
||||
if err := json.Unmarshal(payload.value, &uar); err != nil {
|
||||
api.WriteError(w, acme.WrapError(acme.ErrorMalformedType, err,
|
||||
render.Error(w, acme.WrapError(acme.ErrorMalformedType, err,
|
||||
"failed to unmarshal new-account request payload"))
|
||||
return
|
||||
}
|
||||
if err := uar.Validate(); err != nil {
|
||||
api.WriteError(w, err)
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
if len(uar.Status) > 0 || len(uar.Contact) > 0 {
|
||||
|
@ -189,7 +188,7 @@ func (h *Handler) GetOrUpdateAccount(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
if err := h.db.UpdateAccount(ctx, acc); err != nil {
|
||||
api.WriteError(w, acme.WrapErrorISE(err, "error updating account"))
|
||||
render.Error(w, acme.WrapErrorISE(err, "error updating account"))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
@ -215,17 +214,17 @@ func (h *Handler) GetOrdersByAccountID(w http.ResponseWriter, r *http.Request) {
|
|||
ctx := r.Context()
|
||||
acc, err := accountFromContext(ctx)
|
||||
if err != nil {
|
||||
api.WriteError(w, err)
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
accID := chi.URLParam(r, "accID")
|
||||
if acc.ID != accID {
|
||||
api.WriteError(w, acme.NewError(acme.ErrorUnauthorizedType, "account ID '%s' does not match url param '%s'", acc.ID, accID))
|
||||
render.Error(w, acme.NewError(acme.ErrorUnauthorizedType, "account ID '%s' does not match url param '%s'", acc.ID, accID))
|
||||
return
|
||||
}
|
||||
orders, err := h.db.GetOrdersByAccountID(ctx, acc.ID)
|
||||
if err != nil {
|
||||
api.WriteError(w, err)
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -183,7 +183,7 @@ func (h *Handler) GetDirectory(w http.ResponseWriter, r *http.Request) {
|
|||
ctx := r.Context()
|
||||
acmeProv, err := acmeProvisionerFromContext(ctx)
|
||||
if err != nil {
|
||||
api.WriteError(w, err)
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -202,7 +202,7 @@ func (h *Handler) GetDirectory(w http.ResponseWriter, r *http.Request) {
|
|||
// NotImplemented returns a 501 and is generally a placeholder for functionality which
|
||||
// MAY be added at some point in the future but is not in any way a guarantee of such.
|
||||
func (h *Handler) NotImplemented(w http.ResponseWriter, r *http.Request) {
|
||||
api.WriteError(w, acme.NewError(acme.ErrorNotImplementedType, "this API is not implemented"))
|
||||
render.Error(w, acme.NewError(acme.ErrorNotImplementedType, "this API is not implemented"))
|
||||
}
|
||||
|
||||
// GetAuthorization ACME api for retrieving an Authz.
|
||||
|
@ -210,21 +210,21 @@ func (h *Handler) GetAuthorization(w http.ResponseWriter, r *http.Request) {
|
|||
ctx := r.Context()
|
||||
acc, err := accountFromContext(ctx)
|
||||
if err != nil {
|
||||
api.WriteError(w, err)
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
az, err := h.db.GetAuthorization(ctx, chi.URLParam(r, "authzID"))
|
||||
if err != nil {
|
||||
api.WriteError(w, acme.WrapErrorISE(err, "error retrieving authorization"))
|
||||
render.Error(w, acme.WrapErrorISE(err, "error retrieving authorization"))
|
||||
return
|
||||
}
|
||||
if acc.ID != az.AccountID {
|
||||
api.WriteError(w, acme.NewError(acme.ErrorUnauthorizedType,
|
||||
render.Error(w, acme.NewError(acme.ErrorUnauthorizedType,
|
||||
"account '%s' does not own authorization '%s'", acc.ID, az.ID))
|
||||
return
|
||||
}
|
||||
if err = az.UpdateStatus(ctx, h.db); err != nil {
|
||||
api.WriteError(w, acme.WrapErrorISE(err, "error updating authorization status"))
|
||||
render.Error(w, acme.WrapErrorISE(err, "error updating authorization status"))
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -239,14 +239,14 @@ func (h *Handler) GetChallenge(w http.ResponseWriter, r *http.Request) {
|
|||
ctx := r.Context()
|
||||
acc, err := accountFromContext(ctx)
|
||||
if err != nil {
|
||||
api.WriteError(w, err)
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
// Just verify that the payload was set, since we're not strictly adhering
|
||||
// to ACME V2 spec for reasons specified below.
|
||||
_, err = payloadFromContext(ctx)
|
||||
if err != nil {
|
||||
api.WriteError(w, err)
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -259,22 +259,22 @@ func (h *Handler) GetChallenge(w http.ResponseWriter, r *http.Request) {
|
|||
azID := chi.URLParam(r, "authzID")
|
||||
ch, err := h.db.GetChallenge(ctx, chi.URLParam(r, "chID"), azID)
|
||||
if err != nil {
|
||||
api.WriteError(w, acme.WrapErrorISE(err, "error retrieving challenge"))
|
||||
render.Error(w, acme.WrapErrorISE(err, "error retrieving challenge"))
|
||||
return
|
||||
}
|
||||
ch.AuthorizationID = azID
|
||||
if acc.ID != ch.AccountID {
|
||||
api.WriteError(w, acme.NewError(acme.ErrorUnauthorizedType,
|
||||
render.Error(w, acme.NewError(acme.ErrorUnauthorizedType,
|
||||
"account '%s' does not own challenge '%s'", acc.ID, ch.ID))
|
||||
return
|
||||
}
|
||||
jwk, err := jwkFromContext(ctx)
|
||||
if err != nil {
|
||||
api.WriteError(w, err)
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
if err = ch.Validate(ctx, h.db, jwk, h.validateChallengeOptions); err != nil {
|
||||
api.WriteError(w, acme.WrapErrorISE(err, "error validating challenge"))
|
||||
render.Error(w, acme.WrapErrorISE(err, "error validating challenge"))
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -290,18 +290,18 @@ func (h *Handler) GetCertificate(w http.ResponseWriter, r *http.Request) {
|
|||
ctx := r.Context()
|
||||
acc, err := accountFromContext(ctx)
|
||||
if err != nil {
|
||||
api.WriteError(w, err)
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
certID := chi.URLParam(r, "certID")
|
||||
|
||||
cert, err := h.db.GetCertificate(ctx, certID)
|
||||
if err != nil {
|
||||
api.WriteError(w, acme.WrapErrorISE(err, "error retrieving certificate"))
|
||||
render.Error(w, acme.WrapErrorISE(err, "error retrieving certificate"))
|
||||
return
|
||||
}
|
||||
if cert.AccountID != acc.ID {
|
||||
api.WriteError(w, acme.NewError(acme.ErrorUnauthorizedType,
|
||||
render.Error(w, acme.NewError(acme.ErrorUnauthorizedType,
|
||||
"account '%s' does not own certificate '%s'", acc.ID, certID))
|
||||
return
|
||||
}
|
||||
|
|
|
@ -10,13 +10,14 @@ import (
|
|||
"strings"
|
||||
|
||||
"github.com/go-chi/chi"
|
||||
"go.step.sm/crypto/jose"
|
||||
"go.step.sm/crypto/keyutil"
|
||||
|
||||
"github.com/smallstep/certificates/acme"
|
||||
"github.com/smallstep/certificates/api"
|
||||
"github.com/smallstep/certificates/api/render"
|
||||
"github.com/smallstep/certificates/authority/provisioner"
|
||||
"github.com/smallstep/certificates/logging"
|
||||
"github.com/smallstep/nosql"
|
||||
"go.step.sm/crypto/jose"
|
||||
"go.step.sm/crypto/keyutil"
|
||||
)
|
||||
|
||||
type nextHTTP = func(http.ResponseWriter, *http.Request)
|
||||
|
@ -64,7 +65,7 @@ func (h *Handler) addNonce(next nextHTTP) nextHTTP {
|
|||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
nonce, err := h.db.CreateNonce(r.Context())
|
||||
if err != nil {
|
||||
api.WriteError(w, err)
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
w.Header().Set("Replay-Nonce", string(nonce))
|
||||
|
@ -90,7 +91,7 @@ func (h *Handler) verifyContentType(next nextHTTP) nextHTTP {
|
|||
var expected []string
|
||||
p, err := provisionerFromContext(r.Context())
|
||||
if err != nil {
|
||||
api.WriteError(w, err)
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -110,7 +111,7 @@ func (h *Handler) verifyContentType(next nextHTTP) nextHTTP {
|
|||
return
|
||||
}
|
||||
}
|
||||
api.WriteError(w, acme.NewError(acme.ErrorMalformedType,
|
||||
render.Error(w, acme.NewError(acme.ErrorMalformedType,
|
||||
"expected content-type to be in %s, but got %s", expected, ct))
|
||||
}
|
||||
}
|
||||
|
@ -120,12 +121,12 @@ func (h *Handler) parseJWS(next nextHTTP) nextHTTP {
|
|||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
body, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
api.WriteError(w, acme.WrapErrorISE(err, "failed to read request body"))
|
||||
render.Error(w, acme.WrapErrorISE(err, "failed to read request body"))
|
||||
return
|
||||
}
|
||||
jws, err := jose.ParseJWS(string(body))
|
||||
if err != nil {
|
||||
api.WriteError(w, acme.WrapError(acme.ErrorMalformedType, err, "failed to parse JWS from request body"))
|
||||
render.Error(w, acme.WrapError(acme.ErrorMalformedType, err, "failed to parse JWS from request body"))
|
||||
return
|
||||
}
|
||||
ctx := context.WithValue(r.Context(), jwsContextKey, jws)
|
||||
|
@ -153,15 +154,15 @@ func (h *Handler) validateJWS(next nextHTTP) nextHTTP {
|
|||
ctx := r.Context()
|
||||
jws, err := jwsFromContext(r.Context())
|
||||
if err != nil {
|
||||
api.WriteError(w, err)
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
if len(jws.Signatures) == 0 {
|
||||
api.WriteError(w, acme.NewError(acme.ErrorMalformedType, "request body does not contain a signature"))
|
||||
render.Error(w, acme.NewError(acme.ErrorMalformedType, "request body does not contain a signature"))
|
||||
return
|
||||
}
|
||||
if len(jws.Signatures) > 1 {
|
||||
api.WriteError(w, acme.NewError(acme.ErrorMalformedType, "request body contains more than one signature"))
|
||||
render.Error(w, acme.NewError(acme.ErrorMalformedType, "request body contains more than one signature"))
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -172,7 +173,7 @@ func (h *Handler) validateJWS(next nextHTTP) nextHTTP {
|
|||
len(uh.Algorithm) > 0 ||
|
||||
len(uh.Nonce) > 0 ||
|
||||
len(uh.ExtraHeaders) > 0 {
|
||||
api.WriteError(w, acme.NewError(acme.ErrorMalformedType, "unprotected header must not be used"))
|
||||
render.Error(w, acme.NewError(acme.ErrorMalformedType, "unprotected header must not be used"))
|
||||
return
|
||||
}
|
||||
hdr := sig.Protected
|
||||
|
@ -182,13 +183,13 @@ func (h *Handler) validateJWS(next nextHTTP) nextHTTP {
|
|||
switch k := hdr.JSONWebKey.Key.(type) {
|
||||
case *rsa.PublicKey:
|
||||
if k.Size() < keyutil.MinRSAKeyBytes {
|
||||
api.WriteError(w, acme.NewError(acme.ErrorMalformedType,
|
||||
render.Error(w, acme.NewError(acme.ErrorMalformedType,
|
||||
"rsa keys must be at least %d bits (%d bytes) in size",
|
||||
8*keyutil.MinRSAKeyBytes, keyutil.MinRSAKeyBytes))
|
||||
return
|
||||
}
|
||||
default:
|
||||
api.WriteError(w, acme.NewError(acme.ErrorMalformedType,
|
||||
render.Error(w, acme.NewError(acme.ErrorMalformedType,
|
||||
"jws key type and algorithm do not match"))
|
||||
return
|
||||
}
|
||||
|
@ -196,35 +197,35 @@ func (h *Handler) validateJWS(next nextHTTP) nextHTTP {
|
|||
case jose.ES256, jose.ES384, jose.ES512, jose.EdDSA:
|
||||
// we good
|
||||
default:
|
||||
api.WriteError(w, acme.NewError(acme.ErrorBadSignatureAlgorithmType, "unsuitable algorithm: %s", hdr.Algorithm))
|
||||
render.Error(w, acme.NewError(acme.ErrorBadSignatureAlgorithmType, "unsuitable algorithm: %s", hdr.Algorithm))
|
||||
return
|
||||
}
|
||||
|
||||
// Check the validity/freshness of the Nonce.
|
||||
if err := h.db.DeleteNonce(ctx, acme.Nonce(hdr.Nonce)); err != nil {
|
||||
api.WriteError(w, err)
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
// Check that the JWS url matches the requested url.
|
||||
jwsURL, ok := hdr.ExtraHeaders["url"].(string)
|
||||
if !ok {
|
||||
api.WriteError(w, acme.NewError(acme.ErrorMalformedType, "jws missing url protected header"))
|
||||
render.Error(w, acme.NewError(acme.ErrorMalformedType, "jws missing url protected header"))
|
||||
return
|
||||
}
|
||||
reqURL := &url.URL{Scheme: "https", Host: r.Host, Path: r.URL.Path}
|
||||
if jwsURL != reqURL.String() {
|
||||
api.WriteError(w, acme.NewError(acme.ErrorMalformedType,
|
||||
render.Error(w, acme.NewError(acme.ErrorMalformedType,
|
||||
"url header in JWS (%s) does not match request url (%s)", jwsURL, reqURL))
|
||||
return
|
||||
}
|
||||
|
||||
if hdr.JSONWebKey != nil && len(hdr.KeyID) > 0 {
|
||||
api.WriteError(w, acme.NewError(acme.ErrorMalformedType, "jwk and kid are mutually exclusive"))
|
||||
render.Error(w, acme.NewError(acme.ErrorMalformedType, "jwk and kid are mutually exclusive"))
|
||||
return
|
||||
}
|
||||
if hdr.JSONWebKey == nil && hdr.KeyID == "" {
|
||||
api.WriteError(w, acme.NewError(acme.ErrorMalformedType, "either jwk or kid must be defined in jws protected header"))
|
||||
render.Error(w, acme.NewError(acme.ErrorMalformedType, "either jwk or kid must be defined in jws protected header"))
|
||||
return
|
||||
}
|
||||
next(w, r)
|
||||
|
@ -239,23 +240,23 @@ func (h *Handler) extractJWK(next nextHTTP) nextHTTP {
|
|||
ctx := r.Context()
|
||||
jws, err := jwsFromContext(r.Context())
|
||||
if err != nil {
|
||||
api.WriteError(w, err)
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
jwk := jws.Signatures[0].Protected.JSONWebKey
|
||||
if jwk == nil {
|
||||
api.WriteError(w, acme.NewError(acme.ErrorMalformedType, "jwk expected in protected header"))
|
||||
render.Error(w, acme.NewError(acme.ErrorMalformedType, "jwk expected in protected header"))
|
||||
return
|
||||
}
|
||||
if !jwk.Valid() {
|
||||
api.WriteError(w, acme.NewError(acme.ErrorMalformedType, "invalid jwk in protected header"))
|
||||
render.Error(w, acme.NewError(acme.ErrorMalformedType, "invalid jwk in protected header"))
|
||||
return
|
||||
}
|
||||
|
||||
// Overwrite KeyID with the JWK thumbprint.
|
||||
jwk.KeyID, err = acme.KeyToID(jwk)
|
||||
if err != nil {
|
||||
api.WriteError(w, acme.WrapErrorISE(err, "error getting KeyID from JWK"))
|
||||
render.Error(w, acme.WrapErrorISE(err, "error getting KeyID from JWK"))
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -269,11 +270,11 @@ func (h *Handler) extractJWK(next nextHTTP) nextHTTP {
|
|||
// For NewAccount and Revoke requests ...
|
||||
break
|
||||
case err != nil:
|
||||
api.WriteError(w, err)
|
||||
render.Error(w, err)
|
||||
return
|
||||
default:
|
||||
if !acc.IsValid() {
|
||||
api.WriteError(w, acme.NewError(acme.ErrorUnauthorizedType, "account is not active"))
|
||||
render.Error(w, acme.NewError(acme.ErrorUnauthorizedType, "account is not active"))
|
||||
return
|
||||
}
|
||||
ctx = context.WithValue(ctx, accContextKey, acc)
|
||||
|
@ -290,17 +291,17 @@ func (h *Handler) lookupProvisioner(next nextHTTP) nextHTTP {
|
|||
nameEscaped := chi.URLParam(r, "provisionerID")
|
||||
name, err := url.PathUnescape(nameEscaped)
|
||||
if err != nil {
|
||||
api.WriteError(w, acme.WrapErrorISE(err, "error url unescaping provisioner name '%s'", nameEscaped))
|
||||
render.Error(w, acme.WrapErrorISE(err, "error url unescaping provisioner name '%s'", nameEscaped))
|
||||
return
|
||||
}
|
||||
p, err := h.ca.LoadProvisionerByName(name)
|
||||
if err != nil {
|
||||
api.WriteError(w, err)
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
acmeProv, ok := p.(*provisioner.ACME)
|
||||
if !ok {
|
||||
api.WriteError(w, acme.NewError(acme.ErrorAccountDoesNotExistType, "provisioner must be of type ACME"))
|
||||
render.Error(w, acme.NewError(acme.ErrorAccountDoesNotExistType, "provisioner must be of type ACME"))
|
||||
return
|
||||
}
|
||||
ctx = context.WithValue(ctx, provisionerContextKey, acme.Provisioner(acmeProv))
|
||||
|
@ -315,11 +316,11 @@ func (h *Handler) checkPrerequisites(next nextHTTP) nextHTTP {
|
|||
ctx := r.Context()
|
||||
ok, err := h.prerequisitesChecker(ctx)
|
||||
if err != nil {
|
||||
api.WriteError(w, acme.WrapErrorISE(err, "error checking acme provisioner prerequisites"))
|
||||
render.Error(w, acme.WrapErrorISE(err, "error checking acme provisioner prerequisites"))
|
||||
return
|
||||
}
|
||||
if !ok {
|
||||
api.WriteError(w, acme.NewError(acme.ErrorNotImplementedType, "acme provisioner configuration lacks prerequisites"))
|
||||
render.Error(w, acme.NewError(acme.ErrorNotImplementedType, "acme provisioner configuration lacks prerequisites"))
|
||||
return
|
||||
}
|
||||
next(w, r.WithContext(ctx))
|
||||
|
@ -334,14 +335,14 @@ func (h *Handler) lookupJWK(next nextHTTP) nextHTTP {
|
|||
ctx := r.Context()
|
||||
jws, err := jwsFromContext(ctx)
|
||||
if err != nil {
|
||||
api.WriteError(w, err)
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
kidPrefix := h.linker.GetLink(ctx, AccountLinkType, "")
|
||||
kid := jws.Signatures[0].Protected.KeyID
|
||||
if !strings.HasPrefix(kid, kidPrefix) {
|
||||
api.WriteError(w, acme.NewError(acme.ErrorMalformedType,
|
||||
render.Error(w, acme.NewError(acme.ErrorMalformedType,
|
||||
"kid does not have required prefix; expected %s, but got %s",
|
||||
kidPrefix, kid))
|
||||
return
|
||||
|
@ -351,14 +352,14 @@ func (h *Handler) lookupJWK(next nextHTTP) nextHTTP {
|
|||
acc, err := h.db.GetAccount(ctx, accID)
|
||||
switch {
|
||||
case nosql.IsErrNotFound(err):
|
||||
api.WriteError(w, acme.NewError(acme.ErrorAccountDoesNotExistType, "account with ID '%s' not found", accID))
|
||||
render.Error(w, acme.NewError(acme.ErrorAccountDoesNotExistType, "account with ID '%s' not found", accID))
|
||||
return
|
||||
case err != nil:
|
||||
api.WriteError(w, err)
|
||||
render.Error(w, err)
|
||||
return
|
||||
default:
|
||||
if !acc.IsValid() {
|
||||
api.WriteError(w, acme.NewError(acme.ErrorUnauthorizedType, "account is not active"))
|
||||
render.Error(w, acme.NewError(acme.ErrorUnauthorizedType, "account is not active"))
|
||||
return
|
||||
}
|
||||
ctx = context.WithValue(ctx, accContextKey, acc)
|
||||
|
@ -376,7 +377,7 @@ func (h *Handler) extractOrLookupJWK(next nextHTTP) nextHTTP {
|
|||
ctx := r.Context()
|
||||
jws, err := jwsFromContext(ctx)
|
||||
if err != nil {
|
||||
api.WriteError(w, err)
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -412,21 +413,21 @@ func (h *Handler) verifyAndExtractJWSPayload(next nextHTTP) nextHTTP {
|
|||
ctx := r.Context()
|
||||
jws, err := jwsFromContext(ctx)
|
||||
if err != nil {
|
||||
api.WriteError(w, err)
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
jwk, err := jwkFromContext(ctx)
|
||||
if err != nil {
|
||||
api.WriteError(w, err)
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
if jwk.Algorithm != "" && jwk.Algorithm != jws.Signatures[0].Protected.Algorithm {
|
||||
api.WriteError(w, acme.NewError(acme.ErrorMalformedType, "verifier and signature algorithm do not match"))
|
||||
render.Error(w, acme.NewError(acme.ErrorMalformedType, "verifier and signature algorithm do not match"))
|
||||
return
|
||||
}
|
||||
payload, err := jws.Verify(jwk)
|
||||
if err != nil {
|
||||
api.WriteError(w, acme.WrapError(acme.ErrorMalformedType, err, "error verifying jws"))
|
||||
render.Error(w, acme.WrapError(acme.ErrorMalformedType, err, "error verifying jws"))
|
||||
return
|
||||
}
|
||||
ctx = context.WithValue(ctx, payloadContextKey, &payloadInfo{
|
||||
|
@ -443,11 +444,11 @@ func (h *Handler) isPostAsGet(next nextHTTP) nextHTTP {
|
|||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
payload, err := payloadFromContext(r.Context())
|
||||
if err != nil {
|
||||
api.WriteError(w, err)
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
if !payload.isPostAsGet {
|
||||
api.WriteError(w, acme.NewError(acme.ErrorMalformedType, "expected POST-as-GET"))
|
||||
render.Error(w, acme.NewError(acme.ErrorMalformedType, "expected POST-as-GET"))
|
||||
return
|
||||
}
|
||||
next(w, r)
|
||||
|
|
|
@ -15,7 +15,6 @@ import (
|
|||
"go.step.sm/crypto/randutil"
|
||||
|
||||
"github.com/smallstep/certificates/acme"
|
||||
"github.com/smallstep/certificates/api"
|
||||
"github.com/smallstep/certificates/api/render"
|
||||
)
|
||||
|
||||
|
@ -73,28 +72,28 @@ func (h *Handler) NewOrder(w http.ResponseWriter, r *http.Request) {
|
|||
ctx := r.Context()
|
||||
acc, err := accountFromContext(ctx)
|
||||
if err != nil {
|
||||
api.WriteError(w, err)
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
prov, err := provisionerFromContext(ctx)
|
||||
if err != nil {
|
||||
api.WriteError(w, err)
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
payload, err := payloadFromContext(ctx)
|
||||
if err != nil {
|
||||
api.WriteError(w, err)
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
var nor NewOrderRequest
|
||||
if err := json.Unmarshal(payload.value, &nor); err != nil {
|
||||
api.WriteError(w, acme.WrapError(acme.ErrorMalformedType, err,
|
||||
render.Error(w, acme.WrapError(acme.ErrorMalformedType, err,
|
||||
"failed to unmarshal new-order request payload"))
|
||||
return
|
||||
}
|
||||
|
||||
if err := nor.Validate(); err != nil {
|
||||
api.WriteError(w, err)
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -119,7 +118,7 @@ func (h *Handler) NewOrder(w http.ResponseWriter, r *http.Request) {
|
|||
Status: acme.StatusPending,
|
||||
}
|
||||
if err := h.newAuthorization(ctx, az); err != nil {
|
||||
api.WriteError(w, err)
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
o.AuthorizationIDs[i] = az.ID
|
||||
|
@ -138,7 +137,7 @@ func (h *Handler) NewOrder(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
if err := h.db.CreateOrder(ctx, o); err != nil {
|
||||
api.WriteError(w, acme.WrapErrorISE(err, "error creating order"))
|
||||
render.Error(w, acme.WrapErrorISE(err, "error creating order"))
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -189,31 +188,31 @@ func (h *Handler) GetOrder(w http.ResponseWriter, r *http.Request) {
|
|||
ctx := r.Context()
|
||||
acc, err := accountFromContext(ctx)
|
||||
if err != nil {
|
||||
api.WriteError(w, err)
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
prov, err := provisionerFromContext(ctx)
|
||||
if err != nil {
|
||||
api.WriteError(w, err)
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
o, err := h.db.GetOrder(ctx, chi.URLParam(r, "ordID"))
|
||||
if err != nil {
|
||||
api.WriteError(w, acme.WrapErrorISE(err, "error retrieving order"))
|
||||
render.Error(w, acme.WrapErrorISE(err, "error retrieving order"))
|
||||
return
|
||||
}
|
||||
if acc.ID != o.AccountID {
|
||||
api.WriteError(w, acme.NewError(acme.ErrorUnauthorizedType,
|
||||
render.Error(w, acme.NewError(acme.ErrorUnauthorizedType,
|
||||
"account '%s' does not own order '%s'", acc.ID, o.ID))
|
||||
return
|
||||
}
|
||||
if prov.GetID() != o.ProvisionerID {
|
||||
api.WriteError(w, acme.NewError(acme.ErrorUnauthorizedType,
|
||||
render.Error(w, acme.NewError(acme.ErrorUnauthorizedType,
|
||||
"provisioner '%s' does not own order '%s'", prov.GetID(), o.ID))
|
||||
return
|
||||
}
|
||||
if err = o.UpdateStatus(ctx, h.db); err != nil {
|
||||
api.WriteError(w, acme.WrapErrorISE(err, "error updating order status"))
|
||||
render.Error(w, acme.WrapErrorISE(err, "error updating order status"))
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -228,47 +227,47 @@ func (h *Handler) FinalizeOrder(w http.ResponseWriter, r *http.Request) {
|
|||
ctx := r.Context()
|
||||
acc, err := accountFromContext(ctx)
|
||||
if err != nil {
|
||||
api.WriteError(w, err)
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
prov, err := provisionerFromContext(ctx)
|
||||
if err != nil {
|
||||
api.WriteError(w, err)
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
payload, err := payloadFromContext(ctx)
|
||||
if err != nil {
|
||||
api.WriteError(w, err)
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
var fr FinalizeRequest
|
||||
if err := json.Unmarshal(payload.value, &fr); err != nil {
|
||||
api.WriteError(w, acme.WrapError(acme.ErrorMalformedType, err,
|
||||
render.Error(w, acme.WrapError(acme.ErrorMalformedType, err,
|
||||
"failed to unmarshal finalize-order request payload"))
|
||||
return
|
||||
}
|
||||
if err := fr.Validate(); err != nil {
|
||||
api.WriteError(w, err)
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
o, err := h.db.GetOrder(ctx, chi.URLParam(r, "ordID"))
|
||||
if err != nil {
|
||||
api.WriteError(w, acme.WrapErrorISE(err, "error retrieving order"))
|
||||
render.Error(w, acme.WrapErrorISE(err, "error retrieving order"))
|
||||
return
|
||||
}
|
||||
if acc.ID != o.AccountID {
|
||||
api.WriteError(w, acme.NewError(acme.ErrorUnauthorizedType,
|
||||
render.Error(w, acme.NewError(acme.ErrorUnauthorizedType,
|
||||
"account '%s' does not own order '%s'", acc.ID, o.ID))
|
||||
return
|
||||
}
|
||||
if prov.GetID() != o.ProvisionerID {
|
||||
api.WriteError(w, acme.NewError(acme.ErrorUnauthorizedType,
|
||||
render.Error(w, acme.NewError(acme.ErrorUnauthorizedType,
|
||||
"provisioner '%s' does not own order '%s'", prov.GetID(), o.ID))
|
||||
return
|
||||
}
|
||||
if err = o.Finalize(ctx, h.db, fr.csr, h.ca, prov); err != nil {
|
||||
api.WriteError(w, acme.WrapErrorISE(err, "error finalizing order"))
|
||||
render.Error(w, acme.WrapErrorISE(err, "error finalizing order"))
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -10,13 +10,14 @@ import (
|
|||
"net/http"
|
||||
"strings"
|
||||
|
||||
"go.step.sm/crypto/jose"
|
||||
"golang.org/x/crypto/ocsp"
|
||||
|
||||
"github.com/smallstep/certificates/acme"
|
||||
"github.com/smallstep/certificates/api"
|
||||
"github.com/smallstep/certificates/api/render"
|
||||
"github.com/smallstep/certificates/authority"
|
||||
"github.com/smallstep/certificates/authority/provisioner"
|
||||
"github.com/smallstep/certificates/logging"
|
||||
"go.step.sm/crypto/jose"
|
||||
"golang.org/x/crypto/ocsp"
|
||||
)
|
||||
|
||||
type revokePayload struct {
|
||||
|
@ -30,65 +31,65 @@ func (h *Handler) RevokeCert(w http.ResponseWriter, r *http.Request) {
|
|||
ctx := r.Context()
|
||||
jws, err := jwsFromContext(ctx)
|
||||
if err != nil {
|
||||
api.WriteError(w, err)
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
prov, err := provisionerFromContext(ctx)
|
||||
if err != nil {
|
||||
api.WriteError(w, err)
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
payload, err := payloadFromContext(ctx)
|
||||
if err != nil {
|
||||
api.WriteError(w, err)
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
var p revokePayload
|
||||
err = json.Unmarshal(payload.value, &p)
|
||||
if err != nil {
|
||||
api.WriteError(w, acme.WrapErrorISE(err, "error unmarshaling payload"))
|
||||
render.Error(w, acme.WrapErrorISE(err, "error unmarshaling payload"))
|
||||
return
|
||||
}
|
||||
|
||||
certBytes, err := base64.RawURLEncoding.DecodeString(p.Certificate)
|
||||
if err != nil {
|
||||
// in this case the most likely cause is a client that didn't properly encode the certificate
|
||||
api.WriteError(w, acme.WrapError(acme.ErrorMalformedType, err, "error base64url decoding payload certificate property"))
|
||||
render.Error(w, acme.WrapError(acme.ErrorMalformedType, err, "error base64url decoding payload certificate property"))
|
||||
return
|
||||
}
|
||||
|
||||
certToBeRevoked, err := x509.ParseCertificate(certBytes)
|
||||
if err != nil {
|
||||
// in this case a client may have encoded something different than a certificate
|
||||
api.WriteError(w, acme.WrapError(acme.ErrorMalformedType, err, "error parsing certificate"))
|
||||
render.Error(w, acme.WrapError(acme.ErrorMalformedType, err, "error parsing certificate"))
|
||||
return
|
||||
}
|
||||
|
||||
serial := certToBeRevoked.SerialNumber.String()
|
||||
dbCert, err := h.db.GetCertificateBySerial(ctx, serial)
|
||||
if err != nil {
|
||||
api.WriteError(w, acme.WrapErrorISE(err, "error retrieving certificate by serial"))
|
||||
render.Error(w, acme.WrapErrorISE(err, "error retrieving certificate by serial"))
|
||||
return
|
||||
}
|
||||
|
||||
if !bytes.Equal(dbCert.Leaf.Raw, certToBeRevoked.Raw) {
|
||||
// this should never happen
|
||||
api.WriteError(w, acme.NewErrorISE("certificate raw bytes are not equal"))
|
||||
render.Error(w, acme.NewErrorISE("certificate raw bytes are not equal"))
|
||||
return
|
||||
}
|
||||
|
||||
if shouldCheckAccountFrom(jws) {
|
||||
account, err := accountFromContext(ctx)
|
||||
if err != nil {
|
||||
api.WriteError(w, err)
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
acmeErr := h.isAccountAuthorized(ctx, dbCert, certToBeRevoked, account)
|
||||
if acmeErr != nil {
|
||||
api.WriteError(w, acmeErr)
|
||||
render.Error(w, acmeErr)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
|
@ -97,26 +98,26 @@ func (h *Handler) RevokeCert(w http.ResponseWriter, r *http.Request) {
|
|||
_, err := jws.Verify(certToBeRevoked.PublicKey)
|
||||
if err != nil {
|
||||
// TODO(hs): possible to determine an error vs. unauthorized and thus provide an ISE vs. Unauthorized?
|
||||
api.WriteError(w, wrapUnauthorizedError(certToBeRevoked, nil, "verification of jws using certificate public key failed", err))
|
||||
render.Error(w, wrapUnauthorizedError(certToBeRevoked, nil, "verification of jws using certificate public key failed", err))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
hasBeenRevokedBefore, err := h.ca.IsRevoked(serial)
|
||||
if err != nil {
|
||||
api.WriteError(w, acme.WrapErrorISE(err, "error retrieving revocation status of certificate"))
|
||||
render.Error(w, acme.WrapErrorISE(err, "error retrieving revocation status of certificate"))
|
||||
return
|
||||
}
|
||||
|
||||
if hasBeenRevokedBefore {
|
||||
api.WriteError(w, acme.NewError(acme.ErrorAlreadyRevokedType, "certificate was already revoked"))
|
||||
render.Error(w, acme.NewError(acme.ErrorAlreadyRevokedType, "certificate was already revoked"))
|
||||
return
|
||||
}
|
||||
|
||||
reasonCode := p.ReasonCode
|
||||
acmeErr := validateReasonCode(reasonCode)
|
||||
if acmeErr != nil {
|
||||
api.WriteError(w, acmeErr)
|
||||
render.Error(w, acmeErr)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -124,14 +125,14 @@ func (h *Handler) RevokeCert(w http.ResponseWriter, r *http.Request) {
|
|||
ctx = provisioner.NewContextWithMethod(ctx, provisioner.RevokeMethod)
|
||||
err = prov.AuthorizeRevoke(ctx, "")
|
||||
if err != nil {
|
||||
api.WriteError(w, acme.WrapErrorISE(err, "error authorizing revocation on provisioner"))
|
||||
render.Error(w, acme.WrapErrorISE(err, "error authorizing revocation on provisioner"))
|
||||
return
|
||||
}
|
||||
|
||||
options := revokeOptions(serial, certToBeRevoked, reasonCode)
|
||||
err = h.ca.Revoke(ctx, options)
|
||||
if err != nil {
|
||||
api.WriteError(w, wrapRevokeErr(err))
|
||||
render.Error(w, wrapRevokeErr(err))
|
||||
return
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue