moved to struct validation;
This commit is contained in:
@@ -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,
|
||||
})
|
||||
|
||||
@@ -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(®Data); 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(®Data.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(®Data.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(®Data.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",
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user