From 03ba229bcbf6acbb76fb2efd056041b5f6ac38d8 Mon Sep 17 00:00:00 2001 From: max furman Date: Sun, 28 Feb 2021 23:33:18 -0800 Subject: [PATCH] [acme db interface] wip more errors --- acme/authority.go | 45 +++++++++--------- acme/authorization.go | 10 ++-- acme/challenge.go | 106 ++++++++++++++++++++++-------------------- acme/common.go | 11 ++--- acme/directory.go | 4 +- acme/errors.go | 29 ++++++------ acme/order.go | 35 +++++++------- 7 files changed, 123 insertions(+), 117 deletions(-) diff --git a/acme/authority.go b/acme/authority.go index 77e031d0..098c48d4 100644 --- a/acme/authority.go +++ b/acme/authority.go @@ -10,7 +10,6 @@ import ( "net/url" "time" - "github.com/pkg/errors" "github.com/smallstep/certificates/authority/provisioner" "go.step.sm/crypto/jose" ) @@ -125,7 +124,7 @@ func (a *Authority) UseNonce(ctx context.Context, nonce string) error { // NewAccount creates, stores, and returns a new ACME account. func (a *Authority) NewAccount(ctx context.Context, acc *Account) error { if err := a.db.CreateAccount(ctx, acc); err != nil { - return ErrorWrap(ErrorServerInternalType, err, "newAccount: error creating account") + return ErrorWrap(ErrorServerInternalType, err, "error creating account") } return nil } @@ -137,14 +136,18 @@ func (a *Authority) UpdateAccount(ctx context.Context, acc *Account) (*Account, acc.Status = auo.Status */ if err := a.db.UpdateAccount(ctx, acc); err != nil { - return nil, ErrorWrap(ErrorServerInternalType, err, "authority.UpdateAccount - database update failed" + return nil, ErrorWrap(ErrorServerInternalType, err, "error updating account") } return acc, nil } // GetAccount returns an ACME account. func (a *Authority) GetAccount(ctx context.Context, id string) (*Account, error) { - return a.db.GetAccount(ctx, id) + acc, err := a.db.GetAccount(ctx, id) + if err != nil { + return nil, ErrorWrap(ErrorServerInternalType, err, "error retrieving account") + } + return acc, nil } // GetAccountByKey returns the ACME associated with the jwk id. @@ -165,18 +168,18 @@ func (a *Authority) GetOrder(ctx context.Context, accID, orderID string) (*Order } o, err := a.db.GetOrder(ctx, orderID) if err != nil { - return nil, ServerInternalErr(err) + return nil, ErrorWrap(ErrorServerInternalType, err, "error retrieving order") } if accID != o.AccountID { log.Printf("account-id from request ('%s') does not match order account-id ('%s')", accID, o.AccountID) - return nil, UnauthorizedErr(errors.New("account does not own order")) + return nil, NewError(ErrorUnauthorizedType, "account does not own order") } if prov.GetID() != o.ProvisionerID { log.Printf("provisioner-id from request ('%s') does not match order provisioner-id ('%s')", prov.GetID(), o.ProvisionerID) - return nil, UnauthorizedErr(errors.New("provisioner does not own order")) + return nil, NewError(ErrorUnauthorizedType, "provisioner does not own order") } if err = o.UpdateStatus(ctx, a.db); err != nil { - return nil, err + return nil, ErrorWrap(ErrorServerInternalType, err, "error updating order") } return o, nil } @@ -212,7 +215,7 @@ func (a *Authority) NewOrder(ctx context.Context, o *Order) (*Order, error) { o.ProvisionerID = prov.GetID() if err = a.db.CreateOrder(ctx, o); err != nil { - return nil, ServerInternalErr(err) + return nil, ErrorWrap(ErrorServerInternalType, err, "error creating order") } return o, nil } @@ -225,18 +228,18 @@ func (a *Authority) FinalizeOrder(ctx context.Context, accID, orderID string, cs } o, err := a.db.GetOrder(ctx, orderID) if err != nil { - return nil, err + return nil, ErrorWrap(ErrorServerInternalType, err, "error retrieving order") } if accID != o.AccountID { log.Printf("account-id from request ('%s') does not match order account-id ('%s')", accID, o.AccountID) - return nil, UnauthorizedErr(errors.New("account does not own order")) + return nil, NewError(ErrorUnauthorizedType, "account does not own order") } if prov.GetID() != o.ProvisionerID { log.Printf("provisioner-id from request ('%s') does not match order provisioner-id ('%s')", prov.GetID(), o.ProvisionerID) - return nil, UnauthorizedErr(errors.New("provisioner does not own order")) + return nil, NewError(ErrorUnauthorizedType, "provisioner does not own order") } if err = o.Finalize(ctx, a.db, csr, a.signAuth, prov); err != nil { - return nil, Wrap(err, "error finalizing order") + return nil, ErrorWrap(ErrorServerInternalType, err, "error finalizing order") } return o, nil } @@ -246,14 +249,14 @@ func (a *Authority) FinalizeOrder(ctx context.Context, accID, orderID string, cs func (a *Authority) GetAuthz(ctx context.Context, accID, authzID string) (*Authorization, error) { az, err := a.db.GetAuthorization(ctx, authzID) if err != nil { - return nil, err + return nil, ErrorWrap(ErrorServerInternalType, err, "error retrieving authorization") } if accID != az.AccountID { log.Printf("account-id from request ('%s') does not match authz account-id ('%s')", accID, az.AccountID) - return nil, UnauthorizedErr(errors.New("account does not own authz")) + return nil, NewError(ErrorUnauthorizedType, "account does not own order") } if err = az.UpdateStatus(ctx, a.db); err != nil { - return nil, Wrap(err, "error updating authz status") + return nil, ErrorWrap(ErrorServerInternalType, err, "error updating authorization status") } return az, nil } @@ -262,11 +265,11 @@ func (a *Authority) GetAuthz(ctx context.Context, accID, authzID string) (*Autho func (a *Authority) ValidateChallenge(ctx context.Context, accID, chID string, jwk *jose.JSONWebKey) (*Challenge, error) { ch, err := a.db.GetChallenge(ctx, chID, "todo") if err != nil { - return nil, err + return nil, ErrorWrap(ErrorServerInternalType, err, "error retrieving challenge") } if accID != ch.AccountID { log.Printf("account-id from request ('%s') does not match challenge account-id ('%s')", accID, ch.AccountID) - return nil, UnauthorizedErr(errors.New("account does not own challenge")) + return nil, NewError(ErrorUnauthorizedType, "account does not own order") } client := http.Client{ Timeout: time.Duration(30 * time.Second), @@ -281,7 +284,7 @@ func (a *Authority) ValidateChallenge(ctx context.Context, accID, chID string, j return tls.DialWithDialer(dialer, network, addr, config) }, }); err != nil { - return nil, Wrap(err, "error attempting challenge validation") + return nil, ErrorWrap(ErrorServerInternalType, err, "error validating challenge") } return ch, nil } @@ -290,11 +293,11 @@ func (a *Authority) ValidateChallenge(ctx context.Context, accID, chID string, j func (a *Authority) GetCertificate(ctx context.Context, accID, certID string) ([]byte, error) { cert, err := a.db.GetCertificate(ctx, certID) if err != nil { - return nil, err + return nil, ErrorWrap(ErrorServerInternalType, err, "error retrieving certificate") } if cert.AccountID != accID { log.Printf("account-id from request ('%s') does not match challenge account-id ('%s')", accID, cert.AccountID) - return nil, UnauthorizedErr(errors.New("account does not own challenge")) + return nil, NewError(ErrorUnauthorizedType, "account does not own order") } return cert.ToACME(ctx) } diff --git a/acme/authorization.go b/acme/authorization.go index 43095fb3..ef230286 100644 --- a/acme/authorization.go +++ b/acme/authorization.go @@ -4,8 +4,6 @@ import ( "context" "encoding/json" "time" - - "github.com/pkg/errors" ) // Authorization representst an ACME Authorization. @@ -23,7 +21,7 @@ type Authorization struct { func (az *Authorization) ToLog() (interface{}, error) { b, err := json.Marshal(az) if err != nil { - return nil, ServerInternalErr(errors.Wrap(err, "error marshaling authz for logging")) + return nil, ErrorInternalServerWrap(err, "error marshaling authz for logging") } return string(b), nil } @@ -34,7 +32,7 @@ func (az *Authorization) UpdateStatus(ctx context.Context, db DB) error { now := time.Now().UTC() expiry, err := time.Parse(time.RFC3339, az.Expires) if err != nil { - return ServerInternalErr(errors.Wrap(err, "error converting expiry string to time")) + return ErrorInternalServerWrap(err, "error converting expiry string to time") } switch az.Status { @@ -62,11 +60,11 @@ func (az *Authorization) UpdateStatus(ctx context.Context, db DB) error { } az.Status = StatusValid default: - return ServerInternalErr(errors.Errorf("unrecognized authorization status: %s", az.Status)) + return NewError(ErrorServerInternalType, "unrecognized authorization status: %s", az.Status) } if err = db.UpdateAuthorization(ctx, az); err != nil { - return ServerInternalErr(err) + return ErrorInternalServerWrap(err, "error updating authorization") } return nil } diff --git a/acme/challenge.go b/acme/challenge.go index 59ca454a..05987427 100644 --- a/acme/challenge.go +++ b/acme/challenge.go @@ -17,29 +17,28 @@ import ( "strings" "time" - "github.com/pkg/errors" "go.step.sm/crypto/jose" ) // Challenge represents an ACME response Challenge type. type Challenge struct { - Type string `json:"type"` - Status Status `json:"status"` - Token string `json:"token"` - Validated string `json:"validated,omitempty"` - URL string `json:"url"` - Error *AError `json:"error,omitempty"` - ID string `json:"-"` - AuthzID string `json:"-"` - AccountID string `json:"-"` - Value string `json:"-"` + Type string `json:"type"` + Status Status `json:"status"` + Token string `json:"token"` + Validated string `json:"validated,omitempty"` + URL string `json:"url"` + Error *Error `json:"error,omitempty"` + ID string `json:"-"` + AuthzID string `json:"-"` + AccountID string `json:"-"` + Value string `json:"-"` } // ToLog enables response logging. func (ch *Challenge) ToLog() (interface{}, error) { b, err := json.Marshal(ch) if err != nil { - return nil, ServerInternalErr(errors.Wrap(err, "error marshaling challenge for logging")) + return nil, ErrorInternalServerWrap(err, "error marshaling challenge for logging") } return string(b), nil } @@ -61,7 +60,7 @@ func (ch *Challenge) Validate(ctx context.Context, db DB, jwk *jose.JSONWebKey, case "tls-alpn-01": return tlsalpn01Validate(ctx, ch, db, jwk, vo) default: - return ServerInternalErr(errors.Errorf("unexpected challenge type '%s'", ch.Type)) + return NewError(ErrorServerInternalType, "unexpected challenge type '%s'", ch.Type) } } @@ -70,19 +69,19 @@ func http01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSONWeb resp, err := vo.httpGet(url) if err != nil { - return storeError(ctx, ch, db, ConnectionErr(errors.Wrapf(err, - "error doing http GET for url %s", url))) + return storeError(ctx, ch, db, ErrorWrap(ErrorConnectionType, err, + "error doing http GET for url %s", url)) } if resp.StatusCode >= 400 { - return storeError(ctx, ch, db, ConnectionErr(errors.Errorf("error doing http GET for url %s with status code %d", - url, resp.StatusCode))) + return storeError(ctx, ch, db, NewError(ErrorConnectionType, + "error doing http GET for url %s with status code %d", url, resp.StatusCode)) } defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) if err != nil { - return ServerInternalErr(errors.Wrapf(err, "error reading "+ - "response body for url %s", url)) + return ErrorInternalServerWrap(err, "error reading "+ + "response body for url %s", url) } keyAuth := strings.Trim(string(body), "\r\n") @@ -91,8 +90,8 @@ func http01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSONWeb return err } if keyAuth != expected { - return storeError(ctx, ch, db, RejectedIdentifierErr(errors.Errorf("keyAuthorization does not match; "+ - "expected %s, but got %s", expected, keyAuth))) + return storeError(ctx, ch, db, NewError(ErrorRejectedIdentifierType, + "keyAuthorization does not match; expected %s, but got %s", expected, keyAuth)) } // Update and store the challenge. @@ -100,7 +99,10 @@ func http01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSONWeb ch.Error = nil ch.Validated = clock.Now().Format(time.RFC3339) - return ServerInternalErr(db.UpdateChallenge(ctx, ch)) + if err = db.UpdateChallenge(ctx, ch); err != nil { + return ErrorInternalServerWrap(err, "error updating challenge") + } + return nil } func tlsalpn01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSONWebKey, vo validateOptions) error { @@ -114,8 +116,8 @@ func tlsalpn01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSON conn, err := vo.tlsDial("tcp", hostPort, config) if err != nil { - return storeError(ctx, ch, db, ConnectionErr(errors.Wrapf(err, - "error doing TLS dial for %s", hostPort))) + return storeError(ctx, ch, db, ErrorWrap(ErrorConnectionType, err, + "error doing TLS dial for %s", hostPort)) } defer conn.Close() @@ -123,20 +125,20 @@ func tlsalpn01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSON certs := cs.PeerCertificates if len(certs) == 0 { - return storeError(ctx, ch, db, RejectedIdentifierErr(errors.Errorf("%s "+ - "challenge for %s resulted in no certificates", ch.Type, ch.Value))) + return storeError(ctx, ch, db, NewError(ErrorRejectedIdentifierType, + "%s challenge for %s resulted in no certificates", ch.Type, ch.Value)) } if !cs.NegotiatedProtocolIsMutual || cs.NegotiatedProtocol != "acme-tls/1" { - return storeError(ctx, ch, db, RejectedIdentifierErr(errors.Errorf("cannot "+ - "negotiate ALPN acme-tls/1 protocol for tls-alpn-01 challenge"))) + return storeError(ctx, ch, db, NewError(ErrorRejectedIdentifierType, + "cannot negotiate ALPN acme-tls/1 protocol for tls-alpn-01 challenge")) } leafCert := certs[0] if len(leafCert.DNSNames) != 1 || !strings.EqualFold(leafCert.DNSNames[0], ch.Value) { - return storeError(ctx, ch, db, RejectedIdentifierErr(errors.Errorf("incorrect certificate for tls-alpn-01 challenge: "+ - "leaf certificate must contain a single DNS name, %v", ch.Value))) + return storeError(ctx, ch, db, NewError(ErrorRejectedIdentifierType, + "incorrect certificate for tls-alpn-01 challenge: leaf certificate must contain a single DNS name, %v", ch.Value)) } idPeAcmeIdentifier := asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 1, 31} @@ -152,22 +154,23 @@ func tlsalpn01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSON for _, ext := range leafCert.Extensions { if idPeAcmeIdentifier.Equal(ext.Id) { if !ext.Critical { - return storeError(ctx, ch, db, RejectedIdentifierErr(errors.Errorf("incorrect "+ - "certificate for tls-alpn-01 challenge: acmeValidationV1 extension not critical"))) + return storeError(ctx, ch, db, NewError(ErrorRejectedIdentifierType, + "incorrect certificate for tls-alpn-01 challenge: acmeValidationV1 extension not critical")) } var extValue []byte rest, err := asn1.Unmarshal(ext.Value, &extValue) if err != nil || len(rest) > 0 || len(hashedKeyAuth) != len(extValue) { - return storeError(ctx, ch, db, RejectedIdentifierErr(errors.Errorf("incorrect "+ - "certificate for tls-alpn-01 challenge: malformed acmeValidationV1 extension value"))) + return storeError(ctx, ch, db, NewError(ErrorRejectedIdentifierType, + "incorrect certificate for tls-alpn-01 challenge: malformed acmeValidationV1 extension value")) } if subtle.ConstantTimeCompare(hashedKeyAuth[:], extValue) != 1 { - return storeError(ctx, ch, db, RejectedIdentifierErr(errors.Errorf("incorrect certificate for tls-alpn-01 challenge: "+ - "expected acmeValidationV1 extension value %s for this challenge but got %s", - hex.EncodeToString(hashedKeyAuth[:]), hex.EncodeToString(extValue)))) + return storeError(ctx, ch, db, NewError(ErrorRejectedIdentifierType, + "incorrect certificate for tls-alpn-01 challenge: "+ + "expected acmeValidationV1 extension value %s for this challenge but got %s", + hex.EncodeToString(hashedKeyAuth[:]), hex.EncodeToString(extValue))) } ch.Status = StatusValid @@ -175,7 +178,7 @@ func tlsalpn01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSON ch.Validated = clock.Now().Format(time.RFC3339) if err = db.UpdateChallenge(ctx, ch); err != nil { - return ServerInternalErr(errors.Wrap(err, "tlsalpn01ValidateChallenge - error updating challenge")) + return ErrorInternalServerWrap(err, "tlsalpn01ValidateChallenge - error updating challenge") } return nil } @@ -186,12 +189,12 @@ func tlsalpn01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSON } if foundIDPeAcmeIdentifierV1Obsolete { - return storeError(ctx, ch, db, RejectedIdentifierErr(errors.Errorf("incorrect "+ - "certificate for tls-alpn-01 challenge: obsolete id-pe-acmeIdentifier in acmeValidationV1 extension"))) + return storeError(ctx, ch, db, NewError(ErrorRejectedIdentifierType, + "incorrect certificate for tls-alpn-01 challenge: obsolete id-pe-acmeIdentifier in acmeValidationV1 extension")) } - return storeError(ctx, ch, db, RejectedIdentifierErr(errors.Errorf("incorrect "+ - "certificate for tls-alpn-01 challenge: missing acmeValidationV1 extension"))) + return storeError(ctx, ch, db, NewError(ErrorRejectedIdentifierType, + "incorrect certificate for tls-alpn-01 challenge: missing acmeValidationV1 extension")) } func dns01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSONWebKey, vo validateOptions) error { @@ -203,8 +206,8 @@ func dns01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSONWebK txtRecords, err := vo.lookupTxt("_acme-challenge." + domain) if err != nil { - return storeError(ctx, ch, db, DNSErr(errors.Wrapf(err, "error looking up TXT "+ - "records for domain %s", domain))) + return storeError(ctx, ch, db, ErrorWrap(ErrorDNSType, err, + "error looking up TXT records for domain %s", domain)) } expectedKeyAuth, err := KeyAuthorization(ch.Token, jwk) @@ -221,8 +224,8 @@ func dns01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSONWebK } } if !found { - return storeError(ctx, ch, db, RejectedIdentifierErr(errors.Errorf("keyAuthorization "+ - "does not match; expected %s, but got %s", expectedKeyAuth, txtRecords))) + return storeError(ctx, ch, db, NewError(ErrorRejectedIdentifierType, + "keyAuthorization does not match; expected %s, but got %s", expectedKeyAuth, txtRecords)) } // Update and store the challenge. @@ -230,7 +233,10 @@ func dns01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSONWebK ch.Error = nil ch.Validated = clock.Now().UTC().Format(time.RFC3339) - return ServerInternalErr(db.UpdateChallenge(ctx, ch)) + if err = db.UpdateChallenge(ctx, ch); err != nil { + return ErrorInternalServerWrap(err, "error updating challenge") + } + return nil } // KeyAuthorization creates the ACME key authorization value from a token @@ -238,7 +244,7 @@ func dns01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSONWebK func KeyAuthorization(token string, jwk *jose.JSONWebKey) (string, error) { thumbprint, err := jwk.Thumbprint(crypto.SHA256) if err != nil { - return "", ServerInternalErr(errors.Wrap(err, "error generating JWK thumbprint")) + return "", ErrorInternalServerWrap(err, "error generating JWK thumbprint") } encPrint := base64.RawURLEncoding.EncodeToString(thumbprint) return fmt.Sprintf("%s.%s", token, encPrint), nil @@ -246,9 +252,9 @@ func KeyAuthorization(token string, jwk *jose.JSONWebKey) (string, error) { // storeError the given error to an ACME error and saves using the DB interface. func storeError(ctx context.Context, ch *Challenge, db DB, err *Error) error { - ch.Error = err.ToACME() + ch.Error = err if err := db.UpdateChallenge(ctx, ch); err != nil { - return ServerInternalErr(errors.Wrap(err, "failure saving error to acme challenge")) + return ErrorInternalServerWrap(err, "failure saving error to acme challenge") } return nil } diff --git a/acme/common.go b/acme/common.go index 1b268327..b9dc6ff2 100644 --- a/acme/common.go +++ b/acme/common.go @@ -6,7 +6,6 @@ import ( "net/url" "time" - "github.com/pkg/errors" "github.com/smallstep/certificates/authority/provisioner" "go.step.sm/crypto/jose" ) @@ -96,7 +95,7 @@ const ( func AccountFromContext(ctx context.Context) (*Account, error) { val, ok := ctx.Value(AccContextKey).(*Account) if !ok || val == nil { - return nil, AccountDoesNotExistErr(nil) + return nil, NewError(ErrorServerInternalType, "account not in context") } return val, nil } @@ -114,7 +113,7 @@ func BaseURLFromContext(ctx context.Context) *url.URL { func JwkFromContext(ctx context.Context) (*jose.JSONWebKey, error) { val, ok := ctx.Value(JwkContextKey).(*jose.JSONWebKey) if !ok || val == nil { - return nil, ServerInternalErr(errors.Errorf("jwk expected in request context")) + return nil, NewError(ErrorServerInternalType, "jwk expected in request context") } return val, nil } @@ -123,7 +122,7 @@ func JwkFromContext(ctx context.Context) (*jose.JSONWebKey, error) { func JwsFromContext(ctx context.Context) (*jose.JSONWebSignature, error) { val, ok := ctx.Value(JwsContextKey).(*jose.JSONWebSignature) if !ok || val == nil { - return nil, ServerInternalErr(errors.Errorf("jws expected in request context")) + return nil, NewError(ErrorServerInternalType, "jws expected in request context") } return val, nil } @@ -133,11 +132,11 @@ func JwsFromContext(ctx context.Context) (*jose.JSONWebSignature, error) { func ProvisionerFromContext(ctx context.Context) (Provisioner, error) { val := ctx.Value(ProvisionerContextKey) if val == nil { - return nil, ServerInternalErr(errors.Errorf("provisioner expected in request context")) + return nil, NewError(ErrorServerInternalType, "provisioner expected in request context") } pval, ok := val.(Provisioner) if !ok || pval == nil { - return nil, ServerInternalErr(errors.Errorf("provisioner in context is not an ACME provisioner")) + return nil, NewError(ErrorServerInternalType, "provisioner in context is not an ACME provisioner") } return pval, nil } diff --git a/acme/directory.go b/acme/directory.go index d5681b73..1b5b8c4b 100644 --- a/acme/directory.go +++ b/acme/directory.go @@ -5,8 +5,6 @@ import ( "encoding/json" "fmt" "net/url" - - "github.com/pkg/errors" ) // Directory represents an ACME directory for configuring clients. @@ -23,7 +21,7 @@ type Directory struct { func (d *Directory) ToLog() (interface{}, error) { b, err := json.Marshal(d) if err != nil { - return nil, ServerInternalErr(errors.Wrap(err, "error marshaling directory for logging")) + return nil, ErrorInternalServerWrap(err, "error marshaling directory for logging") } return string(b), nil } diff --git a/acme/errors.go b/acme/errors.go index dc5b5568..aabc7302 100644 --- a/acme/errors.go +++ b/acme/errors.go @@ -93,8 +93,6 @@ func (ap ProblemType) String() string { return "externalAccountRequired" case ErrorInvalidContactType: return "incorrectResponse" - case ErrorInvalidContactType: - return "invalidContact" case ErrorMalformedType: return "malformed" case ErrorOrderNotReadyType: @@ -133,13 +131,11 @@ var ( officialACMEPrefix = "urn:ietf:params:acme:error:" stepACMEPrefix = "urn:step:acme:error:" errorServerInternalMetadata = errorMetadata{ - ErrorAccountDoesNotExistType: { - typ: officialACMEPrefix + ErrorServerInternalType.String(), - details: "The server experienced an internal error", - status: 500, - }, + typ: officialACMEPrefix + ErrorServerInternalType.String(), + details: "The server experienced an internal error", + status: 500, } - errorMap = [ProblemType]errorMetadata{ + errorMap = map[ProblemType]errorMetadata{ ErrorAccountDoesNotExistType: { typ: officialACMEPrefix + ErrorAccountDoesNotExistType.String(), details: "Account does not exist", @@ -267,7 +263,7 @@ var ( // Error represents an ACME type Error struct { Type string `json:"type"` - Detail string `json:"detail"` + Details string `json:"detail"` Subproblems []interface{} `json:"subproblems,omitempty"` Identifier interface{} `json:"identifier,omitempty"` Err error `json:"-"` @@ -275,13 +271,13 @@ type Error struct { } func NewError(pt ProblemType, msg string, args ...interface{}) *Error { - meta, ok := errorMetadata[typ] + meta, ok := errorMap[pt] if !ok { meta = errorServerInternalMetadata return &Error{ Type: meta.typ, Details: meta.details, - Status: meta.Status, + Status: meta.status, Err: errors.Errorf("unrecognized problemType %v", pt), } } @@ -301,7 +297,7 @@ func ErrorWrap(typ ProblemType, err error, msg string, args ...interface{}) *Err return nil case *Error: if e.Err == nil { - e.Err = errors.Errorf(msg+"; "+e.Detail, args...) + e.Err = errors.Errorf(msg+"; "+e.Details, args...) } else { e.Err = errors.Wrapf(e.Err, msg, args...) } @@ -311,6 +307,11 @@ func ErrorWrap(typ ProblemType, err error, msg string, args ...interface{}) *Err } } +// ErrorInternalServerWrap shortcut to wrap an internal server error type. +func ErrorInternalServerWrap(err error, msg string, args ...interface{}) *Error { + return ErrorWrap(ErrorServerInternalType, err, msg, args...) +} + // StatusCode returns the status code and implements the StatusCoder interface. func (e *Error) StatusCode() int { return e.Status @@ -318,13 +319,13 @@ func (e *Error) StatusCode() int { // Error allows AError to implement the error interface. func (e *Error) Error() string { - return e.Detail + return e.Details } // Cause returns the internal error and implements the Causer interface. func (e *Error) Cause() error { if e.Err == nil { - return errors.New(e.Detail) + return errors.New(e.Details) } return e.Err } diff --git a/acme/order.go b/acme/order.go index 01d3bc20..e0ac822b 100644 --- a/acme/order.go +++ b/acme/order.go @@ -8,7 +8,6 @@ import ( "strings" "time" - "github.com/pkg/errors" "github.com/smallstep/certificates/authority/provisioner" "go.step.sm/crypto/x509util" ) @@ -41,7 +40,7 @@ type Order struct { func (o *Order) ToLog() (interface{}, error) { b, err := json.Marshal(o) if err != nil { - return nil, ServerInternalErr(errors.Wrap(err, "error marshaling order for logging")) + return nil, ErrorInternalServerWrap(err, "error marshaling order for logging") } return string(b), nil } @@ -52,7 +51,7 @@ func (o *Order) UpdateStatus(ctx context.Context, db DB) error { now := time.Now().UTC() expiry, err := time.Parse(time.RFC3339, o.Expires) if err != nil { - return ServerInternalErr(errors.Wrap(err, "order.UpdateStatus - error converting expiry string to time")) + return ErrorInternalServerWrap(err, "order.UpdateStatus - error converting expiry string to time") } switch o.Status { @@ -64,7 +63,7 @@ func (o *Order) UpdateStatus(ctx context.Context, db DB) error { // Check expiry if now.After(expiry) { o.Status = StatusInvalid - o.Error = MalformedErr(errors.New("order has expired")) + o.Error = NewError(ErrorMalformedType, "order has expired") break } return nil @@ -72,7 +71,7 @@ func (o *Order) UpdateStatus(ctx context.Context, db DB) error { // Check expiry if now.After(expiry) { o.Status = StatusInvalid - o.Error = MalformedErr(errors.New("order has expired")) + o.Error = NewError(ErrorMalformedType, "order has expired") break } @@ -105,10 +104,10 @@ func (o *Order) UpdateStatus(ctx context.Context, db DB) error { o.Status = StatusReady default: - return ServerInternalErr(errors.New("unexpected authz status")) + return NewError(ErrorServerInternalType, "unexpected authz status") } default: - return ServerInternalErr(errors.Errorf("unrecognized order status: %s", o.Status)) + return NewError(ErrorServerInternalType, "unrecognized order status: %s", o.Status) } return db.UpdateOrder(ctx, o) } @@ -122,15 +121,15 @@ func (o *Order) Finalize(ctx context.Context, db DB, csr *x509.CertificateReques switch o.Status { case StatusInvalid: - return OrderNotReadyErr(errors.Errorf("order %s has been abandoned", o.ID)) + return NewError(ErrorOrderNotReadyType, "order %s has been abandoned", o.ID) case StatusValid: return nil case StatusPending: - return OrderNotReadyErr(errors.Errorf("order %s is not ready", o.ID)) + return NewError(ErrorOrderNotReadyType, "order %s is not ready", o.ID) case StatusReady: break default: - return ServerInternalErr(errors.Errorf("unexpected status %s for order %s", o.Status, o.ID)) + return NewError(ErrorServerInternalType, "unexpected status %s for order %s", o.Status, o.ID) } // RFC8555: The CSR MUST indicate the exact same set of requested @@ -154,13 +153,15 @@ func (o *Order) Finalize(ctx context.Context, db DB, csr *x509.CertificateReques // absence of other SANs as they will only be set if the templates allows // them. if len(csr.DNSNames) != len(orderNames) { - return BadCSRErr(errors.Errorf("CSR names do not match identifiers exactly: CSR names = %v, Order names = %v", csr.DNSNames, orderNames)) + return NewError(ErrorBadCSRType, "CSR names do not match identifiers exactly: "+ + "CSR names = %v, Order names = %v", csr.DNSNames, orderNames) } sans := make([]x509util.SubjectAlternativeName, len(csr.DNSNames)) for i := range csr.DNSNames { if csr.DNSNames[i] != orderNames[i] { - return BadCSRErr(errors.Errorf("CSR names do not match identifiers exactly: CSR names = %v, Order names = %v", csr.DNSNames, orderNames)) + return NewError(ErrorBadCSRType, "CSR names do not match identifiers exactly: "+ + "CSR names = %v, Order names = %v", csr.DNSNames, orderNames) } sans[i] = x509util.SubjectAlternativeName{ Type: x509util.DNSType, @@ -172,7 +173,7 @@ func (o *Order) Finalize(ctx context.Context, db DB, csr *x509.CertificateReques ctx = provisioner.NewContextWithMethod(ctx, provisioner.SignMethod) signOps, err := p.AuthorizeSign(ctx, "") if err != nil { - return ServerInternalErr(errors.Wrapf(err, "error retrieving authorization options from ACME provisioner")) + return ErrorInternalServerWrap(err, "error retrieving authorization options from ACME provisioner") } // Template data @@ -182,17 +183,17 @@ func (o *Order) Finalize(ctx context.Context, db DB, csr *x509.CertificateReques templateOptions, err := provisioner.TemplateOptions(p.GetOptions(), data) if err != nil { - return ServerInternalErr(errors.Wrapf(err, "error creating template options from ACME provisioner")) + return ErrorInternalServerWrap(err, "error creating template options from ACME provisioner") } signOps = append(signOps, templateOptions) nbf, err := time.Parse(time.RFC3339, o.NotBefore) if err != nil { - return ServerInternalErr(errors.Wrap(err, "error parsing order NotBefore")) + return ErrorInternalServerWrap(err, "error parsing order NotBefore") } naf, err := time.Parse(time.RFC3339, o.NotAfter) if err != nil { - return ServerInternalErr(errors.Wrap(err, "error parsing order NotAfter")) + return ErrorInternalServerWrap(err, "error parsing order NotAfter") } // Sign a new certificate. @@ -201,7 +202,7 @@ func (o *Order) Finalize(ctx context.Context, db DB, csr *x509.CertificateReques NotAfter: provisioner.NewTimeDuration(naf), }, signOps...) if err != nil { - return ServerInternalErr(errors.Wrapf(err, "error signing certificate for order %s", o.ID)) + return ErrorInternalServerWrap(err, "error signing certificate for order %s", o.ID) } cert := &Certificate{