Skip to content

Commit

Permalink
add ability to validation against the Banned-Passwords List
Browse files Browse the repository at this point in the history
  • Loading branch information
2403905 committed Sep 20, 2023
1 parent e1f68d0 commit 54564e3
Show file tree
Hide file tree
Showing 6 changed files with 61 additions and 14 deletions.
5 changes: 5 additions & 0 deletions changelog/unreleased/add-passwod-banned-password-list.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Enhancement: Add the Banned-Passwords List

Add ability to validation against the Banned-Passwords List OCIS-3809

https://github.com/cs3org/reva/pull/4197
13 changes: 7 additions & 6 deletions internal/http/services/owncloud/ocs/data/capabilities.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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),
}
}
Expand Down Expand Up @@ -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
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1592,14 +1592,15 @@ 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,
c.Capabilities.Capabilities.PasswordPolicy.MinLowerCaseCharacters,
c.Capabilities.Capabilities.PasswordPolicy.MinUpperCaseCharacters,
c.Capabilities.Capabilities.PasswordPolicy.MinDigits,
c.Capabilities.Capabilities.PasswordPolicy.MinSpecialCharacters,
c.Capabilities.Capabilities.PasswordPolicy.BannedPasswordsList,
)
}

Expand Down
22 changes: 19 additions & 3 deletions pkg/password/password_policies.go
Original file line number Diff line number Diff line change
Expand Up @@ -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]")
Expand All @@ -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)
}
Expand All @@ -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)
Expand Down Expand Up @@ -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
}
Expand Down
28 changes: 26 additions & 2 deletions pkg/password/password_policies_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package password

import (
"testing"

"github.com/stretchr/testify/assert"
)

func TestPolicies_Validate(t *testing.T) {
Expand All @@ -11,12 +13,14 @@ func TestPolicies_Validate(t *testing.T) {
minUpperCaseCharacters int
minDigits int
minSpecialCharacters int
bannedPasswordsList map[string]struct{}
}
tests := []struct {
name string
fields fields
args string
wantErr bool
errMsg string
}{
{
name: "all in one",
Expand Down Expand Up @@ -51,6 +55,21 @@ func TestPolicies_Validate(t *testing.T) {
},
args: "0äÖ-",
wantErr: true,
errMsg: "at least 2 lowercase letters are required\nat least 2 uppercase letters are required\nat least 2 numbers are required\nat least 2 special characters are required !\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~",
},
{
name: "banned list",
fields: fields{
minCharacters: 10,
minLowerCaseCharacters: 3,
minUpperCaseCharacters: 3,
minDigits: 3,
minSpecialCharacters: 1,
bannedPasswordsList: map[string]struct{}{"123abcABC!": struct{}{}},
},
args: "123abcABC!",
wantErr: true,
errMsg: "unfortunately, your password is commonly used. please pick a harder-to-guess password for your safety",
},
}
for _, tt := range tests {
Expand All @@ -61,9 +80,13 @@ func TestPolicies_Validate(t *testing.T) {
tt.fields.minUpperCaseCharacters,
tt.fields.minDigits,
tt.fields.minSpecialCharacters,
tt.fields.bannedPasswordsList,
)
if err := s.Validate(tt.args); (err != nil) != tt.wantErr {
t.Errorf("Validate() error = %v, wantErr %v", err.Error(), tt.wantErr)
err := s.Validate(tt.args)
if tt.wantErr {
assert.EqualError(t, err, tt.errMsg)
} else {
assert.NoError(t, err)
}
})
}
Expand Down Expand Up @@ -125,6 +148,7 @@ func TestPasswordPolicies_Count(t *testing.T) {
tt.fields.wantUpperCaseCharacters,
tt.fields.wantDigits,
tt.fields.wantSpecialCharacters,
nil,
)
s := i.(*Policies)
if got := s.count(tt.args); got != tt.fields.wantCharacters {
Expand Down

0 comments on commit 54564e3

Please sign in to comment.