package utils import ( "errors" "strings" "unicode/utf8" ) const PasswordSpecialCharacters = "!@#$%^&*()_+-=[]{}|;:,.<>?" var ( ErrPasswordPolicy = errors.New("password must be 3-32 characters and no more than 72 bytes") ErrComplexPasswordPolicy = errors.New( "password must be 8-32 characters and no more than 72 bytes, and contain uppercase and lowercase " + "letters, numbers, and special characters") ) // ValidatePasswordPolicy is shared by account registration, rotation and // workspace user management. Keep the byte limit in addition to the Unicode // character limit because bcrypt rejects inputs longer than 72 bytes. func ValidatePasswordPolicy(password string, complexPasswordEnabled bool) error { length := utf8.RuneCountInString(password) if !utf8.ValidString(password) || length < 3 || length > 32 || len(password) > 72 { return ErrPasswordPolicy } if !complexPasswordEnabled { return nil } if length < 8 { return ErrComplexPasswordPolicy } var hasUpper, hasLower, hasDigit, hasSpecial bool for _, r := range password { switch { case r >= 'A' && r <= 'Z': hasUpper = true case r >= 'a' && r <= 'z': hasLower = true case r >= '0' && r <= '9': hasDigit = true case strings.ContainsRune(PasswordSpecialCharacters, r): hasSpecial = true } } if !hasUpper || !hasLower || !hasDigit || !hasSpecial { return ErrComplexPasswordPolicy } return nil }