mirror of
https://github.com/matrix-org/dendrite.git
synced 2025-12-29 01:33:10 -06:00
Pull Request Comment Resolutions:
* Change checking symbols / upper / lower from regex to using the `unicode` package to be more non-English-language-friendly. * Name fields in validatePassword test to make it easier to read * Adding a test for the defaults for PasswordRequirements Signed-off-by: Devon Mizelle <dev@devon.so>
This commit is contained in:
parent
6cfb0a36d7
commit
e1c7ad2675
|
|
@ -28,6 +28,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
"unicode"
|
||||||
|
|
||||||
"github.com/matrix-org/dendrite/internal/eventutil"
|
"github.com/matrix-org/dendrite/internal/eventutil"
|
||||||
"github.com/matrix-org/dendrite/setup/config"
|
"github.com/matrix-org/dendrite/setup/config"
|
||||||
|
|
@ -109,10 +110,6 @@ var (
|
||||||
// sessions stores the completed flow stages for all sessions. Referenced using their sessionID.
|
// sessions stores the completed flow stages for all sessions. Referenced using their sessionID.
|
||||||
sessions = newSessionsDict()
|
sessions = newSessionsDict()
|
||||||
validUsernameRegex = regexp.MustCompile(`^[0-9a-z_\-=./]+$`)
|
validUsernameRegex = regexp.MustCompile(`^[0-9a-z_\-=./]+$`)
|
||||||
|
|
||||||
passwordSymbols = regexp.MustCompile(`[^0-9a-zA-Z]`)
|
|
||||||
passwordUppercase = regexp.MustCompile(`[A-Z]`)
|
|
||||||
passwordLowercase = regexp.MustCompile(`[a-z]`)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// registerRequest represents the submitted registration request.
|
// registerRequest represents the submitted registration request.
|
||||||
|
|
@ -229,38 +226,49 @@ func validateApplicationServiceUsername(username string) *util.JSONResponse {
|
||||||
// validatePassword returns an error response if the password is invalid
|
// validatePassword returns an error response if the password is invalid
|
||||||
func validatePassword(password string, cfg config.PasswordRequirements) *util.JSONResponse {
|
func validatePassword(password string, cfg config.PasswordRequirements) *util.JSONResponse {
|
||||||
// https://github.com/matrix-org/synapse/blob/v0.20.0/synapse/rest/client/v2_alpha/register.py#L161
|
// https://github.com/matrix-org/synapse/blob/v0.20.0/synapse/rest/client/v2_alpha/register.py#L161
|
||||||
|
|
||||||
if len(password) > cfg.MaxPasswordLength {
|
if len(password) > cfg.MaxPasswordLength {
|
||||||
return &util.JSONResponse{
|
return &util.JSONResponse{
|
||||||
Code: http.StatusBadRequest,
|
Code: http.StatusBadRequest,
|
||||||
JSON: jsonerror.WeakPassword(fmt.Sprintf("'password' >%d characters", cfg.MaxPasswordLength)),
|
JSON: jsonerror.WeakPassword(fmt.Sprintf("'password' >%d characters", cfg.MaxPasswordLength)),
|
||||||
}
|
}
|
||||||
} else if len(password) > 0 && len(password) < cfg.MinPasswordLength {
|
} else if len(password) >= 0 && len(password) < cfg.MinPasswordLength {
|
||||||
return &util.JSONResponse{
|
return &util.JSONResponse{
|
||||||
Code: http.StatusBadRequest,
|
Code: http.StatusBadRequest,
|
||||||
JSON: jsonerror.WeakPassword(fmt.Sprintf("password too weak: min %d chars", cfg.MinPasswordLength)),
|
JSON: jsonerror.WeakPassword(fmt.Sprintf("password too weak: min %d chars", cfg.MinPasswordLength)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if cfg.MinNumberSymbols > 0 {
|
var (
|
||||||
matches := passwordSymbols.FindAllStringIndex(password, -1)
|
symbolCount = 0
|
||||||
if len(matches) < cfg.MinNumberSymbols {
|
uppercaseCount = 0
|
||||||
|
lowercaseCount = 0
|
||||||
|
)
|
||||||
|
|
||||||
|
for _, letter := range password {
|
||||||
|
switch {
|
||||||
|
case unicode.IsSymbol(letter) || unicode.IsPunct(letter):
|
||||||
|
symbolCount++
|
||||||
|
case unicode.IsUpper(letter):
|
||||||
|
uppercaseCount++
|
||||||
|
case unicode.IsLower(letter):
|
||||||
|
lowercaseCount++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if cfg.MinNumberSymbols > 0 && symbolCount < cfg.MinNumberSymbols {
|
||||||
return &util.JSONResponse{
|
return &util.JSONResponse{
|
||||||
Code: http.StatusBadRequest,
|
Code: http.StatusBadRequest,
|
||||||
JSON: jsonerror.WeakPassword(fmt.Sprintf("password too weak: minimum %d symbols", cfg.MinNumberSymbols)),
|
JSON: jsonerror.WeakPassword(fmt.Sprintf("password too weak: minimum %d symbols", cfg.MinNumberSymbols)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if cfg.RequireMixedCase {
|
if cfg.RequireMixedCase && (uppercaseCount == 0 || lowercaseCount == 0) {
|
||||||
lowercase := passwordLowercase.FindAllStringIndex(password, -1)
|
|
||||||
uppercase := passwordUppercase.FindAllStringIndex(password, -1)
|
|
||||||
if len(lowercase) == 0 || len(uppercase) == 0 {
|
|
||||||
return &util.JSONResponse{
|
return &util.JSONResponse{
|
||||||
Code: http.StatusBadRequest,
|
Code: http.StatusBadRequest,
|
||||||
JSON: jsonerror.WeakPassword("password must have uppercase and lowercase letters"),
|
JSON: jsonerror.WeakPassword("password must have uppercase and lowercase letters"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,9 +15,9 @@
|
||||||
package routing
|
package routing
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/matrix-org/dendrite/clientapi/auth/authtypes"
|
"github.com/matrix-org/dendrite/clientapi/auth/authtypes"
|
||||||
|
|
@ -231,78 +231,66 @@ func TestValidatePassword(t *testing.T) {
|
||||||
expected *util.JSONResponse
|
expected *util.JSONResponse
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
"default reject too short",
|
name: "default reject too short",
|
||||||
*defaults,
|
config: *defaults,
|
||||||
"foobar",
|
password: strings.Repeat("a", defaults.MinPasswordLength-1),
|
||||||
&util.JSONResponse{
|
expected: &util.JSONResponse{
|
||||||
Code: http.StatusBadRequest,
|
Code: http.StatusBadRequest,
|
||||||
JSON: jsonerror.WeakPassword(fmt.Sprintf("password too weak: min %d chars", defaults.MinPasswordLength)),
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"default reject too long",
|
name: "default reject too long",
|
||||||
*defaults,
|
config: *defaults,
|
||||||
// len 600
|
password: strings.Repeat("a", defaults.MaxPasswordLength+10),
|
||||||
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
expected: &util.JSONResponse{
|
||||||
&util.JSONResponse{
|
|
||||||
Code: http.StatusBadRequest,
|
Code: http.StatusBadRequest,
|
||||||
JSON: jsonerror.WeakPassword(fmt.Sprintf("'password' >%d characters", defaults.MaxPasswordLength)),
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"default allow long enough",
|
name: "default reject empty password",
|
||||||
*defaults,
|
config: *defaults,
|
||||||
"thisisalongenoughpassword",
|
password: "",
|
||||||
nil,
|
expected: &util.JSONResponse{
|
||||||
},
|
|
||||||
{
|
|
||||||
"default allow with symbols",
|
|
||||||
*defaults,
|
|
||||||
"ih@ve$ome$ymbol$_here",
|
|
||||||
nil,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"set min reject too short",
|
|
||||||
*custom,
|
|
||||||
"abcd",
|
|
||||||
&util.JSONResponse{
|
|
||||||
Code: http.StatusBadRequest,
|
Code: http.StatusBadRequest,
|
||||||
JSON: jsonerror.WeakPassword(fmt.Sprintf("password too weak: min %d chars", custom.MinPasswordLength)),
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"set max reject too long",
|
name: "set min reject too short",
|
||||||
*custom,
|
config: *custom,
|
||||||
// len 33
|
password: "abcd",
|
||||||
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
|
expected: &util.JSONResponse{
|
||||||
&util.JSONResponse{
|
|
||||||
Code: http.StatusBadRequest,
|
Code: http.StatusBadRequest,
|
||||||
JSON: jsonerror.WeakPassword(fmt.Sprintf("'password' >%d characters", custom.MaxPasswordLength)),
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"set symbols not enough",
|
name: "set max reject too long",
|
||||||
*custom,
|
config: *custom,
|
||||||
"thi$i$apasswordshouldbelong",
|
password: strings.Repeat("x", custom.MaxPasswordLength+10),
|
||||||
&util.JSONResponse{
|
expected: &util.JSONResponse{
|
||||||
Code: http.StatusBadRequest,
|
Code: http.StatusBadRequest,
|
||||||
JSON: jsonerror.WeakPassword(fmt.Sprintf("password too weak: minimum %d symbols", custom.MinNumberSymbols)),
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"require mixed case but none given",
|
name: "set symbols not enough",
|
||||||
*custom,
|
config: *custom,
|
||||||
"haha_all_lowercase_cant_catch_me",
|
password: "thi$i$apasswordshouldbelong",
|
||||||
&util.JSONResponse{
|
expected: &util.JSONResponse{
|
||||||
Code: http.StatusBadRequest,
|
Code: http.StatusBadRequest,
|
||||||
JSON: jsonerror.WeakPassword("password must have uppercase and lowercase letters"),
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"custom settings allow",
|
name: "require mixed case but none given",
|
||||||
*custom,
|
config: *custom,
|
||||||
"$0me_$up3r_$trong_P@ass",
|
password: "haha_all_lowercase_cant_catch_me",
|
||||||
nil,
|
expected: &util.JSONResponse{
|
||||||
|
Code: http.StatusBadRequest,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "custom settings allow",
|
||||||
|
config: *custom,
|
||||||
|
password: "$0me_$up3r_$trong_P@ass",
|
||||||
|
expected: nil,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -317,13 +305,15 @@ func TestValidatePassword(t *testing.T) {
|
||||||
t.Errorf("expected password to fail, but was validated")
|
t.Errorf("expected password to fail, but was validated")
|
||||||
} else if test.expected != nil && test.expected.Code != response.Code {
|
} else if test.expected != nil && test.expected.Code != response.Code {
|
||||||
t.Errorf("expected error code %d, got %d", test.expected.Code, response.Code)
|
t.Errorf("expected error code %d, got %d", test.expected.Code, response.Code)
|
||||||
} else if test.expected != nil && response != nil {
|
// } else if test.expected != nil && response != nil {
|
||||||
matrixError := response.JSON.(*jsonerror.MatrixError)
|
// matrixError := response.JSON.(*jsonerror.MatrixError)
|
||||||
expectedError := test.expected.JSON.(*jsonerror.MatrixError)
|
// expectedError := test.expected.JSON.(*jsonerror.MatrixError)
|
||||||
|
|
||||||
if expectedError.Err != matrixError.Err {
|
// if expectedError.Err != matrixError.Err {
|
||||||
t.Errorf("expected error: %s, got error: %s", expectedError.Err, matrixError.Err)
|
// t.Errorf("expected error: %s, got error: %s", expectedError.Err, matrixError.Err)
|
||||||
}
|
// }
|
||||||
|
} else if test.expected != nil && test.expected.Code == response.Code {
|
||||||
|
t.Logf("expected error code %d matches %d", test.expected.Code, response.Code)
|
||||||
} else if test.expected == nil && response == nil {
|
} else if test.expected == nil && response == nil {
|
||||||
t.Log("password validation passed")
|
t.Log("password validation passed")
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
22
setup/config/config_clientapi_test.go
Normal file
22
setup/config/config_clientapi_test.go
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestPasswordRequirementsDefaults(t *testing.T) {
|
||||||
|
requirements := &PasswordRequirements{}
|
||||||
|
requirements.Defaults()
|
||||||
|
|
||||||
|
if requirements.MinPasswordLength <= 0 {
|
||||||
|
t.Errorf("default minimum password length should be greater than 0, got %d", requirements.MinPasswordLength)
|
||||||
|
}
|
||||||
|
|
||||||
|
if requirements.MinPasswordLength > requirements.MaxPasswordLength {
|
||||||
|
t.Errorf(
|
||||||
|
"default minimum password length should not be greater than maxmium password length. currently: %d > %d",
|
||||||
|
requirements.MinPasswordLength,
|
||||||
|
requirements.MaxPasswordLength,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue