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.', 'Verdammt, Fehler auf unserer Seite, probieren Sie es nochmal, danach rufen Sie jemanden vom Verein an.',
not_possible: 'Vorgang nicht möglich.', not_possible: 'Vorgang nicht möglich.',
not_found: 'Konnte nicht gefunden werden.', 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: { validation: {
invalid_user_id: 'Nutzer ID ungültig', invalid_user_id: 'Nutzer ID ungültig',

View File

@@ -16,18 +16,6 @@ const (
MailContactSubject = "Jemand hat das Kontaktformular gefunden" 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 { var Licences = struct {
AM string AM string
A1 string A1 string
@@ -78,10 +66,22 @@ var Priviliges = struct {
Update int8 Update int8
Delete int8 Delete int8
}{ }{
View: 0, View: 1,
Update: 10, Update: 4,
Create: 20, Create: 4,
Delete: 30, 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{ var MemberUpdateFields = map[string]bool{

View File

@@ -35,7 +35,7 @@ type RegistrationData struct {
func (uc *UserController) CurrentUserHandler(c *gin.Context) { func (uc *UserController) CurrentUserHandler(c *gin.Context) {
requestUser, err := uc.ExtractUserFromContext(c) requestUser, err := uc.ExtractUserFromContext(c)
if err != nil { 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 return
} }
@@ -48,17 +48,17 @@ func (uc *UserController) GetAllUsers(c *gin.Context) {
requestUser, err := uc.ExtractUserFromContext(c) requestUser, err := uc.ExtractUserFromContext(c)
if err != nil { 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 return
} }
if requestUser.RoleID == constants.Roles.Member { if utils.HasPrivilige(requestUser, constants.Priviliges.View) {
utils.RespondWithError(c, errors.ErrNotAuthorized, "Not allowed to update user", http.StatusForbidden, "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 return
} }
users, err := uc.Service.GetUsers(nil) users, err := uc.Service.GetUsers(nil)
if err != 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 return
} }
@@ -78,7 +78,7 @@ func (uc *UserController) UpdateHandler(c *gin.Context) {
// 1. Extract and validate the user ID from the route // 1. Extract and validate the user ID from the route
requestUser, err := uc.ExtractUserFromContext(c) requestUser, err := uc.ExtractUserFromContext(c)
if err != nil { 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 return
} }
@@ -91,12 +91,12 @@ func (uc *UserController) UpdateHandler(c *gin.Context) {
user = updateData.User user = updateData.User
if !utils.HasPrivilige(requestUser, constants.Priviliges.Update) && user.ID != requestUser.ID { 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 return
} }
existingUser, err := uc.Service.GetUserByID(user.ID) existingUser, err := uc.Service.GetUserByID(user.ID)
if err != nil { 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 return
} }
// user.Membership.ID = existingUser.Membership.ID // user.Membership.ID = existingUser.Membership.ID
@@ -113,10 +113,10 @@ func (uc *UserController) UpdateHandler(c *gin.Context) {
existingUser.Password = "" existingUser.Password = ""
if err := utils.FilterAllowedStructFields(&user, existingUser, constants.MemberUpdateFields, ""); err != nil { if err := utils.FilterAllowedStructFields(&user, existingUser, constants.MemberUpdateFields, ""); err != nil {
if err.Error() == "Not authorized" { if err.Error() == "Not authorized" {
utils.RespondWithError(c, errors.ErrNotAuthorized, "Trying to update unauthorized fields", http.StatusUnauthorized, "user.user", "server.error.unauthorized") utils.RespondWithError(c, errors.ErrNotAuthorized, "Trying to update unauthorized fields", http.StatusUnauthorized, errors.Responses.Fields.User, errors.Responses.Keys.Unauthorized)
return } 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 return
} }
} }
@@ -136,7 +136,7 @@ func (uc *UserController) DeleteUser(c *gin.Context) {
requestUser, err := uc.ExtractUserFromContext(c) requestUser, err := uc.ExtractUserFromContext(c)
if err != nil { 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 return
} }
@@ -154,13 +154,13 @@ func (uc *UserController) DeleteUser(c *gin.Context) {
} }
if !utils.HasPrivilige(requestUser, constants.Priviliges.Delete) && data.User.ID != requestUser.ID { 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 return
} }
logger.Error.Printf("Deleting user: %v", data.User) logger.Error.Printf("Deleting user: %v", data.User)
if err := uc.Service.DeleteUser(data.User.LastName, data.User.ID); err != nil { 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 return
} }
@@ -227,7 +227,7 @@ func (uc *UserController) LoginHandler(c *gin.Context) {
ok, err := user.PasswordMatches(input.Password) ok, err := user.PasswordMatches(input.Password)
if err != nil { 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 return
} }
if !ok { if !ok {
@@ -265,13 +265,13 @@ func (uc *UserController) RegisterUser(c *gin.Context) {
logger.Info.Printf("Registering user %v", regData.User.Email) logger.Info.Printf("Registering user %v", regData.User.Email)
selectedModel, err := uc.MembershipService.GetSubscriptionByName(&regData.User.Membership.SubscriptionModel.Name) selectedModel, err := uc.MembershipService.GetSubscriptionByName(&regData.User.Membership.SubscriptionModel.Name)
if err != nil { 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 return
} }
regData.User.Membership.SubscriptionModel = *selectedModel regData.User.Membership.SubscriptionModel = *selectedModel
if selectedModel.RequiredMembershipField != "" { if selectedModel.RequiredMembershipField != "" {
if err := validation.CheckParentMembershipID(regData.User.Membership); err != nil { 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 return
} }
} }
@@ -280,11 +280,10 @@ func (uc *UserController) RegisterUser(c *gin.Context) {
// Register User // Register User
id, token, err := uc.Service.RegisterUser(&regData.User) id, token, err := uc.Service.RegisterUser(&regData.User)
if err != nil { 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") { 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 { } 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 return
} }
@@ -308,15 +307,14 @@ func (uc *UserController) RegisterUser(c *gin.Context) {
for _, consent := range consents { for _, consent := range consents {
_, err = uc.ConsentService.RegisterConsent(&consent) _, err = uc.ConsentService.RegisterConsent(&consent)
if err != nil { 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 return
} }
} }
// Send notifications // Send notifications
if err := uc.EmailService.SendVerificationEmail(&regData.User, &token); err != nil { 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) utils.RespondWithError(c, err, "Error in RegisterUser, couldn't send verification email", http.StatusInternalServerError, errors.Responses.Fields.Email, errors.Responses.Keys.UndeliveredVerificationMail)
// Proceed without returning error since user registration is successful
// TODO Notify Admin // TODO Notify Admin
} }
@@ -342,13 +340,13 @@ func (uc *UserController) VerifyMailHandler(c *gin.Context) {
verification, err := uc.Service.VerifyUser(&token, &constants.VerificationTypes.Email) verification, err := uc.Service.VerifyUser(&token, &constants.VerificationTypes.Email)
if err != nil { if err != nil {
utils.HandleVerifyUserError(c, err) c.HTML(http.StatusBadRequest, "verification_error.html", gin.H{"ErrorMessage": "Couldn't verify user"})
return return
} }
user, err := uc.Service.GetUserByID(verification.UserID) user, err := uc.Service.GetUserByID(verification.UserID)
if err != nil { 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 return
} }

View File

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

View File

@@ -1,25 +1,13 @@
package utils package utils
import ( import (
"GoMembership/internal/constants"
"GoMembership/internal/models" "GoMembership/internal/models"
"errors" "errors"
"reflect" "reflect"
) )
func HasPrivilige(user *models.User, privilige int8) bool { func HasPrivilige(user *models.User, privilige int8) bool {
switch privilige { return user.RoleID >= 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
}
} }
// FilterAllowedStructFields filters allowed fields recursively in a struct and modifies structToModify in place. // 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) 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

@@ -17,6 +17,7 @@ type ValidationKeys struct {
NoAuthToken string NoAuthToken string
NotFound string NotFound string
InUse string InUse string
UndeliveredVerificationMail string
} }
type ValidationFields struct { type ValidationFields struct {
@@ -70,6 +71,7 @@ var Responses = struct {
NoAuthToken: "server.error.no_auth_token", NoAuthToken: "server.error.no_auth_token",
NotFound: "server.error.not_found", NotFound: "server.error.not_found",
InUse: "server.error.in_use", InUse: "server.error.in_use",
UndeliveredVerificationMail: "server.error.undelivered_verification_mail",
}, },
Fields: ValidationFields{ Fields: ValidationFields{
General: "general", General: "general",