moved to struct validation;

This commit is contained in:
Alex
2024-11-04 17:21:55 +01:00
parent 0fa57bfe75
commit fa10a0a507
15 changed files with 246 additions and 275 deletions

View File

@@ -2,7 +2,7 @@ package controllers
import (
"GoMembership/internal/services"
"GoMembership/pkg/logger"
"GoMembership/internal/utils"
"net/http"
"github.com/gin-gonic/gin"
@@ -17,14 +17,9 @@ func (lc *LicenceController) GetAllCategories(c *gin.Context) {
categories, err := lc.Service.GetAllCategories()
if err != nil {
logger.Error.Printf("Error retrieving licence categories: %v", err)
c.JSON(http.StatusInternalServerError, gin.H{"errors": []gin.H{{
"field": "general",
"key": "validation.internal_server_error",
}}})
utils.RespondWithError(c, err, "Error retrieving licence categories", http.StatusInternalServerError, "general", "server.error.internal_server_error")
return
}
logger.Error.Printf("categories: %v", categories)
c.JSON(http.StatusOK, gin.H{
"licence_categories": categories,
})

View File

@@ -7,12 +7,13 @@ import (
"GoMembership/internal/models"
"GoMembership/internal/services"
"GoMembership/internal/utils"
"GoMembership/internal/validation"
"fmt"
"strings"
"net/http"
"github.com/gin-gonic/gin"
"github.com/go-playground/validator/v10"
"GoMembership/pkg/errors"
"GoMembership/pkg/logger"
@@ -32,49 +33,21 @@ type RegistrationData struct {
}
func (uc *UserController) CurrentUserHandler(c *gin.Context) {
userIDInterface, ok := c.Get("user_id")
if !ok || userIDInterface == nil {
logger.Error.Printf("Error getting user_id from header")
c.JSON(http.StatusInternalServerError, gin.H{"errors": []gin.H{{
"field": "general",
"key": "server.validation.no_user_id_provided",
}}})
return
}
userID, ok := userIDInterface.(uint)
if !ok {
logger.Error.Printf("Error: user_id is not of type uint")
c.JSON(http.StatusInternalServerError, gin.H{"errors": []gin.H{{
"field": "user",
"key": "server.error.internal_server_error",
}}})
return
}
user, err := uc.Service.GetUserByID(uint(userID))
requestUser, err := uc.extractUserFromContext(c)
if err != nil {
logger.Error.Printf("Error retrieving valid user: %v", err)
c.JSON(http.StatusInternalServerError, gin.H{"errors": []gin.H{{
"field": "general",
"key": "server.error.internal_server_error",
}}})
utils.RespondWithError(c, err, "Error extracting user from context in CurrentUserHandler", http.StatusBadRequest, "general", "server.error.internal_server_error")
return
}
c.JSON(http.StatusOK, gin.H{
"user": user.Safe(),
"user": requestUser.Safe(),
})
}
func (uc *UserController) GetAllUsers(c *gin.Context) {
users, err := uc.Service.GetUsers(nil)
if err != nil {
logger.Error.Printf("Error retrieving users: %v", err)
c.JSON(http.StatusInternalServerError, gin.H{"errors": []gin.H{{
"field": "general",
"key": "server.error.internal_server_error",
}}})
utils.RespondWithError(c, err, "Error getting users in GetAllUsers", http.StatusInternalServerError, "user", "server.error.internal_server_error")
return
}
@@ -84,113 +57,59 @@ func (uc *UserController) GetAllUsers(c *gin.Context) {
}
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")
return
}
var user models.User
if err := c.ShouldBindJSON(&user); err != nil {
logger.Error.Printf("Couldn't decode input: %v", err)
var validationErrors []gin.H
if ve, ok := err.(validator.ValidationErrors); ok {
for _, e := range ve {
validationErrors = append(validationErrors, gin.H{
"field": e.Field(),
"key": "server.validation." + e.Tag(),
})
}
} else {
validationErrors = append(validationErrors, gin.H{
"field": "general",
"key": "server.error.invalid_json",
})
}
logger.Error.Printf("ValidationErrors: %#v", validationErrors)
c.JSON(http.StatusBadRequest, gin.H{"errors": validationErrors})
c.Abort()
return
}
logger.Error.Print("Continuing...")
tokenString, err := c.Cookie("jwt")
if err != nil {
logger.Error.Printf("No Auth token: %v\n", err)
c.JSON(http.StatusUnauthorized, gin.H{"errors": []gin.H{{
"field": "general",
"key": "server.error.no_auth_token",
}}})
c.Abort()
return
}
_, claims, err := middlewares.ExtractContentFrom(tokenString)
if err != nil {
logger.Error.Printf("Error retrieving token and claims from JWT")
c.JSON(http.StatusInternalServerError, gin.H{"errors": []gin.H{{
"field": "general",
"key": "server.error.jwt_parsing_error",
}}})
return
}
jwtUserID := uint((*claims)["user_id"].(float64))
userRole := int8((*claims)["role_id"].(float64))
if user.ID == 0 {
logger.Error.Printf("No User.ID in request from user with id: %v, aborting", jwtUserID)
c.JSON(http.StatusBadRequest, gin.H{"errors": []gin.H{{
"field": "id",
"key": "server.validation.no_user_id_provided",
}}})
return
}
if user.ID != jwtUserID && userRole < constants.Roles.Editor {
c.JSON(http.StatusForbidden, gin.H{"errors": []gin.H{{
"field": "general",
"key": "server.error.unauthorized_update",
}}})
utils.HandleValidationError(c, err)
return
}
if !utils.HasPrivilige(requestUser, constants.Priviliges.Update) && user.ID != requestUser.ID {
utils.RespondWithError(c, errors.ErrNotAuthorized, "Not allowed to update user", http.StatusForbidden, "user", "server.error.unauthorized")
return
}
// Validate subscription model
selectedModel, err := uc.MembershipService.GetModelByName(&user.Membership.SubscriptionModel.Name)
if err != nil {
logger.Error.Printf("%v:No subscription model found: %#v", user.Email, err)
c.JSON(http.StatusNotFound, gin.H{"errors": []gin.H{{
"field": "subscription_model",
"key": "server.validation.invalid_subscription_model",
}}})
utils.RespondWithError(c, err, "Error in UpdateHandler", http.StatusNotFound, "subscription_model", "server.validation.subscription_model_not_found")
return
}
user.Membership.SubscriptionModel = *selectedModel
// TODO: If it's not an admin, prevent changes to critical fields
// if userRole != constants.Roles.Admin {
// existingUser, err := uc.Service.GetUserByID(jwtUserID)
// if err != nil {
// c.JSON(http.StatusInternalServerError, gin.H{"error": "Error retrieving user data"})
// return
// }
// user.Email = existingUser.Email
// user.RoleID = existingUser.RoleID
// }
updatedUser, err := uc.Service.UpdateUser(&user, userRole)
updatedUser, err := uc.Service.UpdateUser(&user)
if err != nil {
switch err {
case errors.ErrUserNotFound:
c.JSON(http.StatusNotFound, gin.H{"errors": []gin.H{{
"field": user.FirstName + " " + user.LastName,
"key": "server.validation.user_not_found",
}}})
case errors.ErrInvalidUserData:
c.JSON(http.StatusBadRequest, gin.H{"errors": []gin.H{{
"field": "user",
"key": "server.validation.invalid_user_data",
}}})
default:
logger.Error.Printf("Failed to update user: %v", err)
c.JSON(http.StatusInternalServerError, gin.H{"errors": []gin.H{{
"field": "general",
"key": "server.error.internal_server_error",
}}})
return
}
utils.HandleUpdateError(c, err)
return
}
c.JSON(http.StatusAccepted, gin.H{"message": "User updated successfully", "user": updatedUser})
logger.Info.Printf("User %d updated successfully by user %d", updatedUser.ID, requestUser.ID)
c.JSON(http.StatusAccepted, gin.H{"message": "User updated successfully", "user": updatedUser.Safe()})
}
func (uc *UserController) extractUserFromContext(c *gin.Context) (*models.User, error) {
tokenString, err := c.Cookie("jwt")
if err != nil {
return nil, err
}
_, claims, err := middlewares.ExtractContentFrom(tokenString)
if err != nil {
return nil, err
}
jwtUserID := uint((*claims)["user_id"].(float64))
user, err := uc.Service.GetUserByID(jwtUserID)
if err != nil {
return nil, err
}
return user, nil
}
func (uc *UserController) LogoutHandler(c *gin.Context) {
@@ -212,51 +131,36 @@ func (uc *UserController) LoginHandler(c *gin.Context) {
}
if err := c.ShouldBindJSON(&input); err != nil {
logger.Error.Printf("Couldn't decode input: %v", err.Error())
c.JSON(http.StatusBadRequest, gin.H{"errors": []gin.H{{
"field": "general",
"key": "server.error.invalid_json",
}}})
utils.RespondWithError(c, err, "Error in LoginHandler", http.StatusBadRequest, "general", "server.validation.invalid_json")
return
}
user, err := uc.Service.GetUserByEmail(input.Email)
if err != nil {
logger.Error.Printf("Error during user(%v) retrieval: %v\n", input.Email, err)
c.JSON(http.StatusNotFound, gin.H{"errors": []gin.H{{
"field": "login",
"key": "server.validation.user_not_found_or_wrong_password",
}}})
utils.RespondWithError(c, err, "Login Error; user not found", http.StatusNotFound,
errors.Responses.Fields.Login,
errors.Responses.Keys.UserNotFoundWrongPassword)
return
}
ok, err := user.PasswordMatches(input.Password)
if err != nil {
logger.Error.Printf("Error during Password comparison: %v", err.Error())
c.JSON(http.StatusInternalServerError, gin.H{"errors": []gin.H{{
"field": "general",
"key": "server.error.internal_server_error",
}}})
utils.RespondWithError(c, err, "Login Error; password comparisson failed", http.StatusInternalServerError, errors.Responses.Fields.General, errors.Responses.Keys.InternalServerError)
return
}
if !ok {
logger.Error.Printf("Wrong Password: %v %v", user.FirstName, user.LastName)
c.JSON(http.StatusNotAcceptable, gin.H{"errors": []gin.H{{
"field": "login",
"key": "server.validation.user_not_found_or_wrong_password",
}}})
utils.RespondWithError(c, fmt.Errorf("%v %v", user.FirstName, user.LastName),
"Login Error; wrong password",
http.StatusNotAcceptable,
errors.Responses.Fields.Login,
errors.Responses.Keys.UserNotFoundWrongPassword)
return
}
logger.Error.Printf("jwtsevret: %v", config.Auth.JWTSecret)
logger.Error.Printf("jwtsecret: %v", config.Auth.JWTSecret)
token, err := middlewares.GenerateToken(config.Auth.JWTSecret, user, "")
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"errors": []gin.H{{
"field": "general",
"key": "server.error.jwt_generation_failed",
}}})
utils.RespondWithError(c, err, "Error generating token in LoginHandler", http.StatusInternalServerError, errors.Responses.Fields.General, errors.Responses.Keys.JwtGenerationFailed)
return
}
@@ -272,36 +176,23 @@ func (uc *UserController) RegisterUser(c *gin.Context) {
var regData RegistrationData
if err := c.ShouldBindJSON(&regData); err != nil {
logger.Error.Printf("Couldn't decode Userdata: %v", err)
var validationErrors []gin.H
if ve, ok := err.(validator.ValidationErrors); ok {
for _, e := range ve {
validationErrors = append(validationErrors, gin.H{
"field": e.Field(),
"key": "server.validation." + e.Tag(),
})
}
} else {
validationErrors = append(validationErrors, gin.H{
"field": "general",
"key": "server.error.invalid_json",
})
}
c.JSON(http.StatusBadRequest, gin.H{"error": validationErrors})
utils.HandleValidationError(c, err)
return
}
logger.Info.Printf("Registering user %v", regData.User.Email)
selectedModel, err := uc.MembershipService.GetModelByName(&regData.User.Membership.SubscriptionModel.Name)
if err != nil {
logger.Error.Printf("%v:No subscription model found: %#v", regData.User.Email, err)
c.JSON(http.StatusNotFound, gin.H{"errors": []gin.H{{
"field": "subscription_model",
"key": "server.validation.invalid_subscription_model",
}}})
utils.RespondWithError(c, err, "Error in Registeruser, couldn't get selected model", http.StatusNotFound, "subscription_model", "server.validation.subscription_model_not_found")
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")
return
}
}
regData.User.RoleID = constants.Roles.Member
// Register User
@@ -309,16 +200,9 @@ func (uc *UserController) RegisterUser(c *gin.Context) {
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") {
c.JSON(http.StatusConflict, gin.H{"errors": []gin.H{{
"field": "email",
"key": "server.validation.email_already_registered",
}}})
utils.RespondWithError(c, err, "Error in RegisterUser, couldn't register user", http.StatusConflict, "email", "server.validation.email_already_exists")
} else {
logger.Error.Printf("Failed to register user: %v", err)
c.JSON(http.StatusInternalServerError, gin.H{"errors": []gin.H{{
"field": "general",
"key": "server.error.internal_server_error",
}}})
utils.RespondWithError(c, err, "Error in RegisterUser, couldn't register user", http.StatusConflict, "general", "server.error.internal_server_error")
}
return
}
@@ -342,11 +226,7 @@ func (uc *UserController) RegisterUser(c *gin.Context) {
for _, consent := range consents {
_, err = uc.ConsentService.RegisterConsent(&consent)
if err != nil {
logger.Error.Printf("%v, Couldn't register consent: %v", regData.User.Email, err)
c.JSON(http.StatusInternalServerError, gin.H{"errors": []gin.H{{
"field": "general",
"key": "server.error.internal_server_error",
}}})
utils.RespondWithError(c, err, "Error in RegisterUser, couldn't register consent", http.StatusInternalServerError, "general", "server.error.internal_server_error")
return
}
}
@@ -355,12 +235,14 @@ func (uc *UserController) RegisterUser(c *gin.Context) {
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
// TODO Notify Admin
}
// Notify admin of new user registration
if err := uc.EmailService.SendRegistrationNotification(&regData.User); err != nil {
logger.Error.Printf("Failed to notify admin of new user(%v) registration: %v", regData.User.Email, err)
// Proceed without returning error since user registration is successful
// TODO Notify Admin
}
c.JSON(http.StatusCreated, gin.H{
"message": "Registration successuful",

View File

@@ -221,8 +221,8 @@ func testLoginHandler(t *testing.T) (string, http.Cookie) {
}
assert.NotEmpty(t, loginCookie)
} else {
assert.Contains(t, response, "error")
assert.NotEmpty(t, response["error"])
assert.Contains(t, response, "errors")
assert.NotEmpty(t, response["errors"])
}
})
@@ -585,7 +585,7 @@ func testUpdateUser(t *testing.T, loginCookie http.Cookie) {
},
expectedStatus: http.StatusForbidden,
expectedErrors: []map[string]string{
{"field": "general", "key": "server.error.unauthorized_update"},
{"field": "user", "key": "server.error.unauthorized"},
},
},
{
@@ -623,6 +623,7 @@ func testUpdateUser(t *testing.T, loginCookie http.Cookie) {
t.Run(tt.name, func(t *testing.T) {
// Create a copy of the user and apply the updates
updatedUser := user
logger.Error.Printf("user to be updated: %+v", user.Licence)
tt.updateFunc(&updatedUser)
// Convert user to JSON
jsonData, err := json.Marshal(updatedUser)