smallstep-certificates/authority/admin/errors.go

212 lines
5.2 KiB
Go
Raw Normal View History

package admin
2021-05-03 19:48:20 +00:00
import (
"encoding/json"
"fmt"
"net/http"
"github.com/pkg/errors"
"github.com/smallstep/certificates/api/render"
2021-05-03 19:48:20 +00:00
)
// ProblemType is the type of the Admin problem.
2021-05-03 19:48:20 +00:00
type ProblemType int
const (
// ErrorNotFoundType resource not found.
ErrorNotFoundType ProblemType = iota
// ErrorAuthorityMismatchType resource Authority ID does not match the
// context Authority ID.
ErrorAuthorityMismatchType
// ErrorDeletedType resource has been deleted.
ErrorDeletedType
2021-05-12 07:03:40 +00:00
// ErrorBadRequestType bad request.
ErrorBadRequestType
// ErrorNotImplementedType not implemented.
ErrorNotImplementedType
2022-04-21 10:14:03 +00:00
// ErrorUnauthorizedType unauthorized.
ErrorUnauthorizedType
2021-05-03 19:48:20 +00:00
// ErrorServerInternalType internal server error.
ErrorServerInternalType
2022-04-21 10:14:03 +00:00
// ErrorConflictType conflict.
ErrorConflictType
2021-05-03 19:48:20 +00:00
)
// String returns the string representation of the admin problem type,
2021-05-03 19:48:20 +00:00
// fulfilling the Stringer interface.
func (ap ProblemType) String() string {
switch ap {
case ErrorNotFoundType:
return "notFound"
case ErrorAuthorityMismatchType:
return "authorityMismatch"
case ErrorDeletedType:
return "deleted"
2021-05-12 07:03:40 +00:00
case ErrorBadRequestType:
return "badRequest"
case ErrorNotImplementedType:
return "notImplemented"
case ErrorUnauthorizedType:
return "unauthorized"
2021-05-03 19:48:20 +00:00
case ErrorServerInternalType:
return "internalServerError"
2022-04-21 10:14:03 +00:00
case ErrorConflictType:
return "conflict"
2021-05-03 19:48:20 +00:00
default:
return fmt.Sprintf("unsupported error type '%d'", int(ap))
}
}
type errorMetadata struct {
details string
status int
typ string
String string
}
var (
errorServerInternalMetadata = errorMetadata{
typ: ErrorServerInternalType.String(),
details: "the server experienced an internal error",
2022-04-21 10:14:03 +00:00
status: http.StatusInternalServerError,
2021-05-03 19:48:20 +00:00
}
errorMap = map[ProblemType]errorMetadata{
ErrorNotFoundType: {
typ: ErrorNotFoundType.String(),
details: "resource not found",
status: http.StatusNotFound,
2021-05-03 19:48:20 +00:00
},
ErrorAuthorityMismatchType: {
typ: ErrorAuthorityMismatchType.String(),
details: "resource not owned by authority",
status: http.StatusUnauthorized,
2021-05-03 19:48:20 +00:00
},
ErrorDeletedType: {
2021-05-12 07:03:40 +00:00
typ: ErrorDeletedType.String(),
2021-05-03 19:48:20 +00:00
details: "resource is deleted",
status: http.StatusNotFound,
},
ErrorNotImplementedType: {
typ: ErrorNotImplementedType.String(),
details: "not implemented",
status: http.StatusNotImplemented,
2021-05-03 19:48:20 +00:00
},
2021-05-12 07:03:40 +00:00
ErrorBadRequestType: {
typ: ErrorBadRequestType.String(),
details: "bad request",
status: http.StatusBadRequest,
},
ErrorUnauthorizedType: {
typ: ErrorUnauthorizedType.String(),
details: "unauthorized",
status: http.StatusUnauthorized,
2021-05-12 07:03:40 +00:00
},
2021-05-03 19:48:20 +00:00
ErrorServerInternalType: errorServerInternalMetadata,
2022-04-21 10:14:03 +00:00
ErrorConflictType: {
typ: ErrorConflictType.String(),
details: "conflict",
status: http.StatusConflict,
},
2021-05-03 19:48:20 +00:00
}
)
// Error represents an Admin error
2021-05-03 19:48:20 +00:00
type Error struct {
2021-05-18 04:07:25 +00:00
Type string `json:"type"`
Detail string `json:"detail"`
Message string `json:"message"`
Err error `json:"-"`
Status int `json:"-"`
2021-05-03 19:48:20 +00:00
}
2021-05-07 00:03:12 +00:00
// IsType returns true if the error type matches the input type.
func (e *Error) IsType(pt ProblemType) bool {
return pt.String() == e.Type
}
2021-05-03 19:48:20 +00:00
// NewError creates a new Error type.
func NewError(pt ProblemType, msg string, args ...interface{}) *Error {
return newError(pt, errors.Errorf(msg, args...))
}
func newError(pt ProblemType, err error) *Error {
meta, ok := errorMap[pt]
if !ok {
meta = errorServerInternalMetadata
return &Error{
Type: meta.typ,
Detail: meta.details,
Status: meta.status,
Err: err,
}
}
return &Error{
Type: meta.typ,
Detail: meta.details,
Status: meta.status,
Err: err,
}
}
// NewErrorISE creates a new ErrorServerInternalType Error.
func NewErrorISE(msg string, args ...interface{}) *Error {
return NewError(ErrorServerInternalType, msg, args...)
}
// WrapError attempts to wrap the internal error.
func WrapError(typ ProblemType, err error, msg string, args ...interface{}) *Error {
switch e := err.(type) {
case nil:
return nil
case *Error:
if e.Err == nil {
e.Err = errors.Errorf(msg+"; "+e.Detail, args...)
} else {
e.Err = errors.Wrapf(e.Err, msg, args...)
}
return e
default:
return newError(typ, errors.Wrapf(err, msg, args...))
}
}
// WrapErrorISE shortcut to wrap an internal server error type.
func WrapErrorISE(err error, msg string, args ...interface{}) *Error {
return WrapError(ErrorServerInternalType, err, msg, args...)
}
// StatusCode returns the status code and implements the StatusCoder interface.
func (e *Error) StatusCode() int {
return e.Status
}
// Error allows AError to implement the error interface.
func (e *Error) Error() string {
2021-05-18 04:07:25 +00:00
return e.Err.Error()
2021-05-03 19:48:20 +00:00
}
// 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 e.Err
}
// ToLog implements the EnableLogger interface.
func (e *Error) ToLog() (interface{}, error) {
b, err := json.Marshal(e)
if err != nil {
return nil, WrapErrorISE(err, "error marshaling authority.Error for logging")
}
return string(b), nil
}
// Render implements render.RenderableError for Error.
func (e *Error) Render(w http.ResponseWriter) {
e.Message = e.Err.Error()
2021-05-03 19:48:20 +00:00
render.JSONStatus(w, e, e.StatusCode())
2021-05-03 19:48:20 +00:00
}