authelia/internal/middlewares/password_policy.go
James Elliott 92aba8eb0b
feat(server): zxcvbn password policy server side (#3151)
This is so the zxcvbn ppolicy is checked on the server.
2022-04-15 19:30:51 +10:00

95 lines
2.3 KiB
Go

package middlewares
import (
"regexp"
"github.com/trustelem/zxcvbn"
"github.com/authelia/authelia/v4/internal/configuration/schema"
)
// PasswordPolicyProvider represents an implementation of a password policy provider.
type PasswordPolicyProvider interface {
Check(password string) (err error)
}
// NewPasswordPolicyProvider returns a new password policy provider.
func NewPasswordPolicyProvider(config schema.PasswordPolicyConfiguration) (provider PasswordPolicyProvider) {
if !config.Standard.Enabled && !config.ZXCVBN.Enabled {
return &StandardPasswordPolicyProvider{}
}
if config.Standard.Enabled {
p := &StandardPasswordPolicyProvider{}
p.min, p.max = config.Standard.MinLength, config.Standard.MaxLength
if config.Standard.RequireLowercase {
p.patterns = append(p.patterns, *regexp.MustCompile(`[a-z]+`))
}
if config.Standard.RequireUppercase {
p.patterns = append(p.patterns, *regexp.MustCompile(`[A-Z]+`))
}
if config.Standard.RequireNumber {
p.patterns = append(p.patterns, *regexp.MustCompile(`[0-9]+`))
}
if config.Standard.RequireSpecial {
p.patterns = append(p.patterns, *regexp.MustCompile(`[^a-zA-Z0-9]+`))
}
return p
}
if config.ZXCVBN.Enabled {
return &ZXCVBNPasswordPolicyProvider{minScore: config.ZXCVBN.MinScore}
}
return &StandardPasswordPolicyProvider{}
}
// ZXCVBNPasswordPolicyProvider handles zxcvbn password policy checking.
type ZXCVBNPasswordPolicyProvider struct {
minScore int
}
// Check checks the password against the policy.
func (p ZXCVBNPasswordPolicyProvider) Check(password string) (err error) {
result := zxcvbn.PasswordStrength(password, nil)
if result.Score < p.minScore {
return errPasswordPolicyNoMet
}
return nil
}
// StandardPasswordPolicyProvider handles standard password policy checking.
type StandardPasswordPolicyProvider struct {
patterns []regexp.Regexp
min, max int
}
// Check checks the password against the policy.
func (p StandardPasswordPolicyProvider) Check(password string) (err error) {
patterns := len(p.patterns)
if (p.min > 0 && len(password) < p.min) || (p.max > 0 && len(password) > p.max) {
return errPasswordPolicyNoMet
}
if patterns == 0 {
return nil
}
for i := 0; i < patterns; i++ {
if !p.patterns[i].MatchString(password) {
return errPasswordPolicyNoMet
}
}
return nil
}