diff --git a/internal/auth/repository/eventsourcing/eventstore/auth_request.go b/internal/auth/repository/eventsourcing/eventstore/auth_request.go index 7063ab8563..b532c1c0d8 100644 --- a/internal/auth/repository/eventsourcing/eventstore/auth_request.go +++ b/internal/auth/repository/eventsourcing/eventstore/auth_request.go @@ -138,7 +138,7 @@ func (repo *AuthRequestRepo) VerifyPassword(ctx context.Context, id, userID, pas return err } if request.UserID != userID { - return errors.ThrowPreconditionFailed(nil, "EVENT-ds35D", "user id does not match request id") + return errors.ThrowPreconditionFailed(nil, "EVENT-ds35D", "Errors.User.NotMatchingUserID") } return repo.UserEvents.CheckPassword(ctx, userID, password, request.WithCurrentInfo(info)) } @@ -149,7 +149,7 @@ func (repo *AuthRequestRepo) VerifyMfaOTP(ctx context.Context, authRequestID, us return err } if request.UserID != userID { - return errors.ThrowPreconditionFailed(nil, "EVENT-ADJ26", "user id does not match request id") + return errors.ThrowPreconditionFailed(nil, "EVENT-ADJ26", "Errors.User.NotMatchingUserID") } return repo.UserEvents.CheckMfaOTP(ctx, userID, code, request.WithCurrentInfo(info)) } diff --git a/internal/auth_request/repository/cache/cache.go b/internal/auth_request/repository/cache/cache.go index dc930e6d36..3d981e7e3d 100644 --- a/internal/auth_request/repository/cache/cache.go +++ b/internal/auth_request/repository/cache/cache.go @@ -65,16 +65,16 @@ func (c *AuthRequestCache) getAuthRequest(key, value string) (*model.AuthRequest err := c.client.QueryRow(query, value).Scan(&b, &requestType) if err != nil { if errors.Is(err, sql.ErrNoRows) { - return nil, caos_errs.ThrowNotFound(err, "CACHE-d24aD", "auth request not found") + return nil, caos_errs.ThrowNotFound(err, "CACHE-d24aD", "Errors.AuthRequest.NotFound") } - return nil, caos_errs.ThrowInternal(err, "CACHE-as3kj", "unable to get auth request from database") + return nil, caos_errs.ThrowInternal(err, "CACHE-as3kj", "Errors.Internal") } request, err := model.NewAuthRequestFromType(requestType) if err == nil { err = json.Unmarshal(b, request) } if err != nil { - return nil, caos_errs.ThrowInternal(err, "CACHE-2wshg", "unable to unmarshal auth request") + return nil, caos_errs.ThrowInternal(err, "CACHE-2wshg", "Errors.Internal") } return request, nil } @@ -82,15 +82,15 @@ func (c *AuthRequestCache) getAuthRequest(key, value string) (*model.AuthRequest func (c *AuthRequestCache) saveAuthRequest(request *model.AuthRequest, query string, param interface{}) error { b, err := json.Marshal(request) if err != nil { - return caos_errs.ThrowInternal(err, "CACHE-os0GH", "unable to marshal auth request") + return caos_errs.ThrowInternal(err, "CACHE-os0GH", "Errors.Internal") } stmt, err := c.client.Prepare(query) if err != nil { - return caos_errs.ThrowInternal(err, "CACHE-su3GK", "sql prepare failed") + return caos_errs.ThrowInternal(err, "CACHE-su3GK", "Errors.Internal") } _, err = stmt.Exec(request.ID, b, param) if err != nil { - return caos_errs.ThrowInternal(err, "CACHE-sj8iS", "unable to save auth request") + return caos_errs.ThrowInternal(err, "CACHE-sj8iS", "Errors.Internal") } return nil } diff --git a/internal/crypto/code.go b/internal/crypto/code.go index f24c455c2b..fa3fed92e0 100644 --- a/internal/crypto/code.go +++ b/internal/crypto/code.go @@ -123,7 +123,7 @@ func IsCodeExpired(creationDate time.Time, expiry time.Duration) bool { func VerifyCode(creationDate time.Time, expiry time.Duration, cryptoCode *CryptoValue, verificationCode string, g Generator) error { if IsCodeExpired(creationDate, expiry) { - return errors.ThrowPreconditionFailed(nil, "CODE-QvUQ4P", "verification code is expired") + return errors.ThrowPreconditionFailed(nil, "CODE-QvUQ4P", "Errors.User.Code.Expired") } switch alg := g.Alg().(type) { case EncryptionAlgorithm: @@ -131,7 +131,7 @@ func VerifyCode(creationDate time.Time, expiry time.Duration, cryptoCode *Crypto case HashAlgorithm: return verifyHashedCode(cryptoCode, verificationCode, alg) } - return errors.ThrowInvalidArgument(nil, "CODE-fW2gNa", "generator alg is not supported") + return errors.ThrowInvalidArgument(nil, "CODE-fW2gNa", "Errors.User.Code.GeneratorAlgNotSupported") } func generateRandomString(length uint, chars []rune) (string, error) { @@ -158,7 +158,7 @@ func generateRandomString(length uint, chars []rune) (string, error) { func verifyEncryptedCode(cryptoCode *CryptoValue, verificationCode string, alg EncryptionAlgorithm) error { if cryptoCode == nil { - return errors.ThrowInvalidArgument(nil, "CRYPT-aqrFV", "cryptoCode must not be nil") + return errors.ThrowInvalidArgument(nil, "CRYPT-aqrFV", "Errors.User.Code.CryptoCodeNil") } code, err := DecryptString(cryptoCode, alg) if err != nil { @@ -166,7 +166,7 @@ func verifyEncryptedCode(cryptoCode *CryptoValue, verificationCode string, alg E } if code != verificationCode { - return errors.ThrowInvalidArgument(nil, "CODE-woT0xc", "verification code is invalid") + return errors.ThrowInvalidArgument(nil, "CODE-woT0xc", "Errors.User.Code.Invalid") } return nil } diff --git a/internal/errors/caos_error.go b/internal/errors/caos_error.go index 1ed28392ca..5559e5728f 100644 --- a/internal/errors/caos_error.go +++ b/internal/errors/caos_error.go @@ -2,6 +2,7 @@ package errors import ( "fmt" + "reflect" ) var _ Error = (*CaosError)(nil) @@ -51,3 +52,8 @@ func (err *CaosError) Is(target error) bool { _, ok := target.(*CaosError) return ok } + +func (err *CaosError) As(target interface{}) bool { + reflect.Indirect(reflect.ValueOf(target)).Set(reflect.ValueOf(err)) + return true +} diff --git a/internal/eventstore/sdk/sdk.go b/internal/eventstore/sdk/sdk.go index cf231d44c2..2232617994 100644 --- a/internal/eventstore/sdk/sdk.go +++ b/internal/eventstore/sdk/sdk.go @@ -23,7 +23,7 @@ func Filter(ctx context.Context, filter filterFunc, appender appendFunc, query * } err = appender(events...) if err != nil { - return ThrowAppendEventError(err, "SDK-awiWK", "appender failed") + return ThrowAppendEventError(err, "SDK-awiWK", "Errors.Internal") } return nil } @@ -34,7 +34,7 @@ func Filter(ctx context.Context, filter filterFunc, appender appendFunc, query * // the given events are appended by the appender func Push(ctx context.Context, push pushFunc, appender appendFunc, aggregaters ...AggregateFunc) (err error) { if len(aggregaters) < 1 { - return errors.ThrowPreconditionFailed(nil, "SDK-q9wjp", "no aggregaters passed") + return errors.ThrowPreconditionFailed(nil, "SDK-q9wjp", "Errors.Internal") } aggregates, err := makeAggregates(ctx, aggregaters) diff --git a/internal/form/parser.go b/internal/form/parser.go index e0b4406bb1..f466c04f8d 100644 --- a/internal/form/parser.go +++ b/internal/form/parser.go @@ -20,7 +20,7 @@ func NewParser() *Parser { func (p *Parser) Parse(r *http.Request, data interface{}) error { err := r.ParseForm() if err != nil { - return errors.ThrowInternal(err, "FORM-lCC9zI", "error parsing http form") + return errors.ThrowInternal(err, "FORM-lCC9zI", "Errors.Internal") } return p.decoder.Decode(data, r.Form) diff --git a/internal/login/handler/change_password_handler.go b/internal/login/handler/change_password_handler.go index bb39181a12..a5dd2865e3 100644 --- a/internal/login/handler/change_password_handler.go +++ b/internal/login/handler/change_password_handler.go @@ -33,7 +33,7 @@ func (l *Login) handleChangePassword(w http.ResponseWriter, r *http.Request) { func (l *Login) renderChangePassword(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest, err error) { var errType, errMessage string if err != nil { - errMessage = err.Error() + errMessage = l.getErrorMessage(r, err) } data := userData{ baseData: l.getBaseData(r, authReq, "Change Password", errType, errMessage), diff --git a/internal/login/handler/init_password_handler.go b/internal/login/handler/init_password_handler.go index f6a61717a8..57b138d50b 100644 --- a/internal/login/handler/init_password_handler.go +++ b/internal/login/handler/init_password_handler.go @@ -51,7 +51,7 @@ func (l *Login) handleInitPasswordCheck(w http.ResponseWriter, r *http.Request) func (l *Login) checkPWCode(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest, data *initPasswordFormData, err error) { if data.Password != data.PasswordConfirm { - err := errors.ThrowInvalidArgument(nil, "VIEW-KaGue", "passwords dont match") + err := errors.ThrowInvalidArgument(nil, "VIEW-KaGue", "Errors.User.Password.ConfirmationWrong") l.renderInitPassword(w, r, authReq, data.UserID, data.Code, err) return } @@ -75,7 +75,7 @@ func (l *Login) resendPasswordSet(w http.ResponseWriter, r *http.Request, authRe func (l *Login) renderInitPassword(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest, userID, code string, err error) { var errType, errMessage string if err != nil { - errMessage = err.Error() + errMessage = l.getErrorMessage(r, err) } if userID == "" && authReq != nil { userID = authReq.UserID diff --git a/internal/login/handler/init_user_handler.go b/internal/login/handler/init_user_handler.go index b6d89f281a..fa2eed23ff 100644 --- a/internal/login/handler/init_user_handler.go +++ b/internal/login/handler/init_user_handler.go @@ -51,7 +51,7 @@ func (l *Login) handleInitUserCheck(w http.ResponseWriter, r *http.Request) { func (l *Login) checkUserInitCode(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest, data *initUserFormData, err error) { if data.Password != data.PasswordConfirm { - err := caos_errs.ThrowInvalidArgument(nil, "VIEW-fsdfd", "passwords dont match") + err := caos_errs.ThrowInvalidArgument(nil, "VIEW-fsdfd", "Errors.User.Password.ConfirmationWrong") l.renderInitUser(w, r, nil, data.UserID, data.Code, err) return } @@ -79,7 +79,7 @@ func (l *Login) resendUserInit(w http.ResponseWriter, r *http.Request, authReq * func (l *Login) renderInitUser(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest, userID, code string, err error) { var errType, errMessage string if err != nil { - errMessage = err.Error() + errMessage = l.getErrorMessage(r, err) } if authReq != nil { userID = authReq.UserID @@ -93,12 +93,12 @@ func (l *Login) renderInitUser(w http.ResponseWriter, r *http.Request, authReq * } func (l *Login) renderInitUserDone(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest) { - var errType, errMessage, userName string + var userName string if authReq != nil { userName = authReq.UserName } data := userData{ - baseData: l.getBaseData(r, authReq, "User Init Done", errType, errMessage), + baseData: l.getBaseData(r, authReq, "User Init Done", "", ""), UserName: userName, } l.renderer.RenderTemplate(w, r, l.renderer.Templates[tmplInitUserDone], data, nil) diff --git a/internal/login/handler/login_handler.go b/internal/login/handler/login_handler.go index e009426383..105ee92e07 100644 --- a/internal/login/handler/login_handler.go +++ b/internal/login/handler/login_handler.go @@ -53,7 +53,7 @@ func (l *Login) handleUsernameCheck(w http.ResponseWriter, r *http.Request) { func (l *Login) renderLogin(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest, err error) { var errType, errMessage string if err != nil { - errMessage = err.Error() + errMessage = l.getErrorMessage(r, err) } data := userData{ baseData: l.getBaseData(r, authReq, "Login", errType, errMessage), diff --git a/internal/login/handler/mail_verify_handler.go b/internal/login/handler/mail_verify_handler.go index ba99a56e50..532f634fe3 100644 --- a/internal/login/handler/mail_verify_handler.go +++ b/internal/login/handler/mail_verify_handler.go @@ -70,7 +70,7 @@ func (l *Login) checkMailCode(w http.ResponseWriter, r *http.Request, authReq *m func (l *Login) renderMailVerification(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest, userID string, err error) { var errType, errMessage string if err != nil { - errMessage = err.Error() + errMessage = l.getErrorMessage(r, err) } if userID == "" { userID = authReq.UserID diff --git a/internal/login/handler/mfa_init_verify_handler.go b/internal/login/handler/mfa_init_verify_handler.go index 53a9fea609..20ffa89416 100644 --- a/internal/login/handler/mfa_init_verify_handler.go +++ b/internal/login/handler/mfa_init_verify_handler.go @@ -64,7 +64,7 @@ func (l *Login) handleOtpVerify(w http.ResponseWriter, r *http.Request, authReq func (l *Login) renderMfaInitVerify(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest, data *mfaVerifyData, err error) { var errType, errMessage string if err != nil { - errMessage = err.Error() + errMessage = l.getErrorMessage(r, err) } data.baseData = l.getBaseData(r, authReq, "Mfa Init Verify", errType, errMessage) data.UserName = authReq.UserName diff --git a/internal/login/handler/mfa_prompt_handler.go b/internal/login/handler/mfa_prompt_handler.go index 863bea60e3..13f6ca5196 100644 --- a/internal/login/handler/mfa_prompt_handler.go +++ b/internal/login/handler/mfa_prompt_handler.go @@ -39,7 +39,7 @@ func (l *Login) handleMfaPrompt(w http.ResponseWriter, r *http.Request) { func (l *Login) renderMfaPrompt(w http.ResponseWriter, r *http.Request, authSession *model.AuthRequest, mfaPromptData *model.MfaPromptStep, err error) { var errType, errMessage string if err != nil { - errMessage = err.Error() + errMessage = l.getErrorMessage(r, err) } data := mfaData{ baseData: l.getBaseData(r, authSession, "Mfa Prompt", errType, errMessage), @@ -47,7 +47,7 @@ func (l *Login) renderMfaPrompt(w http.ResponseWriter, r *http.Request, authSess } if mfaPromptData == nil { - l.renderError(w, r, authSession, caos_errs.ThrowPreconditionFailed(nil, "APP-XU0tj", "No available mfa providers")) + l.renderError(w, r, authSession, caos_errs.ThrowPreconditionFailed(nil, "APP-XU0tj", "Errors.User.Mfa.NoProviders")) return } @@ -70,7 +70,7 @@ func (l *Login) handleMfaCreation(w http.ResponseWriter, r *http.Request, authSe l.handleOtpCreation(w, r, authSession, data) return } - l.renderError(w, r, authSession, caos_errs.ThrowPreconditionFailed(nil, "APP-Or3HO", "No available mfa providers")) + l.renderError(w, r, authSession, caos_errs.ThrowPreconditionFailed(nil, "APP-Or3HO", "Errors.User.Mfa.NoProviders")) } func (l *Login) handleOtpCreation(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest, data *mfaVerifyData) { diff --git a/internal/login/handler/mfa_verify_handler.go b/internal/login/handler/mfa_verify_handler.go index dbe8008b2e..d692140425 100644 --- a/internal/login/handler/mfa_verify_handler.go +++ b/internal/login/handler/mfa_verify_handler.go @@ -35,7 +35,7 @@ func (l *Login) handleMfaVerify(w http.ResponseWriter, r *http.Request) { func (l *Login) renderMfaVerify(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest, verificationStep *model.MfaVerificationStep, err error) { var errType, errMessage string if err != nil { - errMessage = err.Error() + errMessage = l.getErrorMessage(r, err) } data := userData{ baseData: l.getBaseData(r, authReq, "Mfa Verify", errType, errMessage), diff --git a/internal/login/handler/password_handler.go b/internal/login/handler/password_handler.go index c06e4a5d79..2994f8c219 100644 --- a/internal/login/handler/password_handler.go +++ b/internal/login/handler/password_handler.go @@ -17,7 +17,7 @@ type passwordData struct { func (l *Login) renderPassword(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest, err error) { var errType, errMessage string if err != nil { - errMessage = err.Error() + errMessage = l.getErrorMessage(r, err) } data := userData{ baseData: l.getBaseData(r, authReq, "Password", errType, errMessage), diff --git a/internal/login/handler/password_reset_handler.go b/internal/login/handler/password_reset_handler.go index d9168e6a23..6593ee4988 100644 --- a/internal/login/handler/password_reset_handler.go +++ b/internal/login/handler/password_reset_handler.go @@ -22,7 +22,7 @@ func (l *Login) handlePasswordReset(w http.ResponseWriter, r *http.Request) { func (l *Login) renderPasswordResetDone(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest, err error) { var errType, errMessage string if err != nil { - errMessage = err.Error() + errMessage = l.getErrorMessage(r, err) } data := userData{ baseData: l.getBaseData(r, authReq, "Password Reset Done", errType, errMessage), diff --git a/internal/login/handler/register_handler.go b/internal/login/handler/register_handler.go index b61b6106e8..c7d1aa4b68 100644 --- a/internal/login/handler/register_handler.go +++ b/internal/login/handler/register_handler.go @@ -47,7 +47,7 @@ func (l *Login) handleRegisterCheck(w http.ResponseWriter, r *http.Request) { return } if data.Password != data.Password2 { - err := caos_errs.ThrowInvalidArgument(nil, "VIEW-KaGue", "passwords dont match") + err := caos_errs.ThrowInvalidArgument(nil, "VIEW-KaGue", "Errors.User.Password.ConfirmationWrong") l.renderRegister(w, r, authRequest, data, err) return } @@ -72,7 +72,7 @@ func (l *Login) handleRegisterCheck(w http.ResponseWriter, r *http.Request) { func (l *Login) renderRegister(w http.ResponseWriter, r *http.Request, authRequest *model.AuthRequest, formData *registerFormData, err error) { var errType, errMessage string if err != nil { - errMessage = err.Error() + errMessage = l.getErrorMessage(r, err) } if formData == nil { formData = new(registerFormData) diff --git a/internal/login/handler/renderer.go b/internal/login/handler/renderer.go index c968b11b1a..eafd2514fa 100644 --- a/internal/login/handler/renderer.go +++ b/internal/login/handler/renderer.go @@ -1,9 +1,10 @@ package handler import ( + "errors" "fmt" "github.com/caos/zitadel/internal/auth_request/model" - "github.com/caos/zitadel/internal/errors" + caos_errs "github.com/caos/zitadel/internal/errors" "github.com/caos/zitadel/internal/i18n" "github.com/caos/zitadel/internal/renderer" "net/http" @@ -116,10 +117,10 @@ func CreateRenderer(staticDir http.FileSystem, cookieName string, defaultLanguag func (l *Login) renderNextStep(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest) { authReq, err := l.authRepo.AuthRequestByID(r.Context(), authReq.ID) if err != nil { - l.renderInternalError(w, r, authReq, errors.ThrowInternal(nil, "APP-sio0W", "could not get authreq")) + l.renderInternalError(w, r, authReq, caos_errs.ThrowInternal(nil, "APP-sio0W", "could not get authreq")) } if len(authReq.PossibleSteps) == 0 { - l.renderInternalError(w, r, authReq, errors.ThrowInternal(nil, "APP-9sdp4", "no possible steps")) + l.renderInternalError(w, r, authReq, caos_errs.ThrowInternal(nil, "APP-9sdp4", "no possible steps")) return } l.chooseNextStep(w, r, authReq, 0, nil) @@ -127,7 +128,7 @@ func (l *Login) renderNextStep(w http.ResponseWriter, r *http.Request, authReq * func (l *Login) renderError(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest, err error) { if authReq == nil || len(authReq.PossibleSteps) == 0 { - l.renderInternalError(w, r, authReq, errors.ThrowInternal(err, "APP-OVOiT", "no possible steps")) + l.renderInternalError(w, r, authReq, caos_errs.ThrowInternal(err, "APP-OVOiT", "no possible steps")) return } l.chooseNextStep(w, r, authReq, 0, err) @@ -164,7 +165,7 @@ func (l *Login) chooseNextStep(w http.ResponseWriter, r *http.Request, authReq * case *model.InitUserStep: l.renderInitUser(w, r, authReq, "", "", nil) default: - l.renderInternalError(w, r, authReq, errors.ThrowInternal(nil, "APP-ds3QF", "step no possible")) + l.renderInternalError(w, r, authReq, caos_errs.ThrowInternal(nil, "APP-ds3QF", "step no possible")) } } @@ -191,6 +192,16 @@ func (l *Login) getBaseData(r *http.Request, authReq *model.AuthRequest, title s } } +func (l *Login) getErrorMessage(r *http.Request, err error) (errMsg string) { + caosErr := new(caos_errs.CaosError) + if errors.As(err, &caosErr) { + localized := l.renderer.LocalizeFromRequest(r, caosErr.Message, nil) + return localized + " (" + caosErr.ID + ")" + + } + return err.Error() +} + func (l *Login) getTheme(r *http.Request) string { return "zitadel" //TODO: impl } diff --git a/internal/login/handler/select_user_handler.go b/internal/login/handler/select_user_handler.go index 1676ee5ccf..9b20a1544d 100644 --- a/internal/login/handler/select_user_handler.go +++ b/internal/login/handler/select_user_handler.go @@ -14,9 +14,8 @@ type userSelectionFormData struct { } func (l *Login) renderUserSelection(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest, selectionData *model.SelectUserStep) { - var errType, errMessage string data := userSelectionData{ - baseData: l.getBaseData(r, authReq, "Select User", errType, errMessage), + baseData: l.getBaseData(r, authReq, "Select User", "", ""), Users: selectionData.Users, } l.renderer.RenderTemplate(w, r, l.renderer.Templates[tmplUserSelection], data, nil) diff --git a/internal/login/static/i18n/de.yaml b/internal/login/static/i18n/de.yaml index ae20d7e6a9..26b79607a4 100644 --- a/internal/login/static/i18n/de.yaml +++ b/internal/login/static/i18n/de.yaml @@ -116,4 +116,41 @@ Actions: Register: registrieren ForgotPassword: Password zurücksetzen +Errors: + Internal: Es ist ein interner Fehler aufgetreten + AuthRequest: + NotFound: AuthRequest konnte nicht gefunden werden + User: + NotFound: Benutzer konnte nicht gefunden werden + NotMatchingUserID: User stimm nicht mit User in Auth Request überein + UserIDMissing: UserID ist leer + InvalidData: Userdaten sind ungültig + Password: + ConfirmationWrong: Passwort Bestätigung stimmt nicht überein + Empty: Passwort ist leer + Invalid: Passwort ungültig + PasswordComplexityPolicy: + NotFound: Passwort Policy konnte nicht gefunden werden + MinLength: Passwort ist zu kurz + HasLower: Passwort beinhaltet keinen klein Buchstaben + HasUpper: Passwort beinhaltet keinen gross Buchstaben + HasNumber: Passwort beinhaltet keine Nummer + HasSymbol: Passwort beinhaltet kein Symbol + Code: + Expired: Code ist abgelaufen + Invalid: Code ist ungültig + Empty: Code ist leer + CryptoCodeNil: Crypto Code ist nil + NotFound: Code konnte nicht gefunden werden + GeneratorAlgNotSupported: Generator Algorithums wird nicht unterstützt + EmailVerify: + UserIDEmpty: UserID ist leer + Mfa: + NoProviders: Es stehen keine Multifaktorprovider zur Verfügung + Otp: + AlreadyReady: Multifaktor OTP (OneTimePassword) ist bereits eingerichtet + NotExisting: Multifaktor OTP (OneTimePassword) existiert nicht + InvalidCode: Code ist ungültig + NotReady: Multifaktor OTP (OneTimePassword) ist nicht bereit + optional: (optional) diff --git a/internal/login/static/i18n/en.yaml b/internal/login/static/i18n/en.yaml index 417dae0636..e2e1334c58 100644 --- a/internal/login/static/i18n/en.yaml +++ b/internal/login/static/i18n/en.yaml @@ -116,4 +116,43 @@ Actions: Register: register ForgotPassword: Reset password + +Errors: + Internal: An internal error occured + AuthRequest: + NotFound: Could not find authrequest + User: + NotFound: User could not be found + NotMatchingUserID: User and user in authrequest don't match + UserIDMissing: UserID is empty + InvalidData: Invalid userdata + Password: + ConfirmationWrong: Passwordconfirmation is wrong + Empty: Password is empty + Invalid: Passwort is invalid + PasswordComplexityPolicy: + NotFound: Password policy not found + MinLength: Password is to short + HasLower: Password must contain lower letter + HasUpper: Password must contain upper letter + HasNumber: Password must contain number + HasSymbol: Password must contain symbol + Code: + Expired: Code is expired + Invalid: Code is invalid + Empty: Code is empty + CryptoCodeNil: Crypto code is nil + NotFound: Could not find code + GeneratorAlgNotSupported: Unsupported generator algorithm + EmailVerify: + UserIDEmpty: UserID is empty + Mfa: + NoProviders: No available multifactor providers + Otp: + AlreadyReady: Multifactor OTP (OneTimePassword) is already setup + NotExisting: Multifactor OTP (OneTimePassword) doesn't exist + InvalidCode: Invalid code + NotReady: Multifactor OTP (OneTimePassword) isn't ready + + optional: (optional) diff --git a/internal/policy/model/policy_complexity.go b/internal/policy/model/policy_complexity.go index ed7239af52..2a2a7e8e83 100644 --- a/internal/policy/model/policy_complexity.go +++ b/internal/policy/model/policy_complexity.go @@ -31,23 +31,23 @@ func (p *PasswordComplexityPolicy) IsValid() bool { func (p *PasswordComplexityPolicy) Check(password string) error { if p.MinLength != 0 && uint64(len(password)) < p.MinLength { - return caos_errs.ThrowInvalidArgumentf(nil, "MODEL-HuJf6", "Passwordpolicy doesn't match: Minlength %v", p.MinLength) + return caos_errs.ThrowInvalidArgument(nil, "MODEL-HuJf6", "Errors.User.PasswordComplexityPolicy.MinLength") } if p.HasLowercase && !hasStringLowerCase(password) { - return caos_errs.ThrowInvalidArgument(nil, "MODEL-co3Xw", "Passwordpolicy doesn't match: HasLowerCase") + return caos_errs.ThrowInvalidArgument(nil, "MODEL-co3Xw", "Errors.User.PasswordComplexityPolicy.HasLower") } if p.HasUppercase && !hasStringUpperCase(password) { - return caos_errs.ThrowInvalidArgument(nil, "MODEL-VoaRj", "Passwordpolicy doesn't match: HasUpperCase") + return caos_errs.ThrowInvalidArgument(nil, "MODEL-VoaRj", "Errors.User.PasswordComplexityPolicy.HasUpper") } if p.HasNumber && !hasNumber(password) { - return caos_errs.ThrowInvalidArgument(nil, "MODEL-ZBv4H", "Passwordpolicy doesn't match: HasNumber") + return caos_errs.ThrowInvalidArgument(nil, "MODEL-ZBv4H", "Errors.User.PasswordComplexityPolicy.HasNumber") } if p.HasSymbol && !hasSymbol(password) { - return caos_errs.ThrowInvalidArgument(nil, "MODEL-ZDLwA", "Passwordpolicy doesn't match: HasSymbol") + return caos_errs.ThrowInvalidArgument(nil, "MODEL-ZDLwA", "Errors.User.PasswordComplexityPolicy.HasSymbol") } return nil } diff --git a/internal/user/model/password.go b/internal/user/model/password.go index d9d25963ac..a52fdac5c8 100644 --- a/internal/user/model/password.go +++ b/internal/user/model/password.go @@ -40,7 +40,7 @@ func (p *Password) HashPasswordIfExisting(policy *policy_model.PasswordComplexit return nil } if policy == nil { - return caos_errs.ThrowPreconditionFailed(nil, "MODEL-s8ifS", "Policy should not be nil") + return caos_errs.ThrowPreconditionFailed(nil, "MODEL-s8ifS", "Errors.User.PasswordComplexityPolicy.NotFound") } if err := policy.Check(p.SecretString); err != nil { return err diff --git a/internal/user/repository/eventsourcing/eventstore.go b/internal/user/repository/eventsourcing/eventstore.go index 1a4803f141..cb48f81828 100644 --- a/internal/user/repository/eventsourcing/eventstore.go +++ b/internal/user/repository/eventsourcing/eventstore.go @@ -149,7 +149,7 @@ func (es *UserEventstore) CreateUser(ctx context.Context, user *usr_model.User, func (es *UserEventstore) PrepareRegisterUser(ctx context.Context, user *usr_model.User, policy *policy_model.PasswordComplexityPolicy, resourceOwner string) (*model.User, []*es_models.Aggregate, error) { user.SetEmailAsUsername() if !user.IsValid() || user.Password == nil || user.SecretString == "" { - return nil, nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-9dk45", "user is invalid") + return nil, nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-9dk45", "Errors.User.InvalidData") } id, err := es.idGenerator.Next() if err != nil { @@ -326,8 +326,11 @@ func (es *UserEventstore) InitCodeSent(ctx context.Context, userID string) error } func (es *UserEventstore) VerifyInitCode(ctx context.Context, policy *policy_model.PasswordComplexityPolicy, userID, verificationCode, password string) error { - if userID == "" || verificationCode == "" { - return caos_errs.ThrowPreconditionFailed(nil, "EVENT-lo9fd", "userId or Code empty") + if userID == "" { + return caos_errs.ThrowPreconditionFailed(nil, "EVENT-lo9fd", "Errors.User.UserIDMissing") + } + if verificationCode == "" { + return caos_errs.ThrowPreconditionFailed(nil, "EVENT-lo9fd", "Errors.User.Code.Empty") } pw := &usr_model.Password{SecretString: password} err := pw.HashPasswordIfExisting(policy, es.PasswordAlg, false) @@ -339,13 +342,15 @@ func (es *UserEventstore) VerifyInitCode(ctx context.Context, policy *policy_mod return err } if existing.InitCode == nil { - return caos_errs.ThrowNotFound(nil, "EVENT-spo9W", "code not found") + return caos_errs.ThrowNotFound(nil, "EVENT-spo9W", "Errors.User.Code.NotFound") } repoPassword := model.PasswordFromModel(pw) repoExisting := model.UserFromModel(existing) var updateAggregate func(ctx context.Context) (*es_models.Aggregate, error) if err := crypto.VerifyCode(existing.InitCode.CreationDate, existing.InitCode.Expiry, existing.InitCode.Code, verificationCode, es.InitializeUserCode); err != nil { updateAggregate = InitCodeCheckFailedAggregate(es.AggregateCreator(), repoExisting) + es_sdk.Push(ctx, es.PushAggregates, repoExisting.AppendEvents, updateAggregate) + return err } else { updateAggregate = InitCodeVerifiedAggregate(es.AggregateCreator(), repoExisting, repoPassword) } @@ -360,7 +365,7 @@ func (es *UserEventstore) VerifyInitCode(ctx context.Context, policy *policy_mod func (es *UserEventstore) SkipMfaInit(ctx context.Context, userID string) error { if userID == "" { - return caos_errs.ThrowPreconditionFailed(nil, "EVENT-dic8s", "userID missing") + return caos_errs.ThrowPreconditionFailed(nil, "EVENT-dic8s", "Errors.User.UserIDMissing") } user, err := es.UserByID(ctx, userID) if err != nil { @@ -398,7 +403,7 @@ func (es *UserEventstore) CheckPassword(ctx context.Context, userID, password st return err } if existing.Password == nil { - return caos_errs.ThrowPreconditionFailed(nil, "EVENT-s35Fa", "no password set") + return caos_errs.ThrowPreconditionFailed(nil, "EVENT-s35Fa", "Errors.User.Password.Empty") } if err := crypto.CompareHash(existing.Password.SecretCrypto, []byte(password), es.PasswordAlg); err == nil { return es.setPasswordCheckResult(ctx, existing, authRequest, PasswordCheckSucceededAggregate) @@ -406,7 +411,7 @@ func (es *UserEventstore) CheckPassword(ctx context.Context, userID, password st if err := es.setPasswordCheckResult(ctx, existing, authRequest, PasswordCheckFailedAggregate); err != nil { return err } - return caos_errs.ThrowInvalidArgument(nil, "EVENT-452ad", "invalid password") + return caos_errs.ThrowInvalidArgument(nil, "EVENT-452ad", "Errors.User.Password.Invalid") } func (es *UserEventstore) setPasswordCheckResult(ctx context.Context, user *usr_model.User, authRequest *req_model.AuthRequest, check func(*es_models.AggregateCreator, *model.User, *model.AuthRequest) es_sdk.AggregateFunc) error { @@ -435,10 +440,10 @@ func (es *UserEventstore) SetPassword(ctx context.Context, policy *policy_model. return err } if user.PasswordCode == nil { - return caos_errs.ThrowPreconditionFailed(nil, "EVENT-65sdr", "reset code not found") + return caos_errs.ThrowPreconditionFailed(nil, "EVENT-65sdr", "Errors.User.Code.NotFound") } if err := crypto.VerifyCode(user.PasswordCode.CreationDate, user.PasswordCode.Expiry, user.PasswordCode.Code, code, es.PasswordVerificationCode); err != nil { - return caos_errs.ThrowPreconditionFailed(err, "EVENT-sd6DF", "code invalid") + return err } _, err = es.changedPassword(ctx, user, policy, password, false) return err @@ -450,10 +455,10 @@ func (es *UserEventstore) ChangePassword(ctx context.Context, policy *policy_mod return nil, err } if user.Password == nil { - return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-Fds3s", "user has no password") + return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-Fds3s", "Errors.User.Password.Empty") } if err := crypto.CompareHash(user.Password.SecretCrypto, []byte(old), es.PasswordAlg); err != nil { - return nil, caos_errs.ThrowInvalidArgument(nil, "EVENT-s56a3", "invalid password") + return nil, caos_errs.ThrowInvalidArgument(nil, "EVENT-s56a3", "Errors.User.Password.Invalid") } return es.changedPassword(ctx, user, policy, new, false) } @@ -478,7 +483,7 @@ func (es *UserEventstore) changedPassword(ctx context.Context, user *usr_model.U func (es *UserEventstore) RequestSetPassword(ctx context.Context, userID string, notifyType usr_model.NotificationType) error { if userID == "" { - return caos_errs.ThrowPreconditionFailed(nil, "EVENT-dic8s", "userID missing") + return caos_errs.ThrowPreconditionFailed(nil, "EVENT-dic8s", "Errors.User.UserIDMissing") } user, err := es.UserByID(ctx, userID) if err != nil { @@ -503,7 +508,7 @@ func (es *UserEventstore) RequestSetPassword(ctx context.Context, userID string, func (es *UserEventstore) PasswordCodeSent(ctx context.Context, userID string) error { if userID == "" { - return caos_errs.ThrowPreconditionFailed(nil, "EVENT-s09ow", "userID missing") + return caos_errs.ThrowPreconditionFailed(nil, "EVENT-s09ow", "Errors.User.UserIDMissing") } user, err := es.UserByID(ctx, userID) if err != nil { @@ -603,15 +608,18 @@ func (es *UserEventstore) ChangeEmail(ctx context.Context, email *usr_model.Emai } func (es *UserEventstore) VerifyEmail(ctx context.Context, userID, verificationCode string) error { - if userID == "" || verificationCode == "" { - return caos_errs.ThrowPreconditionFailed(nil, "EVENT-lo9fd", "userId or Code empty") + if userID == "" { + return caos_errs.ThrowPreconditionFailed(nil, "EVENT-lo9fd", "Errors.User.UserIDMissing") + } + if verificationCode == "" { + return caos_errs.ThrowPreconditionFailed(nil, "EVENT-skDws", "Errors.User.Code.Empty") } existing, err := es.UserByID(ctx, userID) if err != nil { return err } if existing.EmailCode == nil { - return caos_errs.ThrowNotFound(nil, "EVENT-lso9w", "code not found") + return caos_errs.ThrowNotFound(nil, "EVENT-lso9w", "Errors.User.Code.NotFound") } err = crypto.VerifyCode(existing.EmailCode.CreationDate, existing.EmailCode.Expiry, existing.EmailCode.Code, verificationCode, es.EmailVerificationCode) @@ -621,7 +629,7 @@ func (es *UserEventstore) VerifyEmail(ctx context.Context, userID, verificationC if err := es.setEmailVerifyResult(ctx, existing, EmailVerificationFailedAggregate); err != nil { return err } - return caos_errs.ThrowInvalidArgument(err, "EVENT-dtGaa", "invalid code") + return caos_errs.ThrowInvalidArgument(err, "EVENT-dtGaa", "Errors.User.Code.Invalid") } func (es *UserEventstore) setEmailVerifyResult(ctx context.Context, existing *usr_model.User, check func(aggCreator *es_models.AggregateCreator, existing *model.User) es_sdk.AggregateFunc) error { @@ -852,7 +860,7 @@ func (es *UserEventstore) AddOTP(ctx context.Context, userID string) (*usr_model return nil, err } if existing.IsOTPReady() { - return nil, caos_errs.ThrowAlreadyExists(nil, "EVENT-do9se", "user has already configured otp") + return nil, caos_errs.ThrowAlreadyExists(nil, "EVENT-do9se", "Errors.User.Mfa.Otp.AlreadyReady") } key, err := totp.Generate(totp.GenerateOpts{Issuer: es.Multifactors.OTP.Issuer, AccountName: userID}) if err != nil { @@ -901,8 +909,11 @@ func (es *UserEventstore) CheckMfaOTPSetup(ctx context.Context, userID, code str if err != nil { return err } - if user.OTP == nil || user.IsOTPReady() { - return caos_errs.ThrowPreconditionFailed(nil, "EVENT-sd5NJ", "otp not existing or already set up") + if user.OTP == nil { + return caos_errs.ThrowPreconditionFailed(nil, "EVENT-sd5NJ", "Errors.Users.Mfa.Otp.NotExisting") + } + if user.IsOTPReady() { + return caos_errs.ThrowPreconditionFailed(nil, "EVENT-sd5NJ", "Errors.Users.Mfa.Otp.AlreadyReady") } if err := es.verifyMfaOTP(user.OTP, code); err != nil { return err @@ -923,18 +934,22 @@ func (es *UserEventstore) CheckMfaOTP(ctx context.Context, userID, code string, return err } if !user.IsOTPReady() { - return caos_errs.ThrowPreconditionFailed(nil, "EVENT-sd5NJ", "opt not ready") + return caos_errs.ThrowPreconditionFailed(nil, "EVENT-sd5NJ", "Errors.User.Mfa.Otp.NotReady") } repoUser := model.UserFromModel(user) repoAuthReq := model.AuthRequestFromModel(authRequest) var aggregate func(*es_models.AggregateCreator, *model.User, *model.AuthRequest) es_sdk.AggregateFunc - if err := es.verifyMfaOTP(user.OTP, code); err != nil { + var checkErr error + if checkErr = es.verifyMfaOTP(user.OTP, code); checkErr != nil { aggregate = MfaOTPCheckFailedAggregate } else { aggregate = MfaOTPCheckSucceededAggregate } err = es_sdk.Push(ctx, es.PushAggregates, repoUser.AppendEvents, aggregate(es.AggregateCreator(), repoUser, repoAuthReq)) + if checkErr != nil { + return checkErr + } if err != nil { return err } @@ -951,7 +966,7 @@ func (es *UserEventstore) verifyMfaOTP(otp *usr_model.OTP, code string) error { valid := es.validateTOTP(code, decrypt) if !valid { - return caos_errs.ThrowInvalidArgument(nil, "EVENT-8isk2", "Invalid code") + return caos_errs.ThrowInvalidArgument(nil, "EVENT-8isk2", "Errors.User.Mfa.Otp.InvalidCode") } return nil } diff --git a/internal/user/repository/eventsourcing/eventstore_test.go b/internal/user/repository/eventsourcing/eventstore_test.go index 7802e4b72e..adf3990b64 100644 --- a/internal/user/repository/eventsourcing/eventstore_test.go +++ b/internal/user/repository/eventsourcing/eventstore_test.go @@ -1487,7 +1487,7 @@ func TestSetPassword(t *testing.T) { password: "password", }, res: res{ - errFunc: caos_errs.IsPreconditionFailed, + errFunc: caos_errs.IsErrorInvalidArgument, }, }, { @@ -1511,7 +1511,7 @@ func TestSetPassword(t *testing.T) { password: "password", }, res: res{ - errFunc: caos_errs.IsPreconditionFailed, + errFunc: caos_errs.IsErrorInvalidArgument, }, }, } @@ -3076,7 +3076,9 @@ func TestCheckMfaOTP(t *testing.T) { }, }, }, - res: res{}, + res: res{ + errFunc: caos_errs.IsErrorInvalidArgument, + }, }, { name: "empty userid", diff --git a/internal/user/repository/view/user_view.go b/internal/user/repository/view/user_view.go index b81fa4b97c..400a48efaa 100644 --- a/internal/user/repository/view/user_view.go +++ b/internal/user/repository/view/user_view.go @@ -12,6 +12,9 @@ func UserByID(db *gorm.DB, table, userID string) (*model.UserView, error) { user := new(model.UserView) query := view.PrepareGetByKey(table, model.UserSearchKey(usr_model.USERSEARCHKEY_USER_ID), userID) err := query(db, user) + if caos_errs.IsNotFound(err) { + return nil, caos_errs.ThrowNotFound(nil, "VIEW-sj8Sw", "Errors.User.NotFound") + } return user, err } @@ -19,6 +22,9 @@ func UserByUserName(db *gorm.DB, table, userName string) (*model.UserView, error user := new(model.UserView) query := view.PrepareGetByKey(table, model.UserSearchKey(usr_model.USERSEARCHKEY_USER_NAME), userName) err := query(db, user) + if caos_errs.IsNotFound(err) { + return nil, caos_errs.ThrowNotFound(nil, "VIEW-Lso9s", "Errors.User.NotFound") + } return user, err } @@ -36,6 +42,9 @@ func GetGlobalUserByEmail(db *gorm.DB, table, email string) (*model.UserView, er user := new(model.UserView) query := view.PrepareGetByKey(table, model.UserSearchKey(usr_model.USERSEARCHKEY_EMAIL), email) err := query(db, user) + if caos_errs.IsNotFound(err) { + return nil, caos_errs.ThrowNotFound(nil, "VIEW-8uWer", "Errors.User.NotFound") + } return user, err } diff --git a/internal/view/requests.go b/internal/view/requests.go index 2a888efd24..3472ab3e03 100644 --- a/internal/view/requests.go +++ b/internal/view/requests.go @@ -23,7 +23,7 @@ func PrepareGetByKey(table string, key ColumnKey, id string) func(db *gorm.DB, r return caos_errs.ThrowNotFound(err, "VIEW-XRI9c", "object not found") } logging.LogWithFields("VIEW-xVShS", "AggregateID", id).WithError(err).Warn("get from view error") - return caos_errs.ThrowInternal(err, "VIEW-J92Td", "view error") + return caos_errs.ThrowInternal(err, "VIEW-J92Td", "Errors.Internal") } }