monorepo/cloud/maplepress-backend/pkg/security/password/validator.go

90 lines
2 KiB
Go

package password
import (
"regexp"
"unicode"
)
const (
// MinPasswordLength is the minimum required password length
MinPasswordLength = 8
// MaxPasswordLength is the maximum allowed password length
MaxPasswordLength = 128
)
var (
// Special characters allowed in passwords
specialCharRegex = regexp.MustCompile(`[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]`)
)
// PasswordValidator provides password strength validation
type PasswordValidator interface {
ValidatePasswordStrength(password string) error
}
type passwordValidator struct{}
// NewPasswordValidator creates a new password validator
func NewPasswordValidator() PasswordValidator {
return &passwordValidator{}
}
// ValidatePasswordStrength validates that a password meets strength requirements
// Requirements:
// - At least 8 characters long
// - At most 128 characters long
// - Contains at least one uppercase letter
// - Contains at least one lowercase letter
// - Contains at least one digit
// - Contains at least one special character
//
// CWE-521: Returns granular error messages to help users create strong passwords
func (v *passwordValidator) ValidatePasswordStrength(password string) error {
// Check length first
if len(password) < MinPasswordLength {
return ErrPasswordTooShort
}
if len(password) > MaxPasswordLength {
return ErrPasswordTooLong
}
// Check character type requirements
var (
hasUpper bool
hasLower bool
hasNumber bool
hasSpecial bool
)
for _, char := range password {
switch {
case unicode.IsUpper(char):
hasUpper = true
case unicode.IsLower(char):
hasLower = true
case unicode.IsNumber(char):
hasNumber = true
}
}
// Check for special characters
hasSpecial = specialCharRegex.MatchString(password)
// Return granular error for the first missing requirement
// This provides specific feedback to users about what's missing
if !hasUpper {
return ErrPasswordNoUppercase
}
if !hasLower {
return ErrPasswordNoLowercase
}
if !hasNumber {
return ErrPasswordNoNumber
}
if !hasSpecial {
return ErrPasswordNoSpecialChar
}
return nil
}