2022-04-03 00:48:26 +00:00
|
|
|
package middlewares
|
|
|
|
|
|
|
|
import (
|
|
|
|
"regexp"
|
|
|
|
|
2022-04-15 09:30:51 +00:00
|
|
|
"github.com/trustelem/zxcvbn"
|
|
|
|
|
2022-04-03 00:48:26 +00:00
|
|
|
"github.com/authelia/authelia/v4/internal/configuration/schema"
|
|
|
|
)
|
|
|
|
|
2022-04-15 09:30:51 +00:00
|
|
|
// PasswordPolicyProvider represents an implementation of a password policy provider.
|
|
|
|
type PasswordPolicyProvider interface {
|
|
|
|
Check(password string) (err error)
|
|
|
|
}
|
|
|
|
|
2022-04-03 00:48:26 +00:00
|
|
|
// NewPasswordPolicyProvider returns a new password policy provider.
|
|
|
|
func NewPasswordPolicyProvider(config schema.PasswordPolicyConfiguration) (provider PasswordPolicyProvider) {
|
2022-04-15 09:30:51 +00:00
|
|
|
if !config.Standard.Enabled && !config.ZXCVBN.Enabled {
|
|
|
|
return &StandardPasswordPolicyProvider{}
|
2022-04-03 00:48:26 +00:00
|
|
|
}
|
|
|
|
|
2022-04-15 09:30:51 +00:00
|
|
|
if config.Standard.Enabled {
|
|
|
|
p := &StandardPasswordPolicyProvider{}
|
2022-04-03 00:48:26 +00:00
|
|
|
|
2022-04-15 09:30:51 +00:00
|
|
|
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]+`))
|
|
|
|
}
|
2022-04-03 00:48:26 +00:00
|
|
|
|
2022-04-15 09:30:51 +00:00
|
|
|
if config.Standard.RequireSpecial {
|
|
|
|
p.patterns = append(p.patterns, *regexp.MustCompile(`[^a-zA-Z0-9]+`))
|
|
|
|
}
|
|
|
|
|
|
|
|
return p
|
2022-04-03 00:48:26 +00:00
|
|
|
}
|
|
|
|
|
2022-04-15 09:30:51 +00:00
|
|
|
if config.ZXCVBN.Enabled {
|
|
|
|
return &ZXCVBNPasswordPolicyProvider{minScore: config.ZXCVBN.MinScore}
|
2022-04-03 00:48:26 +00:00
|
|
|
}
|
|
|
|
|
2022-04-15 09:30:51 +00:00
|
|
|
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
|
2022-04-03 00:48:26 +00:00
|
|
|
}
|
|
|
|
|
2022-04-15 09:30:51 +00:00
|
|
|
return nil
|
2022-04-03 00:48:26 +00:00
|
|
|
}
|
|
|
|
|
2022-04-15 09:30:51 +00:00
|
|
|
// StandardPasswordPolicyProvider handles standard password policy checking.
|
|
|
|
type StandardPasswordPolicyProvider struct {
|
2022-04-03 00:48:26 +00:00
|
|
|
patterns []regexp.Regexp
|
|
|
|
min, max int
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check checks the password against the policy.
|
2022-04-15 09:30:51 +00:00
|
|
|
func (p StandardPasswordPolicyProvider) Check(password string) (err error) {
|
2022-04-03 00:48:26 +00:00
|
|
|
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
|
|
|
|
}
|