90 lines
2 KiB
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
|
|
}
|