fix(configuration): remove unused password policy option (#3149)
Removes the min score option from the ZXCVBN policy and adds tests.pull/3147/head^2
parent
f9da940bfc
commit
9d5ac4526e
|
@ -730,9 +730,6 @@ components:
|
||||||
max_length:
|
max_length:
|
||||||
type: integer
|
type: integer
|
||||||
description: The maximum password length when using the standard mode.
|
description: The maximum password length when using the standard mode.
|
||||||
min_score:
|
|
||||||
type: integer
|
|
||||||
description: The minimum password score when using the zxcvbn mode.
|
|
||||||
require_uppercase:
|
require_uppercase:
|
||||||
type: boolean
|
type: boolean
|
||||||
description: If uppercase characters are required when using the standard mode.
|
description: If uppercase characters are required when using the standard mode.
|
||||||
|
|
|
@ -364,7 +364,6 @@ password_policy:
|
||||||
## zxcvbn is a well known and used password strength algorithm. It does not have tunable settings.
|
## zxcvbn is a well known and used password strength algorithm. It does not have tunable settings.
|
||||||
zxcvbn:
|
zxcvbn:
|
||||||
enabled: false
|
enabled: false
|
||||||
min_score: 0
|
|
||||||
|
|
||||||
##
|
##
|
||||||
## Access Control Configuration
|
## Access Control Configuration
|
||||||
|
|
|
@ -364,7 +364,6 @@ password_policy:
|
||||||
## zxcvbn is a well known and used password strength algorithm. It does not have tunable settings.
|
## zxcvbn is a well known and used password strength algorithm. It does not have tunable settings.
|
||||||
zxcvbn:
|
zxcvbn:
|
||||||
enabled: false
|
enabled: false
|
||||||
min_score: 0
|
|
||||||
|
|
||||||
##
|
##
|
||||||
## Access Control Configuration
|
## Access Control Configuration
|
||||||
|
|
|
@ -11,16 +11,15 @@ type PasswordPolicyStandardParams struct {
|
||||||
RequireSpecial bool `koanf:"require_special"`
|
RequireSpecial bool `koanf:"require_special"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// PasswordPolicyZxcvbnParams represents the configuration related to zxcvbn parameters of password policy.
|
// PasswordPolicyZXCVBNParams represents the configuration related to ZXCVBN parameters of password policy.
|
||||||
type PasswordPolicyZxcvbnParams struct {
|
type PasswordPolicyZXCVBNParams struct {
|
||||||
Enabled bool `koanf:"enabled"`
|
Enabled bool `koanf:"enabled"`
|
||||||
MinScore int `koanf:"min_score"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// PasswordPolicyConfiguration represents the configuration related to password policy.
|
// PasswordPolicyConfiguration represents the configuration related to password policy.
|
||||||
type PasswordPolicyConfiguration struct {
|
type PasswordPolicyConfiguration struct {
|
||||||
Standard PasswordPolicyStandardParams `koanf:"standard"`
|
Standard PasswordPolicyStandardParams `koanf:"standard"`
|
||||||
Zxcvbn PasswordPolicyZxcvbnParams `koanf:"zxcvbn"`
|
ZXCVBN PasswordPolicyZXCVBNParams `koanf:"zxcvbn"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// DefaultPasswordPolicyConfiguration is the default password policy configuration.
|
// DefaultPasswordPolicyConfiguration is the default password policy configuration.
|
||||||
|
@ -30,8 +29,7 @@ var DefaultPasswordPolicyConfiguration = PasswordPolicyConfiguration{
|
||||||
MinLength: 8,
|
MinLength: 8,
|
||||||
MaxLength: 0,
|
MaxLength: 0,
|
||||||
},
|
},
|
||||||
Zxcvbn: PasswordPolicyZxcvbnParams{
|
ZXCVBN: PasswordPolicyZXCVBNParams{
|
||||||
Enabled: false,
|
Enabled: false,
|
||||||
MinScore: 0,
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -243,6 +243,11 @@ const (
|
||||||
errFmtServerBufferSize = "server: option '%s_buffer_size' must be above 0 but it is configured as '%d'"
|
errFmtServerBufferSize = "server: option '%s_buffer_size' must be above 0 but it is configured as '%d'"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
errFmtPasswordPolicyMinLengthNotGreaterThanZero = "password_policy: standard: option 'min_length' must be greater than 0 but is configured as %d"
|
||||||
|
errPasswordPolicyMultipleDefined = "password_policy: only a single password policy mechanism can be specified"
|
||||||
|
)
|
||||||
|
|
||||||
// Error constants.
|
// Error constants.
|
||||||
const (
|
const (
|
||||||
/*
|
/*
|
||||||
|
@ -519,7 +524,6 @@ var ValidKeys = []string{
|
||||||
"password_policy.standard.require_number",
|
"password_policy.standard.require_number",
|
||||||
"password_policy.standard.require_special",
|
"password_policy.standard.require_special",
|
||||||
"password_policy.zxcvbn.enabled",
|
"password_policy.zxcvbn.enabled",
|
||||||
"password_policy.zxcvbn.min_score",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var replacedKeys = map[string]string{
|
var replacedKeys = map[string]string{
|
||||||
|
|
|
@ -54,7 +54,7 @@ func TestAllSpecificErrorKeys(t *testing.T) {
|
||||||
|
|
||||||
var uniqueValues []string
|
var uniqueValues []string
|
||||||
|
|
||||||
// Setup configKeys and uniqueValues expected.
|
// Setup configKeys and uniqueValues expectedErrs.
|
||||||
for key, value := range specificErrorKeys {
|
for key, value := range specificErrorKeys {
|
||||||
configKeys = append(configKeys, key)
|
configKeys = append(configKeys, key)
|
||||||
|
|
||||||
|
|
|
@ -1,33 +1,27 @@
|
||||||
package validator
|
package validator
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"fmt"
|
||||||
|
|
||||||
"github.com/authelia/authelia/v4/internal/configuration/schema"
|
"github.com/authelia/authelia/v4/internal/configuration/schema"
|
||||||
"github.com/authelia/authelia/v4/internal/utils"
|
"github.com/authelia/authelia/v4/internal/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ValidatePasswordPolicy validates and update Password Policy configuration.
|
// ValidatePasswordPolicy validates and update Password Policy configuration.
|
||||||
func ValidatePasswordPolicy(configuration *schema.PasswordPolicyConfiguration, validator *schema.StructValidator) {
|
func ValidatePasswordPolicy(config *schema.PasswordPolicyConfiguration, validator *schema.StructValidator) {
|
||||||
if !utils.IsBoolCountLessThanN(1, true, configuration.Standard.Enabled, configuration.Zxcvbn.Enabled) {
|
if !utils.IsBoolCountLessThanN(1, true, config.Standard.Enabled, config.ZXCVBN.Enabled) {
|
||||||
validator.Push(errors.New("password_policy:only one password policy can be enabled at a time"))
|
validator.Push(fmt.Errorf(errPasswordPolicyMultipleDefined))
|
||||||
}
|
}
|
||||||
|
|
||||||
if configuration.Standard.Enabled {
|
if config.Standard.Enabled {
|
||||||
if configuration.Standard.MinLength == 0 {
|
if config.Standard.MinLength == 0 {
|
||||||
configuration.Standard.MinLength = schema.DefaultPasswordPolicyConfiguration.Standard.MinLength
|
config.Standard.MinLength = schema.DefaultPasswordPolicyConfiguration.Standard.MinLength
|
||||||
} else if configuration.Standard.MinLength < 0 {
|
} else if config.Standard.MinLength < 0 {
|
||||||
validator.Push(errors.New("password_policy: min_length must be > 0"))
|
validator.Push(fmt.Errorf(errFmtPasswordPolicyMinLengthNotGreaterThanZero, config.Standard.MinLength))
|
||||||
}
|
}
|
||||||
|
|
||||||
if configuration.Standard.MaxLength == 0 {
|
if config.Standard.MaxLength == 0 {
|
||||||
configuration.Standard.MaxLength = schema.DefaultPasswordPolicyConfiguration.Standard.MaxLength
|
config.Standard.MaxLength = schema.DefaultPasswordPolicyConfiguration.Standard.MaxLength
|
||||||
}
|
|
||||||
} else if configuration.Zxcvbn.Enabled {
|
|
||||||
if configuration.Zxcvbn.MinScore == 0 {
|
|
||||||
configuration.Zxcvbn.MinScore = schema.DefaultPasswordPolicyConfiguration.Zxcvbn.MinScore
|
|
||||||
} else if configuration.Zxcvbn.MinScore < 0 || configuration.Zxcvbn.MinScore > 4 {
|
|
||||||
validator.Push(errors.New("min_score must be between 0 and 4"))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,113 @@
|
||||||
|
package validator
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
"github.com/authelia/authelia/v4/internal/configuration/schema"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestValidatePasswordPolicy(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
desc string
|
||||||
|
have, expected *schema.PasswordPolicyConfiguration
|
||||||
|
expectedErrs []string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
desc: "ShouldRaiseErrorsWhenMisconfigured",
|
||||||
|
have: &schema.PasswordPolicyConfiguration{
|
||||||
|
Standard: schema.PasswordPolicyStandardParams{
|
||||||
|
Enabled: true,
|
||||||
|
MinLength: -1,
|
||||||
|
},
|
||||||
|
ZXCVBN: schema.PasswordPolicyZXCVBNParams{
|
||||||
|
Enabled: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: &schema.PasswordPolicyConfiguration{
|
||||||
|
Standard: schema.PasswordPolicyStandardParams{
|
||||||
|
Enabled: true,
|
||||||
|
MinLength: -1,
|
||||||
|
},
|
||||||
|
ZXCVBN: schema.PasswordPolicyZXCVBNParams{
|
||||||
|
Enabled: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectedErrs: []string{
|
||||||
|
"password_policy: only a single password policy mechanism can be specified",
|
||||||
|
"password_policy: standard: option 'min_length' must be greater than 0 but is configured as -1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "ShouldNotRaiseErrorsStandard",
|
||||||
|
have: &schema.PasswordPolicyConfiguration{
|
||||||
|
Standard: schema.PasswordPolicyStandardParams{
|
||||||
|
Enabled: true,
|
||||||
|
MinLength: 8,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: &schema.PasswordPolicyConfiguration{
|
||||||
|
Standard: schema.PasswordPolicyStandardParams{
|
||||||
|
Enabled: true,
|
||||||
|
MinLength: 8,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "ShouldNotRaiseErrorsZXCVBN",
|
||||||
|
have: &schema.PasswordPolicyConfiguration{
|
||||||
|
ZXCVBN: schema.PasswordPolicyZXCVBNParams{
|
||||||
|
Enabled: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: &schema.PasswordPolicyConfiguration{
|
||||||
|
ZXCVBN: schema.PasswordPolicyZXCVBNParams{
|
||||||
|
Enabled: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "ShouldSetDefaultstandard",
|
||||||
|
have: &schema.PasswordPolicyConfiguration{
|
||||||
|
Standard: schema.PasswordPolicyStandardParams{
|
||||||
|
Enabled: true,
|
||||||
|
MinLength: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: &schema.PasswordPolicyConfiguration{
|
||||||
|
Standard: schema.PasswordPolicyStandardParams{
|
||||||
|
Enabled: true,
|
||||||
|
MinLength: 8,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.desc, func(t *testing.T) {
|
||||||
|
validator := &schema.StructValidator{}
|
||||||
|
ValidatePasswordPolicy(tc.have, validator)
|
||||||
|
|
||||||
|
assert.Len(t, validator.Warnings(), 0)
|
||||||
|
|
||||||
|
assert.Equal(t, tc.expected.Standard.MaxLength, tc.have.Standard.MaxLength)
|
||||||
|
assert.Equal(t, tc.expected.Standard.MinLength, tc.have.Standard.MinLength)
|
||||||
|
assert.Equal(t, tc.expected.Standard.RequireNumber, tc.have.Standard.RequireNumber)
|
||||||
|
assert.Equal(t, tc.expected.Standard.RequireSpecial, tc.have.Standard.RequireSpecial)
|
||||||
|
assert.Equal(t, tc.expected.Standard.RequireUppercase, tc.have.Standard.RequireUppercase)
|
||||||
|
assert.Equal(t, tc.expected.Standard.RequireLowercase, tc.have.Standard.RequireLowercase)
|
||||||
|
|
||||||
|
errs := validator.Errors()
|
||||||
|
require.Len(t, errs, len(tc.expectedErrs))
|
||||||
|
|
||||||
|
for i := 0; i < len(errs); i++ {
|
||||||
|
t.Run(fmt.Sprintf("Err%d", i+1), func(t *testing.T) {
|
||||||
|
assert.EqualError(t, errs[i], tc.expectedErrs[i])
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -18,9 +18,8 @@ func PasswordPolicyConfigurationGet(ctx *middlewares.AutheliaCtx) {
|
||||||
policyResponse.RequireUppercase = ctx.Configuration.PasswordPolicy.Standard.RequireUppercase
|
policyResponse.RequireUppercase = ctx.Configuration.PasswordPolicy.Standard.RequireUppercase
|
||||||
policyResponse.RequireNumber = ctx.Configuration.PasswordPolicy.Standard.RequireNumber
|
policyResponse.RequireNumber = ctx.Configuration.PasswordPolicy.Standard.RequireNumber
|
||||||
policyResponse.RequireSpecial = ctx.Configuration.PasswordPolicy.Standard.RequireSpecial
|
policyResponse.RequireSpecial = ctx.Configuration.PasswordPolicy.Standard.RequireSpecial
|
||||||
} else if ctx.Configuration.PasswordPolicy.Zxcvbn.Enabled {
|
} else if ctx.Configuration.PasswordPolicy.ZXCVBN.Enabled {
|
||||||
policyResponse.Mode = "zxcvbn"
|
policyResponse.Mode = "zxcvbn"
|
||||||
policyResponse.MinScore = ctx.Configuration.PasswordPolicy.Zxcvbn.MinScore
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
|
|
|
@ -120,7 +120,6 @@ type PassworPolicyBody struct {
|
||||||
Mode string `json:"mode"`
|
Mode string `json:"mode"`
|
||||||
MinLength int `json:"min_length"`
|
MinLength int `json:"min_length"`
|
||||||
MaxLength int `json:"max_length"`
|
MaxLength int `json:"max_length"`
|
||||||
MinScore int `json:"min_score"`
|
|
||||||
RequireUppercase bool `json:"require_uppercase"`
|
RequireUppercase bool `json:"require_uppercase"`
|
||||||
RequireLowercase bool `json:"require_lowercase"`
|
RequireLowercase bool `json:"require_lowercase"`
|
||||||
RequireNumber bool `json:"require_number"`
|
RequireNumber bool `json:"require_number"`
|
||||||
|
|
|
@ -23,7 +23,7 @@ func TestNewPasswordPolicyProvider(t *testing.T) {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: "ShouldReturnUnconfiguredProviderWhenZxcvbn",
|
desc: "ShouldReturnUnconfiguredProviderWhenZxcvbn",
|
||||||
have: schema.PasswordPolicyConfiguration{Zxcvbn: schema.PasswordPolicyZxcvbnParams{Enabled: true}},
|
have: schema.PasswordPolicyConfiguration{ZXCVBN: schema.PasswordPolicyZXCVBNParams{Enabled: true}},
|
||||||
expected: PasswordPolicyProvider{},
|
expected: PasswordPolicyProvider{},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -12,7 +12,6 @@ it("renders without crashing", () => {
|
||||||
policy={{
|
policy={{
|
||||||
max_length: 0,
|
max_length: 0,
|
||||||
min_length: 4,
|
min_length: 4,
|
||||||
min_score: 0,
|
|
||||||
require_lowercase: false,
|
require_lowercase: false,
|
||||||
require_number: false,
|
require_number: false,
|
||||||
require_special: false,
|
require_special: false,
|
||||||
|
@ -30,7 +29,6 @@ it("renders adjusted height without crashing", () => {
|
||||||
policy={{
|
policy={{
|
||||||
max_length: 0,
|
max_length: 0,
|
||||||
min_length: 4,
|
min_length: 4,
|
||||||
min_score: 0,
|
|
||||||
require_lowercase: false,
|
require_lowercase: false,
|
||||||
require_number: false,
|
require_number: false,
|
||||||
require_special: false,
|
require_special: false,
|
||||||
|
|
|
@ -8,7 +8,6 @@ export interface PasswordPolicyConfiguration {
|
||||||
mode: PasswordPolicyMode;
|
mode: PasswordPolicyMode;
|
||||||
min_length: number;
|
min_length: number;
|
||||||
max_length: number;
|
max_length: number;
|
||||||
min_score: number;
|
|
||||||
require_uppercase: boolean;
|
require_uppercase: boolean;
|
||||||
require_lowercase: boolean;
|
require_lowercase: boolean;
|
||||||
require_number: boolean;
|
require_number: boolean;
|
||||||
|
|
|
@ -6,7 +6,6 @@ interface PasswordPolicyConfigurationPayload {
|
||||||
mode: ModePasswordPolicy;
|
mode: ModePasswordPolicy;
|
||||||
min_length: number;
|
min_length: number;
|
||||||
max_length: number;
|
max_length: number;
|
||||||
min_score: number;
|
|
||||||
require_uppercase: boolean;
|
require_uppercase: boolean;
|
||||||
require_lowercase: boolean;
|
require_lowercase: boolean;
|
||||||
require_number: boolean;
|
require_number: boolean;
|
||||||
|
|
|
@ -32,7 +32,6 @@ const ResetPasswordStep2 = function () {
|
||||||
const [pPolicy, setPPolicy] = useState<PasswordPolicyConfiguration>({
|
const [pPolicy, setPPolicy] = useState<PasswordPolicyConfiguration>({
|
||||||
max_length: 0,
|
max_length: 0,
|
||||||
min_length: 8,
|
min_length: 8,
|
||||||
min_score: 0,
|
|
||||||
require_lowercase: false,
|
require_lowercase: false,
|
||||||
require_number: false,
|
require_number: false,
|
||||||
require_special: false,
|
require_special: false,
|
||||||
|
|
Loading…
Reference in New Issue