From b2e4947d3788ffd202e890d36a33d111be9b5096 Mon Sep 17 00:00:00 2001 From: Alex <$(pass /github/email)> Date: Wed, 9 Oct 2024 18:12:20 +0200 Subject: [PATCH] add & moved to validations folder; del validator/v10 --- .../subscription_model_repository.go | 3 +- internal/server/server.go | 2 + internal/services/membership_service.go | 27 +-- internal/services/user_service.go | 49 +----- internal/utils/validators.go | 157 ------------------ .../validation/DriversLicence_validation.go | 38 +++++ internal/validation/bankAccount_validation.go | 19 +++ internal/validation/general_validation.go | 34 ++++ internal/validation/membership_validation.go | 30 ++++ internal/validation/setup.go | 23 +++ .../validation/subscription_validation.go | 46 +++++ internal/validation/user_validation.go | 51 ++++++ 12 files changed, 253 insertions(+), 226 deletions(-) delete mode 100644 internal/utils/validators.go create mode 100644 internal/validation/DriversLicence_validation.go create mode 100644 internal/validation/bankAccount_validation.go create mode 100644 internal/validation/general_validation.go create mode 100644 internal/validation/membership_validation.go create mode 100644 internal/validation/setup.go create mode 100644 internal/validation/subscription_validation.go create mode 100644 internal/validation/user_validation.go diff --git a/internal/repositories/subscription_model_repository.go b/internal/repositories/subscription_model_repository.go index 993480b..8616227 100644 --- a/internal/repositories/subscription_model_repository.go +++ b/internal/repositories/subscription_model_repository.go @@ -11,7 +11,6 @@ import ( type SubscriptionModelsRepositoryInterface interface { CreateSubscriptionModel(subscriptionModel *models.SubscriptionModel) (uint, error) GetMembershipModelNames() ([]string, error) - GetModelByName(modelname *string) (*models.SubscriptionModel, error) GetSubscriptions(where map[string]interface{}) (*[]models.SubscriptionModel, error) } @@ -26,7 +25,7 @@ func (sr *SubscriptionModelsRepository) CreateSubscriptionModel(subscriptionMode return subscriptionModel.ID, nil } -func (sr *SubscriptionModelsRepository) GetModelByName(modelname *string) (*models.SubscriptionModel, error) { +func GetModelByName(modelname *string) (*models.SubscriptionModel, error) { var model models.SubscriptionModel if err := database.DB.Where("name = ?", modelname).First(&model).Error; err != nil { return nil, err diff --git a/internal/server/server.go b/internal/server/server.go index c23e2d0..e4e15d5 100644 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -13,6 +13,7 @@ import ( "GoMembership/internal/controllers" "GoMembership/internal/middlewares" "GoMembership/internal/repositories" + "GoMembership/internal/validation" "GoMembership/internal/routes" "GoMembership/internal/services" @@ -63,6 +64,7 @@ func Run() { router.Use(middlewares.RateLimitMiddleware(limiter)) routes.RegisterRoutes(router, userController, membershipController, contactController) + validation.SetupValidators() logger.Info.Println("Starting server on :8080") srv = &http.Server{ diff --git a/internal/services/membership_service.go b/internal/services/membership_service.go index f10d45c..85aa128 100644 --- a/internal/services/membership_service.go +++ b/internal/services/membership_service.go @@ -1,15 +1,10 @@ package services import ( - "slices" "time" - "github.com/go-playground/validator/v10" - "GoMembership/internal/models" "GoMembership/internal/repositories" - "GoMembership/internal/utils" - "GoMembership/pkg/errors" ) type MembershipServiceInterface interface { @@ -37,9 +32,6 @@ func (service *MembershipService) FindMembershipByUserID(userID uint) (*models.M // Membership_Subscriptions func (service *MembershipService) RegisterSubscription(subscription *models.SubscriptionModel) (uint, error) { - if err := validateSubscriptionData(subscription); err != nil { - return 0, err - } return service.SubscriptionRepo.CreateSubscriptionModel(subscription) } @@ -48,15 +40,7 @@ func (service *MembershipService) GetMembershipModelNames() ([]string, error) { } func (service *MembershipService) GetModelByName(modelname *string) (*models.SubscriptionModel, error) { - sModelNames, err := service.SubscriptionRepo.GetMembershipModelNames() - if err != nil { - return nil, err - } - - if !slices.Contains(sModelNames, *modelname) { - return nil, errors.ErrNotFound - } - return service.SubscriptionRepo.GetModelByName(modelname) + return repositories.GetModelByName(modelname) } func (service *MembershipService) GetSubscriptions(where map[string]interface{}) (*[]models.SubscriptionModel, error) { @@ -65,12 +49,3 @@ func (service *MembershipService) GetSubscriptions(where map[string]interface{}) } return service.SubscriptionRepo.GetSubscriptions(where) } - -func validateSubscriptionData(subscription *models.SubscriptionModel) error { - validate := validator.New() - // subscriptionModel and membershipField don't have to be evaluated if adding a new subscription - validate.RegisterValidation("subscriptionModel", func(fl validator.FieldLevel) bool { return true }) - validate.RegisterValidation("membershipField", func(fl validator.FieldLevel) bool { return true }) - validate.RegisterValidation("safe_content", utils.ValidateSafeContent) - return validate.Struct(subscription) -} diff --git a/internal/services/user_service.go b/internal/services/user_service.go index 2ce3b61..d424247 100644 --- a/internal/services/user_service.go +++ b/internal/services/user_service.go @@ -12,7 +12,6 @@ import ( "GoMembership/pkg/logger" "github.com/alexedwards/argon2id" - "github.com/go-playground/validator/v10" "gorm.io/gorm" "time" @@ -34,12 +33,6 @@ type UserService struct { func (service *UserService) UpdateUser(user *models.User, userRole int8) (*models.User, error) { - if err := validateUserData(user, userRole); err != nil { - logger.Info.Printf("UPDATING user: %#v", user) - logger.Error.Printf("Failed to validate user data: %v", err) - return nil, errors.ErrInvalidUserData - } - if user.Password != "" { setPassword(user.Password, user) } @@ -66,9 +59,6 @@ func (service *UserService) UpdateUser(user *models.User, userRole int8) (*model } func (service *UserService) RegisterUser(user *models.User) (uint, string, error) { - if err := validateUserData(user, user.RoleID); err != nil { - return http.StatusNotAcceptable, "", err - } setPassword(user.Password, user) @@ -76,21 +66,19 @@ func (service *UserService) RegisterUser(user *models.User) (uint, string, error user.CreatedAt = time.Now() user.UpdatedAt = time.Now() user.PaymentStatus = constants.AwaitingPaymentStatus - // user.DriversLicence.Status = constants.UnverifiedStatus + user.DriversLicence.Status = constants.UnverifiedStatus user.BankAccount.MandateDateSigned = time.Now() id, err := service.Repo.CreateUser(user) - if err != nil && strings.Contains(err.Error(), "UNIQUE constraint failed") { - return http.StatusConflict, "", err - } else if err != nil { - return http.StatusInternalServerError, "", err + if err != nil { + return 0, "", err } user.ID = id token, err := utils.GenerateVerificationToken() if err != nil { - return http.StatusInternalServerError, "", err + return 0, "", err } logger.Info.Printf("TOKEN: %v", token) @@ -98,10 +86,10 @@ func (service *UserService) RegisterUser(user *models.User) (uint, string, error // Check if user is already verified verified, err := service.Repo.IsVerified(&user.ID) if err != nil { - return http.StatusInternalServerError, "", err + return 0, "", err } if verified { - return http.StatusAlreadyReported, "", errors.ErrAlreadyVerified + return 0, "", errors.ErrAlreadyVerified } // Prepare the Verification record @@ -119,7 +107,7 @@ func (service *UserService) RegisterUser(user *models.User) (uint, string, error func (service *UserService) GetUserByID(id uint) (*models.User, error) { - return service.Repo.GetUserByID(&id) + return repositories.GetUserByID(&id) } func (service *UserService) GetUserByEmail(email string) (*models.User, error) { @@ -140,7 +128,7 @@ func (service *UserService) VerifyUser(token *string) (*models.User, error) { if err != nil { return nil, err } - user, err := service.Repo.GetUserByID(&verification.UserID) + user, err := repositories.GetUserByID(&verification.UserID) if err != nil { return nil, err } @@ -159,27 +147,6 @@ func (service *UserService) VerifyUser(token *string) (*models.User, error) { return user, nil } -func validateUserData(user *models.User, userRole int8) error { - validate := validator.New() - validate.RegisterValidation("safe_content", utils.ValidateSafeContent) - if userRole == constants.Roles.Admin { - validate.RegisterValidation("membershipField", utils.ValidateToTrue) - validate.RegisterValidation("age", utils.ValidateToTrue) - validate.RegisterValidation("bic", utils.ValidateToTrue) - validate.RegisterValidation("subscriptionModel", utils.ValidateToTrue) - validate.RegisterValidation("iban", utils.ValidateToTrue) - validate.RegisterValidation("euDriversLicence", utils.ValidateToTrue) - } else { - validate.RegisterValidation("membershipField", utils.ValidateRequiredMembershipField) - validate.RegisterValidation("age", utils.AgeValidator) - validate.RegisterValidation("bic", utils.BICValidator) - validate.RegisterValidation("subscriptionModel", utils.SubscriptionModelValidator) - validate.RegisterValidation("iban", utils.IBANValidator) - validate.RegisterValidation("euDriversLicence", utils.ValidateDriversLicence) - } - return validate.Struct(user) -} - func setPassword(plaintextPassword string, u *models.User) error { hash, err := argon2id.CreateHash(plaintextPassword, argon2id.DefaultParams) if err != nil { diff --git a/internal/utils/validators.go b/internal/utils/validators.go deleted file mode 100644 index 1c4f5d1..0000000 --- a/internal/utils/validators.go +++ /dev/null @@ -1,157 +0,0 @@ -package utils - -// import "regexp" - -import ( - "GoMembership/internal/database" - "GoMembership/internal/models" - "GoMembership/pkg/logger" - "reflect" - "regexp" - "slices" - "strconv" - "strings" - "time" - - "github.com/go-playground/validator/v10" - "github.com/jbub/banking/iban" - "github.com/jbub/banking/swift" -) - -var xssPatterns = []*regexp.Regexp{ - regexp.MustCompile(`(?i)= 18 -} - -func SubscriptionModelValidator(fl validator.FieldLevel) bool { - fieldValue := fl.Field().String() - var names []string - if err := database.DB.Model(&models.SubscriptionModel{}).Pluck("name", &names).Error; err != nil { - logger.Error.Fatalf("Couldn't get SubscriptionModel names: %#v", err) - return false - } - return slices.Contains(names, fieldValue) -} - -func IBANValidator(fl validator.FieldLevel) bool { - fieldValue := fl.Field().String() - - return iban.Validate(fieldValue) == nil -} - -func ValidateRequiredMembershipField(fl validator.FieldLevel) bool { - user := fl.Top().Interface().(*models.User) - membership := user.Membership - subModel := membership.SubscriptionModel - - // Get the field name specified in RequiredMembershipField - fieldName := subModel.RequiredMembershipField - if fieldName == "" { - return true - } - - // Get the value of the field specified by RequiredMembershipField - fieldValue := reflect.ValueOf(membership).FieldByName(fieldName) - - // Check if the fieldValue is valid - if !fieldValue.IsValid() { - return false - } - - // Check if the fieldValue is a nil pointer - if fieldValue.Kind() == reflect.Ptr && fieldValue.IsNil() { - return false - } - - // Ensure that the fieldValue is an uint - var fieldUint uint - if fieldValue.Kind() == reflect.Uint { - fieldUint = uint(fieldValue.Uint()) - } else { - return false - } - - var membershipIDs []uint - if err := database.DB.Model(&models.Membership{}).Pluck("id", &membershipIDs).Error; err != nil { - logger.Error.Fatalf("Couldn't get SubscriptionModel names: %#v", err) - return false - } - - // Check if the field value is zero (empty) - return slices.Contains(membershipIDs, fieldUint) -} - -func BICValidator(fl validator.FieldLevel) bool { - fieldValue := fl.Field().String() - - return swift.Validate(fieldValue) == nil -} - -func ValidateSafeContent(fl validator.FieldLevel) bool { - input := strings.ToLower(fl.Field().String()) - for _, pattern := range xssPatterns { - if pattern.MatchString(input) { - return false - } - } - return true -} - -func ValidateDriversLicence(fl validator.FieldLevel) bool { - fieldValue := fl.Field().String() - if len(fieldValue) != 11 { - return false - } - - id, tenthChar := string(fieldValue[:9]), string(fieldValue[9]) - - if tenthChar == "X" { - tenthChar = "10" - } - tenthValue, _ := strconv.ParseInt(tenthChar, 10, 8) - - // for readability - weights := []int{9, 8, 7, 6, 5, 4, 3, 2, 1} - sum := 0 - - for i := 0; i < 9; i++ { - char := string(id[i]) - value, _ := strconv.ParseInt(char, 36, 64) - sum += int(value) * weights[i] - } - - calcCheckDigit := sum % 11 - if calcCheckDigit != int(tenthValue) { - return false - } - - return true -} diff --git a/internal/validation/DriversLicence_validation.go b/internal/validation/DriversLicence_validation.go new file mode 100644 index 0000000..518c0c2 --- /dev/null +++ b/internal/validation/DriversLicence_validation.go @@ -0,0 +1,38 @@ +package validation + +import ( + "strconv" + + "github.com/go-playground/validator/v10" +) + +func ValidateDriversLicence(fl validator.FieldLevel) bool { + fieldValue := fl.Field().String() + if len(fieldValue) != 11 { + return false + } + + id, tenthChar := string(fieldValue[:9]), string(fieldValue[9]) + + if tenthChar == "X" { + tenthChar = "10" + } + tenthValue, _ := strconv.ParseInt(tenthChar, 10, 8) + + // for readability + weights := []int{9, 8, 7, 6, 5, 4, 3, 2, 1} + sum := 0 + + for i := 0; i < 9; i++ { + char := string(id[i]) + value, _ := strconv.ParseInt(char, 36, 64) + sum += int(value) * weights[i] + } + + calcCheckDigit := sum % 11 + if calcCheckDigit != int(tenthValue) { + return false + } + + return true +} diff --git a/internal/validation/bankAccount_validation.go b/internal/validation/bankAccount_validation.go new file mode 100644 index 0000000..eb8feb4 --- /dev/null +++ b/internal/validation/bankAccount_validation.go @@ -0,0 +1,19 @@ +package validation + +import ( + "github.com/go-playground/validator/v10" + "github.com/jbub/banking/iban" + "github.com/jbub/banking/swift" +) + +func IBANValidator(fl validator.FieldLevel) bool { + fieldValue := fl.Field().String() + + return iban.Validate(fieldValue) == nil +} + +func BICValidator(fl validator.FieldLevel) bool { + fieldValue := fl.Field().String() + + return swift.Validate(fieldValue) == nil +} diff --git a/internal/validation/general_validation.go b/internal/validation/general_validation.go new file mode 100644 index 0000000..73d607d --- /dev/null +++ b/internal/validation/general_validation.go @@ -0,0 +1,34 @@ +package validation + +import ( + "regexp" + "strings" + + "github.com/go-playground/validator/v10" +) + +var xssPatterns = []*regexp.Regexp{ + regexp.MustCompile(`(?i)