Compare commits

...

3 Commits

Author SHA1 Message Date
Alex
ef98745732 improved error handling 2025-02-28 11:57:06 +01:00
Alex
e3ebbe596c errors: handling, locale 2025-02-28 11:56:53 +01:00
Alex
658cc9aecd changed privilige handling 2025-02-28 11:56:26 +01:00
7 changed files with 87 additions and 83 deletions

View File

@@ -61,7 +61,9 @@ export default {
'Verdammt, Fehler auf unserer Seite, probieren Sie es nochmal, danach rufen Sie jemanden vom Verein an.',
not_possible: 'Vorgang nicht möglich.',
not_found: 'Konnte nicht gefunden werden.',
in_use: 'Ist in Benutzung'
in_use: 'Ist in Benutzung',
undelivered_verification_mail:
'Registrierung erfolgreicht, leider konnte die Verifizierungs-E-Mail nicht versendet werden. Bitte wenden Sie sich an den Verein um Ihre Emailadresse zu bestätigen und Ihren Account zu aktivieren.'
},
validation: {
invalid_user_id: 'Nutzer ID ungültig',

View File

@@ -16,18 +16,6 @@ const (
MailContactSubject = "Jemand hat das Kontaktformular gefunden"
)
var Roles = struct {
Member int8
Viewer int8
Editor int8
Admin int8
}{
Member: 0,
Viewer: 1,
Editor: 4,
Admin: 8,
}
var Licences = struct {
AM string
A1 string
@@ -78,10 +66,22 @@ var Priviliges = struct {
Update int8
Delete int8
}{
View: 0,
Update: 10,
Create: 20,
Delete: 30,
View: 1,
Update: 4,
Create: 4,
Delete: 4,
}
var Roles = struct {
Member int8
Viewer int8
Editor int8
Admin int8
}{
Member: 0,
Viewer: 1,
Editor: 4,
Admin: 8,
}
var MemberUpdateFields = map[string]bool{

View File

@@ -35,7 +35,7 @@ type RegistrationData struct {
func (uc *UserController) CurrentUserHandler(c *gin.Context) {
requestUser, err := uc.ExtractUserFromContext(c)
if err != nil {
utils.RespondWithError(c, err, "Error extracting user from context in CurrentUserHandler", http.StatusBadRequest, "general", "server.error.internal_server_error")
utils.RespondWithError(c, err, "Error extracting user from context in CurrentUserHandler", http.StatusBadRequest, errors.Responses.Fields.User, errors.Responses.Keys.NoAuthToken)
return
}
@@ -48,17 +48,17 @@ func (uc *UserController) GetAllUsers(c *gin.Context) {
requestUser, err := uc.ExtractUserFromContext(c)
if err != nil {
utils.RespondWithError(c, err, "Error extracting user from context in UpdateHandler", http.StatusBadRequest, "general", "server.validation.no_auth_tokenw")
utils.RespondWithError(c, err, "Error extracting user from context in UpdateHandler", http.StatusBadRequest, errors.Responses.Fields.User, errors.Responses.Keys.NoAuthToken)
return
}
if requestUser.RoleID == constants.Roles.Member {
utils.RespondWithError(c, errors.ErrNotAuthorized, "Not allowed to update user", http.StatusForbidden, "user.user", "server.error.unauthorized")
if utils.HasPrivilige(requestUser, constants.Priviliges.View) {
utils.RespondWithError(c, errors.ErrNotAuthorized, "Not allowed to update user", http.StatusForbidden, errors.Responses.Fields.User, errors.Responses.Keys.Unauthorized)
return
}
users, err := uc.Service.GetUsers(nil)
if err != nil {
utils.RespondWithError(c, err, "Error getting users in GetAllUsers", http.StatusInternalServerError, "user.user", "server.error.internal_server_error")
utils.RespondWithError(c, err, "Error getting users in GetAllUsers", http.StatusInternalServerError, errors.Responses.Fields.User, errors.Responses.Keys.InternalServerError)
return
}
@@ -78,7 +78,7 @@ func (uc *UserController) UpdateHandler(c *gin.Context) {
// 1. Extract and validate the user ID from the route
requestUser, err := uc.ExtractUserFromContext(c)
if err != nil {
utils.RespondWithError(c, err, "Error extracting user from context in UpdateHandler", http.StatusBadRequest, "general", "server.validation.no_auth_tokenw")
utils.RespondWithError(c, err, "Error extracting user from context in UpdateHandler", http.StatusBadRequest, errors.Responses.Fields.User, errors.Responses.Keys.NoAuthToken)
return
}
@@ -91,12 +91,12 @@ func (uc *UserController) UpdateHandler(c *gin.Context) {
user = updateData.User
if !utils.HasPrivilige(requestUser, constants.Priviliges.Update) && user.ID != requestUser.ID {
utils.RespondWithError(c, errors.ErrNotAuthorized, "Not allowed to update user", http.StatusUnauthorized, "user.user", "server.error.unauthorized")
utils.RespondWithError(c, errors.ErrNotAuthorized, "Not allowed to update user", http.StatusForbidden, errors.Responses.Fields.User, errors.Responses.Keys.Unauthorized)
return
}
existingUser, err := uc.Service.GetUserByID(user.ID)
if err != nil {
utils.RespondWithError(c, err, "Error finding an existing user", http.StatusNotFound, "user.user", "server.error.not_found")
utils.RespondWithError(c, err, "Error finding an existing user", http.StatusNotFound, errors.Responses.Fields.User, errors.Responses.Keys.NotFound)
return
}
// user.Membership.ID = existingUser.Membership.ID
@@ -113,10 +113,10 @@ func (uc *UserController) UpdateHandler(c *gin.Context) {
existingUser.Password = ""
if err := utils.FilterAllowedStructFields(&user, existingUser, constants.MemberUpdateFields, ""); err != nil {
if err.Error() == "Not authorized" {
utils.RespondWithError(c, errors.ErrNotAuthorized, "Trying to update unauthorized fields", http.StatusUnauthorized, "user.user", "server.error.unauthorized")
return
utils.RespondWithError(c, errors.ErrNotAuthorized, "Trying to update unauthorized fields", http.StatusUnauthorized, errors.Responses.Fields.User, errors.Responses.Keys.Unauthorized)
} else {
utils.RespondWithError(c, err, "Error filtering users input fields", http.StatusInternalServerError, errors.Responses.Fields.User, errors.Responses.Keys.InternalServerError)
}
utils.RespondWithError(c, err, "Error filtering users input fields", http.StatusInternalServerError, "user.user", "server.error.internal_server_error")
return
}
}
@@ -136,7 +136,7 @@ func (uc *UserController) DeleteUser(c *gin.Context) {
requestUser, err := uc.ExtractUserFromContext(c)
if err != nil {
utils.RespondWithError(c, err, "Error extracting user from context in DeleteUser", http.StatusBadRequest, "general", "server.validation.no_auth_tokenw")
utils.RespondWithError(c, err, "Error extracting user from context in DeleteUser", http.StatusBadRequest, errors.Responses.Fields.User, errors.Responses.Keys.NoAuthToken)
return
}
@@ -154,13 +154,13 @@ func (uc *UserController) DeleteUser(c *gin.Context) {
}
if !utils.HasPrivilige(requestUser, constants.Priviliges.Delete) && data.User.ID != requestUser.ID {
utils.RespondWithError(c, errors.ErrNotAuthorized, "Not allowed to delete user", http.StatusForbidden, "user.user", "server.error.unauthorized")
utils.RespondWithError(c, errors.ErrNotAuthorized, "Not allowed to delete user", http.StatusForbidden, errors.Responses.Fields.User, errors.Responses.Keys.Unauthorized)
return
}
logger.Error.Printf("Deleting user: %v", data.User)
if err := uc.Service.DeleteUser(data.User.LastName, data.User.ID); err != nil {
utils.RespondWithError(c, err, "Error during user deletion", http.StatusInternalServerError, "user.user", "server.error.internal_server_error")
utils.HandleDeleteUserError(c, err)
return
}
@@ -227,7 +227,7 @@ func (uc *UserController) LoginHandler(c *gin.Context) {
ok, err := user.PasswordMatches(input.Password)
if err != nil {
utils.RespondWithError(c, err, "Login Error; password comparisson failed", http.StatusInternalServerError, errors.Responses.Fields.Login, errors.Responses.Keys.InternalServerError)
utils.RespondWithError(c, err, "Login Error; password incorrect", http.StatusInternalServerError, errors.Responses.Fields.Login, errors.Responses.Keys.InternalServerError)
return
}
if !ok {
@@ -265,13 +265,13 @@ func (uc *UserController) RegisterUser(c *gin.Context) {
logger.Info.Printf("Registering user %v", regData.User.Email)
selectedModel, err := uc.MembershipService.GetSubscriptionByName(&regData.User.Membership.SubscriptionModel.Name)
if err != nil {
utils.RespondWithError(c, err, "Error in Registeruser, couldn't get selected model", http.StatusNotFound, "subscription_model", "server.validation.subscription_model_not_found")
utils.RespondWithError(c, err, "Error in Registeruser, couldn't get selected model", http.StatusNotFound, errors.Responses.Fields.SubscriptionModel, errors.Responses.Keys.InvalidSubscriptionModel)
return
}
regData.User.Membership.SubscriptionModel = *selectedModel
if selectedModel.RequiredMembershipField != "" {
if err := validation.CheckParentMembershipID(regData.User.Membership); err != nil {
utils.RespondWithError(c, err, "Error in RegisterUser, couldn't check parent membership id", http.StatusBadRequest, "parent_membership_id", "server.validation.parent_membership_id_not_found")
utils.RespondWithError(c, err, "Error in RegisterUser, couldn't check parent membership id", http.StatusBadRequest, errors.Responses.Fields.ParentMemberShipID, errors.Responses.Keys.NotFound)
return
}
}
@@ -280,11 +280,10 @@ func (uc *UserController) RegisterUser(c *gin.Context) {
// Register User
id, token, err := uc.Service.RegisterUser(&regData.User)
if err != nil {
logger.Error.Printf("Couldn't register User(%v): %v", regData.User.Email, err)
if strings.Contains(err.Error(), "UNIQUE constraint failed: users.email") {
utils.RespondWithError(c, err, "Error in RegisterUser, couldn't register user", http.StatusConflict, "email", "server.validation.email_already_exists")
utils.RespondWithError(c, err, "Error in RegisterUser, couldn't register user", http.StatusConflict, errors.Responses.Fields.Email, errors.Responses.Keys.Duplicate)
} else {
utils.RespondWithError(c, err, "Error in RegisterUser, couldn't register user", http.StatusConflict, "general", "server.error.internal_server_error")
utils.RespondWithError(c, err, "Error in RegisterUser, couldn't register user", http.StatusConflict, errors.Responses.Fields.General, errors.Responses.Keys.InternalServerError)
}
return
}
@@ -308,15 +307,14 @@ func (uc *UserController) RegisterUser(c *gin.Context) {
for _, consent := range consents {
_, err = uc.ConsentService.RegisterConsent(&consent)
if err != nil {
utils.RespondWithError(c, err, "Error in RegisterUser, couldn't register consent", http.StatusInternalServerError, "general", "server.error.internal_server_error")
utils.RespondWithError(c, err, "Error in RegisterUser, couldn't register consent", http.StatusInternalServerError, errors.Responses.Fields.General, errors.Responses.Keys.InternalServerError)
return
}
}
// Send notifications
if err := uc.EmailService.SendVerificationEmail(&regData.User, &token); err != nil {
logger.Error.Printf("Failed to send email verification email to user(%v): %v", regData.User.Email, err)
// Proceed without returning error since user registration is successful
utils.RespondWithError(c, err, "Error in RegisterUser, couldn't send verification email", http.StatusInternalServerError, errors.Responses.Fields.Email, errors.Responses.Keys.UndeliveredVerificationMail)
// TODO Notify Admin
}
@@ -342,13 +340,13 @@ func (uc *UserController) VerifyMailHandler(c *gin.Context) {
verification, err := uc.Service.VerifyUser(&token, &constants.VerificationTypes.Email)
if err != nil {
utils.HandleVerifyUserError(c, err)
c.HTML(http.StatusBadRequest, "verification_error.html", gin.H{"ErrorMessage": "Couldn't verify user"})
return
}
user, err := uc.Service.GetUserByID(verification.UserID)
if err != nil {
utils.RespondWithError(c, err, "Couldn't find user", http.StatusNotFound, errors.Responses.Fields.User, errors.Responses.Keys.UserNotFoundWrongPassword)
c.HTML(http.StatusBadRequest, "verification_error.html", gin.H{"ErrorMessage": "Internal server error, couldn't verify user"})
return
}

View File

@@ -640,7 +640,7 @@ func testUpdateUser(t *testing.T, loginCookie http.Cookie, adminCookie http.Cook
u.Licence.Number = "B072RRE2I50"
u.FirstName = "John Missing ID"
},
expectedStatus: http.StatusUnauthorized,
expectedStatus: http.StatusForbidden,
expectedErrors: []map[string]string{
{"field": "user.user", "key": "server.error.unauthorized"},
},
@@ -692,7 +692,7 @@ func testUpdateUser(t *testing.T, loginCookie http.Cookie, adminCookie http.Cook
expectedErrors: []map[string]string{
{"field": "user.user", "key": "server.error.unauthorized"},
},
expectedStatus: http.StatusUnauthorized,
expectedStatus: http.StatusForbidden,
},
}
for _, tt := range tests {

View File

@@ -1,25 +1,13 @@
package utils
import (
"GoMembership/internal/constants"
"GoMembership/internal/models"
"errors"
"reflect"
)
func HasPrivilige(user *models.User, privilige int8) bool {
switch privilige {
case constants.Priviliges.View:
return user.RoleID >= constants.Roles.Viewer
case constants.Priviliges.Update:
return user.RoleID >= constants.Roles.Editor
case constants.Priviliges.Create:
return user.RoleID >= constants.Roles.Editor
case constants.Priviliges.Delete:
return user.RoleID >= constants.Roles.Editor
default:
return false
}
return user.RoleID >= privilige
}
// FilterAllowedStructFields filters allowed fields recursively in a struct and modifies structToModify in place.

View File

@@ -92,3 +92,17 @@ func HandleVerifyUserError(c *gin.Context, err error) {
RespondWithError(c, err, "Couldn't verify user", http.StatusInternalServerError, errors.Responses.Fields.General, errors.Responses.Keys.InternalServerError)
}
}
func HandleDeleteUserError(c *gin.Context, err error) {
if err.Error() == "record not found" {
RespondWithError(c, err, "Couldn't find user", http.StatusNotFound, errors.Responses.Fields.User, errors.Responses.Keys.NotFound)
}
switch err {
case errors.ErrUserNotFound:
RespondWithError(c, err, "User not found", http.StatusNotFound, errors.Responses.Fields.User, errors.Responses.Keys.NotFound)
case errors.ErrNoData:
RespondWithError(c, err, "No data transmitted", http.StatusNotFound, errors.Responses.Fields.User, errors.Responses.Keys.Invalid)
default:
RespondWithError(c, err, "Couldn't delete user", http.StatusInternalServerError, errors.Responses.Fields.User, errors.Responses.Keys.InternalServerError)
}
}

View File

@@ -3,20 +3,21 @@ package errors
import "errors"
type ValidationKeys struct {
Invalid string
InternalServerError string
InvalidJson string
Unauthorized string
InvalidSubscriptionModel string
UserNotFoundWrongPassword string
JwtGenerationFailed string
Duplicate string
InvalidUserID string
PasswordAlreadyChanged string
UserDisabled string
NoAuthToken string
NotFound string
InUse string
Invalid string
InternalServerError string
InvalidJson string
Unauthorized string
InvalidSubscriptionModel string
UserNotFoundWrongPassword string
JwtGenerationFailed string
Duplicate string
InvalidUserID string
PasswordAlreadyChanged string
UserDisabled string
NoAuthToken string
NotFound string
InUse string
UndeliveredVerificationMail string
}
type ValidationFields struct {
@@ -58,18 +59,19 @@ var Responses = struct {
Fields ValidationFields
}{
Keys: ValidationKeys{
Invalid: "server.validation.invalid",
InternalServerError: "server.error.internal_server_error",
InvalidJson: "server.error.invalid_json",
Unauthorized: "server.error.unauthorized",
UserNotFoundWrongPassword: "server.validation.user_not_found_or_wrong_password",
JwtGenerationFailed: "server.error.jwt_generation_failed",
Duplicate: "server.validation.duplicate",
UserDisabled: "server.validation.user_disabled",
PasswordAlreadyChanged: "server.validation.password_already_changed",
NoAuthToken: "server.error.no_auth_token",
NotFound: "server.error.not_found",
InUse: "server.error.in_use",
Invalid: "server.validation.invalid",
InternalServerError: "server.error.internal_server_error",
InvalidJson: "server.error.invalid_json",
Unauthorized: "server.error.unauthorized",
UserNotFoundWrongPassword: "server.validation.user_not_found_or_wrong_password",
JwtGenerationFailed: "server.error.jwt_generation_failed",
Duplicate: "server.validation.duplicate",
UserDisabled: "server.validation.user_disabled",
PasswordAlreadyChanged: "server.validation.password_already_changed",
NoAuthToken: "server.error.no_auth_token",
NotFound: "server.error.not_found",
InUse: "server.error.in_use",
UndeliveredVerificationMail: "server.error.undelivered_verification_mail",
},
Fields: ValidationFields{
General: "general",