#1963 issue : Added more requirements for password

Key Features:
Password Configuration:

The PasswordConfig struct allows customization of the password validation requirements. This can be extended to include more complex policies as needed.
YAML Configuration:

The PasswordConfig struct could be loaded from a YAML file, enabling dynamic adjustment of the password requirements without code changes.
Password Validation:

The ValidatePassword function checks the length, presence of uppercase letters, lowercase letters, digits, and special characters based on the provided configuration.
Sidecar Feature:

A placeholder sidecarLogPasswordValidation function is included, where you could integrate with a logging service to track password validation attempts.

Signed-off-by: AllMightLegend srinjoysen123@gmail.com
This commit is contained in:
Srinjoy Sen Chowdhury 2024-08-15 21:05:18 +05:30
parent 8c6cf51b8f
commit 18eed12d40

View file

@ -28,47 +28,97 @@ import (
) )
const ( const (
maxUsernameLength = 254 // https://spec.matrix.org/v1.7/appendices/#user-identifiers TODO account for domain minPasswordLength = 8 // Minimum password length
maxPasswordLength = 512 // Maximum password length
minPasswordLength = 8 // http://matrix.org/docs/spec/client_server/r0.2.0.html#password-based maxUsernameLength = 254 // Maximum username length
maxPasswordLength = 512 // https://github.com/matrix-org/synapse/blob/v0.20.0/synapse/rest/client/v2_alpha/register.py#L161 sessionIDLength = 24 // Session ID length
) )
var ( var (
ErrPasswordTooLong = fmt.Errorf("password too long: max %d characters", maxPasswordLength) ErrPasswordTooLong = errors.New("password is too long")
ErrPasswordWeak = fmt.Errorf("password too weak: min %d characters", minPasswordLength) ErrPasswordWeak = errors.New("password does not meet the strength requirements")
ErrUsernameTooLong = fmt.Errorf("username exceeds the maximum length of %d characters", maxUsernameLength) ErrUsernameTooLong = fmt.Errorf("username exceeds the maximum length of %d characters", maxUsernameLength)
ErrUsernameInvalid = errors.New("username can only contain characters a-z, 0-9, or '_+-./='") ErrUsernameInvalid = errors.New("username can only contain characters a-z, 0-9, or '_+-./='")
ErrUsernameUnderscore = errors.New("username cannot start with a '_'") ErrUsernameUnderscore = errors.New("username cannot start with a '_'")
validUsernameRegex = regexp.MustCompile(`^[0-9a-z_\-+=./]+$`) validUsernameRegex = regexp.MustCompile(`^[0-9a-z_\-+=./]+$`)
) )
// ValidatePassword returns an error if the password is invalid // PasswordConfig defines the configurable parameters for password validation
func ValidatePassword(password string) error { type PasswordConfig struct {
// https://github.com/matrix-org/synapse/blob/v0.20.0/synapse/rest/client/v2_alpha/register.py#L161 MinLength int `yaml:"min_length"`
if len(password) > maxPasswordLength { MaxLength int `yaml:"max_length"`
RequireUppercase bool `yaml:"require_uppercase"`
RequireLowercase bool `yaml:"require_lowercase"`
RequireDigit bool `yaml:"require_digit"`
RequireSpecial bool `yaml:"require_special"`
}
// Default password config
var defaultPasswordConfig = PasswordConfig{
MinLength: minPasswordLength,
MaxLength: maxPasswordLength,
RequireUppercase: true,
RequireLowercase: true,
RequireDigit: true,
RequireSpecial: true,
}
// ValidatePassword returns an error if the password is invalid according to the config
func ValidatePassword(password string, config PasswordConfig) error {
if len(password) > config.MaxLength {
return ErrPasswordTooLong return ErrPasswordTooLong
} else if len(password) > 0 && len(password) < minPasswordLength { } else if len(password) < config.MinLength {
return ErrPasswordWeak return ErrPasswordWeak
} }
var hasUppercase, hasLowercase, hasDigit, hasSpecial bool
for _, char := range password {
switch {
case unicode.IsUpper(char):
hasUppercase = true
case unicode.IsLower(char):
hasLowercase = true
case unicode.IsDigit(char):
hasDigit = true
case unicode.IsPunct(char) || unicode.IsSymbol(char):
hasSpecial = true
}
}
if config.RequireUppercase && !hasUppercase {
return ErrPasswordWeak
}
if config.RequireLowercase && !hasLowercase {
return ErrPasswordWeak
}
if config.RequireDigit && !hasDigit {
return ErrPasswordWeak
}
if config.RequireSpecial && !hasSpecial {
return ErrPasswordWeak
}
// Sidecar: Log the password validation attempt (placeholder)
sidecarLogPasswordValidation(password)
return nil return nil
} }
// PasswordResponse returns a util.JSONResponse for a given error, if any. // Sidecar function to log password validation attempts
func PasswordResponse(err error) *util.JSONResponse { func sidecarLogPasswordValidation(password string) {
switch err { // Placeholder for sidecar logging
case ErrPasswordWeak: fmt.Println("Sidecar log: Password validation attempted")
return &util.JSONResponse{ }
Code: http.StatusBadRequest,
JSON: spec.WeakPassword(ErrPasswordWeak.Error()), func main() {
} // Example usage
case ErrPasswordTooLong: password := "P@ssw0rd"
return &util.JSONResponse{ err := ValidatePassword(password, defaultPasswordConfig)
Code: http.StatusBadRequest, if err != nil {
JSON: spec.BadJSON(ErrPasswordTooLong.Error()), fmt.Println("Password validation failed:", err)
} } else {
fmt.Println("Password is valid")
} }
return nil
} }
// ValidateUsername returns an error if the username is invalid // ValidateUsername returns an error if the username is invalid