chatdesk-ui/auth_v2.169.0/internal/api/password.go

74 lines
2.2 KiB
Go

package api
import (
"context"
"fmt"
"strings"
"github.com/sirupsen/logrus"
)
// BCrypt hashed passwords have a 72 character limit
const MaxPasswordLength = 72
// WeakPasswordError encodes an error that a password does not meet strength
// requirements. It is handled specially in errors.go as it gets transformed to
// a HTTPError with a special weak_password field that encodes the Reasons
// slice.
type WeakPasswordError struct {
Message string `json:"message,omitempty"`
Reasons []string `json:"reasons,omitempty"`
}
func (e *WeakPasswordError) Error() string {
return e.Message
}
func (a *API) checkPasswordStrength(ctx context.Context, password string) error {
config := a.config
if len(password) > MaxPasswordLength {
return badRequestError(ErrorCodeValidationFailed, fmt.Sprintf("Password cannot be longer than %v characters", MaxPasswordLength))
}
var messages, reasons []string
if len(password) < config.Password.MinLength {
reasons = append(reasons, "length")
messages = append(messages, fmt.Sprintf("Password should be at least %d characters.", config.Password.MinLength))
}
for _, characterSet := range config.Password.RequiredCharacters {
if characterSet != "" && !strings.ContainsAny(password, characterSet) {
reasons = append(reasons, "characters")
messages = append(messages, fmt.Sprintf("Password should contain at least one character of each: %s.", strings.Join(config.Password.RequiredCharacters, ", ")))
break
}
}
if config.Password.HIBP.Enabled {
pwned, err := a.hibpClient.Check(ctx, password)
if err != nil {
if config.Password.HIBP.FailClosed {
return internalServerError("Unable to perform password strength check with HaveIBeenPwned.org.").WithInternalError(err)
} else {
logrus.WithError(err).Warn("Unable to perform password strength check with HaveIBeenPwned.org, pwned passwords are being allowed")
}
} else if pwned {
reasons = append(reasons, "pwned")
messages = append(messages, "Password is known to be weak and easy to guess, please choose a different one.")
}
}
if len(reasons) > 0 {
return &WeakPasswordError{
Message: strings.Join(messages, " "),
Reasons: reasons,
}
}
return nil
}