mirror of
https://codeberg.org/shroff/phylum.git
synced 2026-05-18 04:08:39 -05:00
[server] Single error message for all unmet password requirements [#31]
This commit is contained in:
@@ -27,6 +27,9 @@ func handlePasswordChangeRoute(c *gin.Context) {
|
||||
if errors.Is(err, auth.ErrCredentialsInvalid) {
|
||||
panic(core.NewError(http.StatusBadRequest, "password_incorrect", "current password is incorrect"))
|
||||
}
|
||||
if e, ok := err.(*auth.PasswordRequirementsError); ok {
|
||||
panic(core.NewError(http.StatusBadRequest, "new_password_invalid", e.Error()))
|
||||
}
|
||||
panic(err)
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type charType int
|
||||
|
||||
const (
|
||||
charTypeOther charType = iota
|
||||
charTypeLower
|
||||
charTypeUpper
|
||||
charTypeNumeric
|
||||
charTypeSymbol
|
||||
)
|
||||
|
||||
var charTypes = map[rune]charType{}
|
||||
|
||||
type PasswordRequirementsError struct {
|
||||
minimumLength int
|
||||
missingRequirements []string
|
||||
}
|
||||
|
||||
func (e PasswordRequirementsError) Error() string {
|
||||
sb := strings.Builder{}
|
||||
sb.WriteString("password must")
|
||||
if e.missingRequirements != nil {
|
||||
sb.WriteString(" have at least " + strings.Join(e.missingRequirements, ", "))
|
||||
|
||||
if e.minimumLength != 0 {
|
||||
sb.WriteString(", and")
|
||||
}
|
||||
}
|
||||
if e.minimumLength != 0 {
|
||||
sb.WriteString(" be at least " + strconv.Itoa(e.minimumLength) + " characters long")
|
||||
}
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
func init() {
|
||||
for _, c := range "abcdefghijklmnopqrstuvwxyz" {
|
||||
charTypes[c] = charTypeLower
|
||||
}
|
||||
for _, c := range "ABCDEFGHIJKLMNOPQRSTUVWXYZ" {
|
||||
charTypes[c] = charTypeUpper
|
||||
}
|
||||
for _, c := range "0123456789" {
|
||||
charTypes[c] = charTypeNumeric
|
||||
}
|
||||
for _, c := range "`~!@#$%^&*()-_=+[]{}\\|;:'\",.<>/?" {
|
||||
charTypes[c] = charTypeSymbol
|
||||
}
|
||||
}
|
||||
|
||||
func checkPasswordStrength(password string) error {
|
||||
var minimumLength int
|
||||
var errors []string
|
||||
if len(password) < passwordRequirements.Length {
|
||||
minimumLength = passwordRequirements.Length
|
||||
}
|
||||
count := map[charType]int{}
|
||||
for _, c := range password {
|
||||
count[charTypes[c]]++
|
||||
}
|
||||
|
||||
if count[charTypeLower] < passwordRequirements.Lower {
|
||||
errors = append(errors, strconv.Itoa(passwordRequirements.Lower)+" lower case characters")
|
||||
}
|
||||
if count[charTypeUpper] < passwordRequirements.Upper {
|
||||
errors = append(errors, strconv.Itoa(passwordRequirements.Upper)+" upper case characters")
|
||||
}
|
||||
if count[charTypeNumeric] < passwordRequirements.Numeric {
|
||||
errors = append(errors, strconv.Itoa(passwordRequirements.Numeric)+" numbers")
|
||||
}
|
||||
if count[charTypeSymbol] < passwordRequirements.Symbols {
|
||||
errors = append(errors, strconv.Itoa(passwordRequirements.Symbols)+" symbols")
|
||||
}
|
||||
if minimumLength != 0 || errors != nil {
|
||||
return &PasswordRequirementsError{
|
||||
minimumLength: minimumLength,
|
||||
missingRequirements: errors,
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type charType int
|
||||
|
||||
const (
|
||||
charTypeOther charType = iota
|
||||
charTypeLower
|
||||
charTypeUpper
|
||||
charTypeNumeric
|
||||
charTypeSymbol
|
||||
)
|
||||
|
||||
var charTypes = map[rune]charType{}
|
||||
|
||||
type PasswordStrengthError struct {
|
||||
Reason string
|
||||
}
|
||||
|
||||
func (e PasswordStrengthError) Error() string {
|
||||
return e.Reason
|
||||
}
|
||||
|
||||
func init() {
|
||||
for _, c := range "abcdefghijklmnopqrstuvwxyz" {
|
||||
charTypes[c] = charTypeLower
|
||||
}
|
||||
for _, c := range "ABCDEFGHIJKLMNOPQRSTUVWXYZ" {
|
||||
charTypes[c] = charTypeUpper
|
||||
}
|
||||
for _, c := range "0123456789" {
|
||||
charTypes[c] = charTypeNumeric
|
||||
}
|
||||
for _, c := range "`~!@#$%^&*()-_=+[]{}\\|;:'\",.<>/?" {
|
||||
charTypes[c] = charTypeSymbol
|
||||
}
|
||||
}
|
||||
|
||||
func checkPasswordStrength(password string) error {
|
||||
if len(password) < passwordRequirements.Length {
|
||||
return &PasswordStrengthError{Reason: "password must be at least " + strconv.Itoa(passwordRequirements.Length) + " characters long."}
|
||||
}
|
||||
count := map[charType]int{}
|
||||
for _, c := range password {
|
||||
count[charTypes[c]]++
|
||||
}
|
||||
|
||||
if count[charTypeLower] < passwordRequirements.Lower {
|
||||
return &PasswordStrengthError{Reason: "password must have at least " + strconv.Itoa(passwordRequirements.Lower) + " lower case."}
|
||||
}
|
||||
if count[charTypeUpper] < passwordRequirements.Upper {
|
||||
return &PasswordStrengthError{Reason: "password must have at least " + strconv.Itoa(passwordRequirements.Upper) + " upper case."}
|
||||
}
|
||||
if count[charTypeNumeric] < passwordRequirements.Numeric {
|
||||
return &PasswordStrengthError{Reason: "password must have at least " + strconv.Itoa(passwordRequirements.Numeric) + " numeric."}
|
||||
}
|
||||
if count[charTypeSymbol] < passwordRequirements.Symbols {
|
||||
return &PasswordStrengthError{Reason: "password must have at least " + strconv.Itoa(passwordRequirements.Symbols) + " symbols."}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user