forked from TrueCloudLab/certificates
acme: Return 501 for the key-change route
RFC 8555 § 7.3.5 is not listed as optional but we do not currently support it. Rather than 404, return a 501 to inform clients that this functionality is not yet implemented. The notImplmented error type is not an official error registered in the ietf:params:acme:error namespace, so prefix if with step:acme:error. An ACME server is allowed to return other errors and clients should display the message detail to users. Fixes: https://github.com/smallstep/certificates/issues/209
This commit is contained in:
parent
ab0f2aedcc
commit
b26e6e42b3
2 changed files with 40 additions and 1 deletions
|
@ -59,6 +59,7 @@ func (h *Handler) Route(r api.Router) {
|
||||||
|
|
||||||
r.MethodFunc("POST", getLink(acme.NewAccountLink, "{provisionerID}", false, nil), extractPayloadByJWK(h.NewAccount))
|
r.MethodFunc("POST", getLink(acme.NewAccountLink, "{provisionerID}", false, nil), extractPayloadByJWK(h.NewAccount))
|
||||||
r.MethodFunc("POST", getLink(acme.AccountLink, "{provisionerID}", false, nil, "{accID}"), extractPayloadByKid(h.GetUpdateAccount))
|
r.MethodFunc("POST", getLink(acme.AccountLink, "{provisionerID}", false, nil, "{accID}"), extractPayloadByKid(h.GetUpdateAccount))
|
||||||
|
r.MethodFunc("POST", getLink(acme.KeyChangeLink, "{provisionerID}", false, nil, "{accID}"), extractPayloadByKid(h.NotImplemented))
|
||||||
r.MethodFunc("POST", getLink(acme.NewOrderLink, "{provisionerID}", false, nil), extractPayloadByKid(h.NewOrder))
|
r.MethodFunc("POST", getLink(acme.NewOrderLink, "{provisionerID}", false, nil), extractPayloadByKid(h.NewOrder))
|
||||||
r.MethodFunc("POST", getLink(acme.OrderLink, "{provisionerID}", false, nil, "{ordID}"), extractPayloadByKid(h.isPostAsGet(h.GetOrder)))
|
r.MethodFunc("POST", getLink(acme.OrderLink, "{provisionerID}", false, nil, "{ordID}"), extractPayloadByKid(h.isPostAsGet(h.GetOrder)))
|
||||||
r.MethodFunc("POST", getLink(acme.OrdersByAccountLink, "{provisionerID}", false, nil, "{accID}"), extractPayloadByKid(h.isPostAsGet(h.GetOrdersByAccount)))
|
r.MethodFunc("POST", getLink(acme.OrdersByAccountLink, "{provisionerID}", false, nil, "{accID}"), extractPayloadByKid(h.isPostAsGet(h.GetOrdersByAccount)))
|
||||||
|
@ -66,6 +67,7 @@ func (h *Handler) Route(r api.Router) {
|
||||||
r.MethodFunc("POST", getLink(acme.AuthzLink, "{provisionerID}", false, nil, "{authzID}"), extractPayloadByKid(h.isPostAsGet(h.GetAuthz)))
|
r.MethodFunc("POST", getLink(acme.AuthzLink, "{provisionerID}", false, nil, "{authzID}"), extractPayloadByKid(h.isPostAsGet(h.GetAuthz)))
|
||||||
r.MethodFunc("POST", getLink(acme.ChallengeLink, "{provisionerID}", false, nil, "{chID}"), extractPayloadByKid(h.GetChallenge))
|
r.MethodFunc("POST", getLink(acme.ChallengeLink, "{provisionerID}", false, nil, "{chID}"), extractPayloadByKid(h.GetChallenge))
|
||||||
r.MethodFunc("POST", getLink(acme.CertificateLink, "{provisionerID}", false, nil, "{certID}"), extractPayloadByKid(h.isPostAsGet(h.GetCertificate)))
|
r.MethodFunc("POST", getLink(acme.CertificateLink, "{provisionerID}", false, nil, "{certID}"), extractPayloadByKid(h.isPostAsGet(h.GetCertificate)))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetNonce just sets the right header since a Nonce is added to each response
|
// GetNonce just sets the right header since a Nonce is added to each response
|
||||||
|
@ -89,6 +91,11 @@ func (h *Handler) GetDirectory(w http.ResponseWriter, r *http.Request) {
|
||||||
api.JSON(w, dir)
|
api.JSON(w, dir)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NotImplemented returns a 501. This is a place holder for future functionality.
|
||||||
|
func (h *Handler) NotImplemented(w http.ResponseWriter, r *http.Request) {
|
||||||
|
api.WriteError(w, acme.NotImplemented(nil).ToACME())
|
||||||
|
}
|
||||||
|
|
||||||
// GetAuthz ACME api for retrieving an Authz.
|
// GetAuthz ACME api for retrieving an Authz.
|
||||||
func (h *Handler) GetAuthz(w http.ResponseWriter, r *http.Request) {
|
func (h *Handler) GetAuthz(w http.ResponseWriter, r *http.Request) {
|
||||||
acc, err := acme.AccountFromContext(r.Context())
|
acc, err := acme.AccountFromContext(r.Context())
|
||||||
|
|
|
@ -194,6 +194,16 @@ func ServerInternalErr(err error) *Error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NotImplemented returns a new acme error.
|
||||||
|
func NotImplemented(err error) *Error {
|
||||||
|
return &Error{
|
||||||
|
Type: notImplemented,
|
||||||
|
Detail: "The requested operation is not implemented",
|
||||||
|
Status: 501,
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TLSErr returns a new acme error.
|
// TLSErr returns a new acme error.
|
||||||
func TLSErr(err error) *Error {
|
func TLSErr(err error) *Error {
|
||||||
return &Error{
|
return &Error{
|
||||||
|
@ -296,6 +306,8 @@ const (
|
||||||
unsupportedIdentifierErr
|
unsupportedIdentifierErr
|
||||||
// Visit the “instance” URL and take actions specified there
|
// Visit the “instance” URL and take actions specified there
|
||||||
userActionRequiredErr
|
userActionRequiredErr
|
||||||
|
// The operation is not implemented
|
||||||
|
notImplemented
|
||||||
)
|
)
|
||||||
|
|
||||||
// String returns the string representation of the acme problem type,
|
// String returns the string representation of the acme problem type,
|
||||||
|
@ -350,6 +362,8 @@ func (ap ProbType) String() string {
|
||||||
return "unsupportedIdentifier"
|
return "unsupportedIdentifier"
|
||||||
case userActionRequiredErr:
|
case userActionRequiredErr:
|
||||||
return "userActionRequired"
|
return "userActionRequired"
|
||||||
|
case notImplemented:
|
||||||
|
return "notImplemented"
|
||||||
default:
|
default:
|
||||||
return "unsupported type"
|
return "unsupported type"
|
||||||
}
|
}
|
||||||
|
@ -398,10 +412,28 @@ func (e *Error) Cause() error {
|
||||||
return e.Err
|
return e.Err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Official returns true if this error is registered with the IETF.
|
||||||
|
//
|
||||||
|
// The RFC says:
|
||||||
|
//
|
||||||
|
// This list is not exhaustive. The server MAY return errors whose
|
||||||
|
// "type" field is set to a URI other than those defined above. Servers
|
||||||
|
// MUST NOT use the ACME URN namespace for errors not listed in the
|
||||||
|
// appropriate IANA registry (see Section 9.6). Clients SHOULD display
|
||||||
|
// the "detail" field of all errors.
|
||||||
|
//
|
||||||
|
func (e *Error) Official() bool {
|
||||||
|
return e.Type != notImplemented
|
||||||
|
}
|
||||||
|
|
||||||
// ToACME returns an acme representation of the problem type.
|
// ToACME returns an acme representation of the problem type.
|
||||||
func (e *Error) ToACME() *AError {
|
func (e *Error) ToACME() *AError {
|
||||||
|
prefix := "urn:step:acme:error"
|
||||||
|
if e.Official() {
|
||||||
|
prefix = "urn:ietf:params:acme:error:"
|
||||||
|
}
|
||||||
ae := &AError{
|
ae := &AError{
|
||||||
Type: "urn:ietf:params:acme:error:" + e.Type.String(),
|
Type: prefix + e.Type.String(),
|
||||||
Detail: e.Error(),
|
Detail: e.Error(),
|
||||||
Status: e.Status,
|
Status: e.Status,
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue