Added an option to enable a password check against a Banned-Password List (#7315)

* Added an option to enable a password check against a Banned-Password-List

* Update services/frontend/README.md

Co-authored-by: Martin <github@diemattels.at>

Co-authored-by: Edith Parzefall <edith_parzefall@gmx.de>

---------

Co-authored-by: Roman Perekhod <rperekhod@owncloud.com>
Co-authored-by: Martin <github@diemattels.at>
Co-authored-by: Edith Parzefall <edith_parzefall@gmx.de>
This commit is contained in:
Roman Perekhod
2023-09-21 12:31:48 +02:00
committed by GitHub
parent 862ad4d0f2
commit 2e7b423dcc
14 changed files with 142 additions and 58 deletions
@@ -172,32 +172,35 @@ func (s *svc) handlePut(ctx context.Context, w http.ResponseWriter, r *http.Requ
w.WriteHeader(http.StatusInternalServerError)
return
}
if tfRes.Status.Code != rpc.Code_CODE_OK {
if tfRes.Status.Code == rpc.Code_CODE_OK {
sRes, err := client.Stat(ctx, &provider.StatRequest{
Ref: ref,
})
if err != nil {
log.Error().Err(err).Msg("error sending grpc touch file request")
w.WriteHeader(http.StatusInternalServerError)
return
}
if sRes.Status.Code != rpc.Code_CODE_OK {
log.Error().Interface("status", sRes.Status).Msg("error touching file")
errors.HandleErrorStatus(&log, w, sRes.Status)
return
}
w.Header().Set(net.HeaderETag, sRes.Info.Etag)
w.Header().Set(net.HeaderOCETag, sRes.Info.Etag)
w.Header().Set(net.HeaderOCFileID, storagespace.FormatResourceID(*sRes.Info.Id))
w.Header().Set(net.HeaderLastModified, net.RFC1123Z(sRes.Info.Mtime))
w.WriteHeader(http.StatusCreated)
return
}
if tfRes.Status.Code != rpc.Code_CODE_ALREADY_EXISTS {
log.Error().Interface("status", tfRes.Status).Msg("error touching file")
w.WriteHeader(http.StatusInternalServerError)
errors.HandleErrorStatus(&log, w, tfRes.Status)
return
}
sRes, err := client.Stat(ctx, &provider.StatRequest{
Ref: ref,
})
if err != nil {
log.Error().Err(err).Msg("error sending grpc touch file request")
w.WriteHeader(http.StatusInternalServerError)
return
}
if sRes.Status.Code != rpc.Code_CODE_OK {
log.Error().Interface("status", sRes.Status).Msg("error touching file")
w.WriteHeader(http.StatusInternalServerError)
return
}
w.Header().Set(net.HeaderETag, sRes.Info.Etag)
w.Header().Set(net.HeaderOCETag, sRes.Info.Etag)
w.Header().Set(net.HeaderOCFileID, storagespace.FormatResourceID(*sRes.Info.Id))
w.Header().Set(net.HeaderLastModified, net.RFC1123Z(sRes.Info.Mtime))
w.WriteHeader(http.StatusCreated)
return
}
utils.AppendPlainToOpaque(opaque, net.HeaderUploadLength, strconv.FormatInt(length, 10))
@@ -88,12 +88,13 @@ type CapabilitiesGraph struct {
// CapabilitiesPasswordPolicy hold the password policy capabilities
type CapabilitiesPasswordPolicy struct {
MinCharacters int `json:"min_characters" xml:"min_characters" mapstructure:"min_characters"`
MaxCharacters int `json:"max_characters" xml:"max_characters" mapstructure:"max_characters"`
MinLowerCaseCharacters int `json:"min_lowercase_characters" xml:"min_lowercase_characters" mapstructure:"min_lowercase_characters"`
MinUpperCaseCharacters int `json:"min_uppercase_characters" xml:"min_uppercase_characters" mapstructure:"min_uppercase_characters"`
MinDigits int `json:"min_digits" xml:"min_digits" mapstructure:"min_digits"`
MinSpecialCharacters int `json:"min_special_characters" xml:"min_special_characters" mapstructure:"min_special_characters"`
MinCharacters int `json:"min_characters" xml:"min_characters" mapstructure:"min_characters"`
MaxCharacters int `json:"max_characters" xml:"max_characters" mapstructure:"max_characters"`
MinLowerCaseCharacters int `json:"min_lowercase_characters" xml:"min_lowercase_characters" mapstructure:"min_lowercase_characters"`
MinUpperCaseCharacters int `json:"min_uppercase_characters" xml:"min_uppercase_characters" mapstructure:"min_uppercase_characters"`
MinDigits int `json:"min_digits" xml:"min_digits" mapstructure:"min_digits"`
MinSpecialCharacters int `json:"min_special_characters" xml:"min_special_characters" mapstructure:"min_special_characters"`
BannedPasswordsList map[string]struct{} `json:"-" xml:"-" mapstructure:"banned_passwords_list"`
}
// CapabilitiesGraphUsers holds the graph user capabilities
@@ -153,7 +153,7 @@ func (h *Handler) createPublicLinkShare(w http.ResponseWriter, r *http.Request,
if err := h.passwordValidator.Validate(password); err != nil {
return nil, &ocsError{
Code: response.MetaBadRequest.StatusCode,
Message: "password validation failed",
Message: err.Error(),
Error: fmt.Errorf("password validation failed: %w", err),
}
}
@@ -479,7 +479,7 @@ func (h *Handler) updatePublicShare(w http.ResponseWriter, r *http.Request, shar
// skip validation if the clear password scenario
if len(newPassword[0]) > 0 {
if err := h.passwordValidator.Validate(newPassword[0]); err != nil {
response.WriteOCSError(w, r, response.MetaBadRequest.StatusCode, fmt.Errorf("missing required password %w", err).Error(), err)
response.WriteOCSError(w, r, response.MetaBadRequest.StatusCode, err.Error(), err)
return
}
}
@@ -1592,7 +1592,7 @@ func publicPwdEnforced(c *config.Config) passwordEnforced {
func passwordPolicies(c *config.Config) password.Validator {
if c.Capabilities.Capabilities == nil || c.Capabilities.Capabilities.PasswordPolicy == nil {
return password.NewPasswordPolicy(0, 0, 0, 0, 0)
return password.NewPasswordPolicy(0, 0, 0, 0, 0, nil)
}
return password.NewPasswordPolicy(
c.Capabilities.Capabilities.PasswordPolicy.MinCharacters,
@@ -1600,6 +1600,7 @@ func passwordPolicies(c *config.Config) password.Validator {
c.Capabilities.Capabilities.PasswordPolicy.MinUpperCaseCharacters,
c.Capabilities.Capabilities.PasswordPolicy.MinDigits,
c.Capabilities.Capabilities.PasswordPolicy.MinSpecialCharacters,
c.Capabilities.Capabilities.PasswordPolicy.BannedPasswordsList,
)
}
+19 -3
View File
@@ -23,18 +23,20 @@ type Policies struct {
minUpperCaseCharacters int
minDigits int
minSpecialCharacters int
bannedPasswordsList map[string]struct{}
digitsRegexp *regexp.Regexp
specialCharactersRegexp *regexp.Regexp
}
// NewPasswordPolicy returns a new NewPasswordPolicy instance
func NewPasswordPolicy(minCharacters, minLowerCaseCharacters, minUpperCaseCharacters, minDigits, minSpecialCharacters int) Validator {
func NewPasswordPolicy(minCharacters, minLowerCaseCharacters, minUpperCaseCharacters, minDigits, minSpecialCharacters int, bannedPasswordsList map[string]struct{}) Validator {
p := &Policies{
minCharacters: minCharacters,
minLowerCaseCharacters: minLowerCaseCharacters,
minUpperCaseCharacters: minUpperCaseCharacters,
minDigits: minDigits,
minSpecialCharacters: minSpecialCharacters,
bannedPasswordsList: bannedPasswordsList,
}
p.digitsRegexp = regexp.MustCompile("[0-9]")
@@ -48,7 +50,11 @@ func (s Policies) Validate(str string) error {
if !utf8.ValidString(str) {
return fmt.Errorf("the password contains invalid characters")
}
err := s.validateCharacters(str)
err := s.validateBannedList(str)
if err != nil {
return err
}
err = s.validateCharacters(str)
if err != nil {
allErr = errors.Join(allErr, err)
}
@@ -74,6 +80,16 @@ func (s Policies) Validate(str string) error {
return nil
}
func (s Policies) validateBannedList(str string) error {
if len(s.bannedPasswordsList) == 0 {
return nil
}
if _, ok := s.bannedPasswordsList[str]; ok {
return fmt.Errorf("unfortunately, your password is commonly used. please pick a harder-to-guess password for your safety")
}
return nil
}
func (s Policies) validateCharacters(str string) error {
if s.count(str) < s.minCharacters {
return fmt.Errorf("at least %d characters are required", s.minCharacters)
@@ -104,7 +120,7 @@ func (s Policies) validateDigits(str string) error {
func (s Policies) validateSpecialCharacters(str string) error {
if s.countSpecialCharacters(str) < s.minSpecialCharacters {
return fmt.Errorf("at least %d special characters are required. %s", s.minSpecialCharacters, _defaultSpecialCharacters)
return fmt.Errorf("at least %d special characters are required %s", s.minSpecialCharacters, _defaultSpecialCharacters)
}
return nil
}