grafana/pkg/services/user/password.go

81 lines
2.3 KiB
Go

package user
import (
"unicode"
"github.com/grafana/grafana/pkg/apimachinery/errutil"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/util"
)
var (
ErrPasswordTooShort = errutil.BadRequest("password.password-policy-too-short", errutil.WithPublicMessage("New password is too short"))
ErrPasswordPolicyInfringe = errutil.BadRequest("password.password-policy-infringe", errutil.WithPublicMessage("New password doesn't comply with the password policy"))
MinPasswordLength = 12
)
type Password string
func NewPassword(newPassword string, config *setting.Cfg) (Password, error) {
if err := ValidatePassword(newPassword, config); err != nil {
return "", err
}
return Password(newPassword), nil
}
func (p Password) Validate(config *setting.Cfg) error {
return ValidatePassword(string(p), config)
}
func (p Password) Hash(salt string) (Password, error) {
hashed, err := util.EncodePassword(string(p), salt)
if err != nil {
return "", err
}
return Password(hashed), nil
}
// ValidatePassword checks if a new password meets the required criteria based on the given configuration.
// If BasicAuthStrongPasswordPolicy is disabled, it only checks for password length.
// Otherwise, it ensures the password meets the minimum length requirement and contains at least one uppercase letter,
// one lowercase letter, one number, and one symbol.
func ValidatePassword(newPassword string, config *setting.Cfg) error {
if !config.BasicAuthStrongPasswordPolicy {
if len(newPassword) < 4 {
return ErrPasswordTooShort.Errorf("new password is too short")
}
return nil
}
if len(newPassword) < MinPasswordLength {
return ErrPasswordPolicyInfringe.Errorf("new password is too short for the strong password policy")
}
hasUpperCase := false
hasLowerCase := false
hasNumber := false
hasSymbol := false
for _, r := range newPassword {
if !hasLowerCase && unicode.IsLower(r) {
hasLowerCase = true
}
if !hasUpperCase && unicode.IsUpper(r) {
hasUpperCase = true
}
if !hasNumber && unicode.IsNumber(r) {
hasNumber = true
}
if !hasSymbol && !unicode.IsLetter(r) && !unicode.IsNumber(r) {
hasSymbol = true
}
if hasUpperCase && hasLowerCase && hasNumber && hasSymbol {
return nil
}
}
return ErrPasswordPolicyInfringe.Errorf("new password doesn't comply with the password policy")
}