mirror of
https://github.com/zitadel/zitadel.git
synced 2025-01-11 16:43:40 +00:00
141 lines
3.8 KiB
Go
141 lines
3.8 KiB
Go
|
package serrors
|
||
|
|
||
|
import (
|
||
|
"context"
|
||
|
"encoding/json"
|
||
|
"errors"
|
||
|
"fmt"
|
||
|
"net/http"
|
||
|
"strconv"
|
||
|
|
||
|
"github.com/zitadel/logging"
|
||
|
"golang.org/x/text/language"
|
||
|
|
||
|
http_util "github.com/zitadel/zitadel/internal/api/http"
|
||
|
zhttp_middleware "github.com/zitadel/zitadel/internal/api/http/middleware"
|
||
|
"github.com/zitadel/zitadel/internal/api/scim/schemas"
|
||
|
"github.com/zitadel/zitadel/internal/i18n"
|
||
|
"github.com/zitadel/zitadel/internal/zerrors"
|
||
|
)
|
||
|
|
||
|
type scimErrorType string
|
||
|
|
||
|
type wrappedScimError struct {
|
||
|
Parent error
|
||
|
ScimType scimErrorType
|
||
|
}
|
||
|
|
||
|
type scimError struct {
|
||
|
Schemas []schemas.ScimSchemaType `json:"schemas"`
|
||
|
ScimType scimErrorType `json:"scimType,omitempty"`
|
||
|
Detail string `json:"detail,omitempty"`
|
||
|
StatusCode int `json:"-"`
|
||
|
Status string `json:"status"`
|
||
|
ZitadelDetail *errorDetail `json:"urn:ietf:params:scim:api:zitadel:messages:2.0:ErrorDetail,omitempty"`
|
||
|
}
|
||
|
|
||
|
type errorDetail struct {
|
||
|
ID string `json:"id"`
|
||
|
Message string `json:"message"`
|
||
|
}
|
||
|
|
||
|
const (
|
||
|
// ScimTypeInvalidValue A required value was missing,
|
||
|
// or the value specified was not compatible with the operation,
|
||
|
// or attribute type (see Section 2.2 of RFC7643),
|
||
|
// or resource schema (see Section 4 of RFC7643).
|
||
|
ScimTypeInvalidValue scimErrorType = "invalidValue"
|
||
|
|
||
|
// ScimTypeInvalidSyntax The request body message structure was invalid or did
|
||
|
// not conform to the request schema.
|
||
|
ScimTypeInvalidSyntax scimErrorType = "invalidSyntax"
|
||
|
)
|
||
|
|
||
|
var translator *i18n.Translator
|
||
|
|
||
|
func ErrorHandler(next zhttp_middleware.HandlerFuncWithError) http.Handler {
|
||
|
var err error
|
||
|
translator, err = i18n.NewZitadelTranslator(language.English)
|
||
|
logging.OnError(err).Panic("unable to get translator")
|
||
|
|
||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||
|
if err = next(w, r); err == nil {
|
||
|
return
|
||
|
}
|
||
|
|
||
|
scimErr := mapToScimJsonError(r.Context(), err)
|
||
|
w.WriteHeader(scimErr.StatusCode)
|
||
|
|
||
|
jsonErr := json.NewEncoder(w).Encode(scimErr)
|
||
|
logging.OnError(jsonErr).Warn("Failed to marshal scim error response")
|
||
|
})
|
||
|
}
|
||
|
|
||
|
func ThrowInvalidValue(parent error) error {
|
||
|
return &wrappedScimError{
|
||
|
Parent: parent,
|
||
|
ScimType: ScimTypeInvalidValue,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func ThrowInvalidSyntax(parent error) error {
|
||
|
return &wrappedScimError{
|
||
|
Parent: parent,
|
||
|
ScimType: ScimTypeInvalidSyntax,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (err *scimError) Error() string {
|
||
|
return fmt.Sprintf("SCIM Error: %s: %s", err.ScimType, err.Detail)
|
||
|
}
|
||
|
|
||
|
func (err *wrappedScimError) Error() string {
|
||
|
return fmt.Sprintf("SCIM Error: %s: %s", err.ScimType, err.Parent.Error())
|
||
|
}
|
||
|
|
||
|
func mapToScimJsonError(ctx context.Context, err error) *scimError {
|
||
|
scimErr := new(wrappedScimError)
|
||
|
if ok := errors.As(err, &scimErr); ok {
|
||
|
mappedErr := mapToScimJsonError(ctx, scimErr.Parent)
|
||
|
mappedErr.ScimType = scimErr.ScimType
|
||
|
return mappedErr
|
||
|
}
|
||
|
|
||
|
zitadelErr := new(zerrors.ZitadelError)
|
||
|
if ok := errors.As(err, &zitadelErr); !ok {
|
||
|
return &scimError{
|
||
|
Schemas: []schemas.ScimSchemaType{schemas.IdError},
|
||
|
Detail: "Unknown internal server error",
|
||
|
Status: strconv.Itoa(http.StatusInternalServerError),
|
||
|
StatusCode: http.StatusInternalServerError,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
statusCode, ok := http_util.ZitadelErrorToHTTPStatusCode(err)
|
||
|
if !ok {
|
||
|
statusCode = http.StatusInternalServerError
|
||
|
}
|
||
|
|
||
|
localizedMsg := translator.LocalizeFromCtx(ctx, zitadelErr.GetMessage(), nil)
|
||
|
return &scimError{
|
||
|
Schemas: []schemas.ScimSchemaType{schemas.IdError, schemas.IdZitadelErrorDetail},
|
||
|
ScimType: mapErrorToScimErrorType(err),
|
||
|
Detail: localizedMsg,
|
||
|
StatusCode: statusCode,
|
||
|
Status: strconv.Itoa(statusCode),
|
||
|
ZitadelDetail: &errorDetail{
|
||
|
ID: zitadelErr.GetID(),
|
||
|
Message: zitadelErr.GetMessage(),
|
||
|
},
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func mapErrorToScimErrorType(err error) scimErrorType {
|
||
|
switch {
|
||
|
case zerrors.IsErrorInvalidArgument(err):
|
||
|
return ScimTypeInvalidValue
|
||
|
default:
|
||
|
return ""
|
||
|
}
|
||
|
}
|