68 lines
2.1 KiB
Go
68 lines
2.1 KiB
Go
package auth
|
|
|
|
import (
|
|
"crypto/rand"
|
|
"encoding/hex"
|
|
"errors"
|
|
"strconv"
|
|
"strings"
|
|
"unicode"
|
|
)
|
|
|
|
// CheckPassword reports whether a password satisfies the configured policy,
|
|
// returning an error phrased for the end user (the caller can surface it
|
|
// verbatim on a signup or change-password form).
|
|
func (a *Authenticator) CheckPassword(password string) error {
|
|
return CheckPasswordPolicy(a.cfg.Password, password)
|
|
}
|
|
|
|
// CheckPasswordPolicy is CheckPassword against an explicit policy, for callers
|
|
// that validate a password without an Authenticator in hand.
|
|
func CheckPasswordPolicy(policy PasswordPolicy, password string) error {
|
|
if strings.TrimSpace(password) == "" {
|
|
return errors.New("Password cannot be blank.")
|
|
}
|
|
|
|
if len(password) < policy.MinLength {
|
|
return errors.New("Password must be at least " + strconv.Itoa(policy.MinLength) + " characters long.")
|
|
}
|
|
|
|
var uppercase, lowercase, numbers, symbols int
|
|
for _, r := range password {
|
|
switch {
|
|
case unicode.IsUpper(r):
|
|
uppercase++
|
|
case unicode.IsLower(r):
|
|
lowercase++
|
|
case unicode.IsNumber(r):
|
|
numbers++
|
|
default:
|
|
symbols++
|
|
}
|
|
}
|
|
|
|
switch {
|
|
case uppercase < policy.RequiredUppercase:
|
|
return errors.New("Password must contain at least " + strconv.Itoa(policy.RequiredUppercase) + " uppercase character(s).")
|
|
case lowercase < policy.RequiredLowercase:
|
|
return errors.New("Password must contain at least " + strconv.Itoa(policy.RequiredLowercase) + " lowercase character(s).")
|
|
case numbers < policy.RequiredNumbers:
|
|
return errors.New("Password must contain at least " + strconv.Itoa(policy.RequiredNumbers) + " number(s).")
|
|
case symbols < policy.RequiredSymbols:
|
|
return errors.New("Password must contain at least " + strconv.Itoa(policy.RequiredSymbols) + " symbol(s).")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// GeneratePasswordResetToken returns a 32-byte crypto-random token, hex-encoded
|
|
// (64 characters). The app stores it against the user with an expiry and mails
|
|
// the same string out.
|
|
func GeneratePasswordResetToken() (string, error) {
|
|
b := make([]byte, 32)
|
|
if _, err := rand.Read(b); err != nil {
|
|
return "", err
|
|
}
|
|
return hex.EncodeToString(b), nil
|
|
}
|