Implement support for base password constraints
This commit is contained in:
@@ -491,6 +491,7 @@ func (app *CortezaApp) Activate(ctx context.Context) (err error) {
|
||||
|
||||
updateFederationSettings(app.Opt.Federation, sysService.CurrentSettings)
|
||||
updateAuthSettings(app.AuthService, sysService.CurrentSettings)
|
||||
updatePasswdSettings(app.Opt.Auth, sysService.CurrentSettings)
|
||||
sysService.DefaultSettings.Register("auth.", func(ctx context.Context, current interface{}, set types.SettingValueSet) {
|
||||
appSettings, is := current.(*types.AppSettings)
|
||||
if !is {
|
||||
@@ -498,6 +499,7 @@ func (app *CortezaApp) Activate(ctx context.Context) (err error) {
|
||||
}
|
||||
|
||||
updateAuthSettings(app.AuthService, appSettings)
|
||||
updatePasswdSettings(app.Opt.Auth, sysService.CurrentSettings)
|
||||
})
|
||||
|
||||
updateLocaleSettings(app.Opt.Locale)
|
||||
@@ -601,6 +603,11 @@ func updateFederationSettings(opt options.FederationOpt, current *types.AppSetti
|
||||
current.Federation.Enabled = opt.Enabled
|
||||
}
|
||||
|
||||
// Checks if password security is enabled in the options
|
||||
func updatePasswdSettings(opt options.AuthOpt, current *types.AppSettings) {
|
||||
current.Auth.Internal.PasswordConstraints.PasswordSecurity = opt.PasswordSecurity
|
||||
}
|
||||
|
||||
// Sanitizes application (current) settings with languages from options
|
||||
//
|
||||
// It updates resource-translations.languages slice
|
||||
|
||||
@@ -73,7 +73,7 @@ func Test_changePasswordProc(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "provided password is not secure",
|
||||
payload: map[string]string{"error": "provided password is not secure; use longer password with more non-alphanumeric character"},
|
||||
payload: map[string]string{"error": "provided password is not secure; use longer password with more special characters"},
|
||||
link: GetLinks().ChangePassword,
|
||||
fn: func(_ *settings.Settings) {
|
||||
authService = &authServiceMocked{
|
||||
|
||||
@@ -147,7 +147,7 @@ func Test_signupProc(t *testing.T) {
|
||||
err: "",
|
||||
alerts: []request.Alert(nil),
|
||||
link: GetLinks().Signup,
|
||||
payload: map[string]string{"email": "", "error": "provided password is not secure; use longer password with more non-alphanumeric character", "handle": "", "name": ""},
|
||||
payload: map[string]string{"email": "", "error": "provided password is not secure; use longer password with more special characters", "handle": "", "name": ""},
|
||||
fn: func(_ *settings.Settings) {
|
||||
authService = &authServiceMocked{
|
||||
internalSignUp: func(c context.Context, user *types.User, s string) (u *types.User, err error) {
|
||||
|
||||
Generated
+2
@@ -15,6 +15,7 @@ import (
|
||||
type (
|
||||
AuthOpt struct {
|
||||
LogEnabled bool `env:"AUTH_LOG_ENABLED"`
|
||||
PasswordSecurity bool `env:"AUTH_PASSWORD_SECURITY"`
|
||||
Secret string `env:"AUTH_JWT_SECRET"`
|
||||
AccessTokenLifetime time.Duration `env:"AUTH_OAUTH2_ACCESS_TOKEN_LIFETIME"`
|
||||
RefreshTokenLifetime time.Duration `env:"AUTH_OAUTH2_REFRESH_TOKEN_LIFETIME"`
|
||||
@@ -44,6 +45,7 @@ type (
|
||||
// Auth initializes and returns a AuthOpt with default values
|
||||
func Auth() (o *AuthOpt) {
|
||||
o = &AuthOpt{
|
||||
PasswordSecurity: true,
|
||||
Secret: getSecretFromEnv("jwt secret"),
|
||||
AccessTokenLifetime: time.Hour * 2,
|
||||
RefreshTokenLifetime: time.Hour * 24 * 3,
|
||||
|
||||
@@ -10,6 +10,17 @@ props:
|
||||
description: |-
|
||||
Enable extra logging for authentication flows
|
||||
|
||||
- name: passwordSecurity
|
||||
type: bool
|
||||
default: true
|
||||
description: |-
|
||||
Password security allows you to disable constraints to which passwords must conform to.
|
||||
|
||||
[CAUTION]
|
||||
====
|
||||
Password security should not be disabled on production environments as it is insecure.
|
||||
====
|
||||
|
||||
- name: secret
|
||||
env: AUTH_JWT_SECRET
|
||||
default: getSecretFromEnv("jwt secret")
|
||||
|
||||
+33
-1
@@ -3,6 +3,7 @@ package service
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math"
|
||||
rand2 "math/rand"
|
||||
"regexp"
|
||||
"strconv"
|
||||
@@ -59,6 +60,9 @@ const (
|
||||
credentialsTypeMFAEmailOTP = "mfa-email-otp"
|
||||
|
||||
credentialsTokenLength = 32
|
||||
|
||||
passwordMinLength = 8
|
||||
passwordMaxLength = 256
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -622,10 +626,38 @@ func (svc auth) hashPassword(password string) (hash []byte, err error) {
|
||||
}
|
||||
|
||||
func (svc auth) CheckPasswordStrength(password string) bool {
|
||||
if len(password) <= 4 {
|
||||
pwdL := len(password)
|
||||
|
||||
// Ignore defined password constraints
|
||||
if !svc.settings.Auth.Internal.PasswordConstraints.PasswordSecurity {
|
||||
return true
|
||||
}
|
||||
|
||||
// Check the password length
|
||||
minL := math.Max(float64(passwordMinLength), float64(svc.settings.Auth.Internal.PasswordConstraints.MinLength))
|
||||
if pwdL < int(minL) || pwdL > passwordMaxLength {
|
||||
return false
|
||||
}
|
||||
|
||||
// Check special constraints
|
||||
// - numeric characters
|
||||
count := svc.settings.Auth.Internal.PasswordConstraints.MinNumCount
|
||||
if count > 0 {
|
||||
rr := regexp.MustCompile("[0-9]")
|
||||
if uint(len(rr.FindAllStringIndex(password, -1))) < count {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// - special characters
|
||||
count = svc.settings.Auth.Internal.PasswordConstraints.MinSpecialCount
|
||||
if count > 0 {
|
||||
rr := regexp.MustCompile("[^0-9a-zA-Z]")
|
||||
if uint(len(rr.FindAllStringIndex(password, -1))) < count {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
Generated
+4
-3
@@ -11,12 +11,13 @@ package service
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/cortezaproject/corteza-server/pkg/actionlog"
|
||||
"github.com/cortezaproject/corteza-server/pkg/errors"
|
||||
"github.com/cortezaproject/corteza-server/pkg/locale"
|
||||
"github.com/cortezaproject/corteza-server/system/types"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type (
|
||||
@@ -1248,7 +1249,7 @@ func AuthErrPasswordNotSecure(mm ...*authActionProps) *errors.Error {
|
||||
var e = errors.New(
|
||||
errors.KindInternal,
|
||||
|
||||
p.Format("provided password is not secure; use longer password with more non-alphanumeric character", nil),
|
||||
p.Format("provided password is not secure; use longer password with more special characters", nil),
|
||||
|
||||
errors.Meta("type", "passwordNotSecure"),
|
||||
errors.Meta("resource", "system:auth"),
|
||||
|
||||
@@ -143,7 +143,7 @@ errors:
|
||||
message: "password create is disabled"
|
||||
|
||||
- error: passwordNotSecure
|
||||
message: "provided password is not secure; use longer password with more non-alphanumeric character"
|
||||
message: "provided password is not secure; use longer password with more special characters"
|
||||
|
||||
- error: externalDisabledByConfig
|
||||
message: "external authentication (using external authentication provider) is disabled"
|
||||
|
||||
Generated
+4
-3
@@ -11,12 +11,13 @@ package service
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/cortezaproject/corteza-server/pkg/actionlog"
|
||||
"github.com/cortezaproject/corteza-server/pkg/errors"
|
||||
"github.com/cortezaproject/corteza-server/pkg/locale"
|
||||
"github.com/cortezaproject/corteza-server/system/types"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type (
|
||||
@@ -1261,7 +1262,7 @@ func UserErrPasswordNotSecure(mm ...*userActionProps) *errors.Error {
|
||||
var e = errors.New(
|
||||
errors.KindInternal,
|
||||
|
||||
p.Format("provided password is not secure; use longer password with more non-alphanumeric character", nil),
|
||||
p.Format("provided password is not secure; use longer password with more special characters", nil),
|
||||
|
||||
errors.Meta("type", "passwordNotSecure"),
|
||||
errors.Meta("resource", "system:user"),
|
||||
|
||||
@@ -145,7 +145,7 @@ errors:
|
||||
severity: warning
|
||||
|
||||
- error: passwordNotSecure
|
||||
message: "provided password is not secure; use longer password with more non-alphanumeric character"
|
||||
message: "provided password is not secure; use longer password with more special characters"
|
||||
|
||||
- error: maxUserLimitReached
|
||||
message: "you have reached your user limit, contact your Corteza administrator"
|
||||
|
||||
@@ -41,7 +41,7 @@ type (
|
||||
Auth struct {
|
||||
Internal struct {
|
||||
// Is internal authentication (username + password) enabled
|
||||
Enabled bool
|
||||
Enabled bool `json:"-"`
|
||||
|
||||
Signup struct {
|
||||
// Can users register
|
||||
@@ -49,10 +49,10 @@ type (
|
||||
|
||||
// Users must confirm their emails when signing-up
|
||||
EmailConfirmationRequired bool `kv:"email-confirmation-required"`
|
||||
}
|
||||
} `json:"-"`
|
||||
|
||||
// Can users reset their passwords
|
||||
PasswordReset struct{ Enabled bool } `kv:"password-reset"`
|
||||
PasswordReset struct{ Enabled bool } `json:"-" kv:"password-reset"`
|
||||
|
||||
// PasswordCreate setting for create password for user via generated link with token
|
||||
// If user has no password then link redirects to create password page
|
||||
@@ -61,14 +61,28 @@ type (
|
||||
PasswordCreate struct {
|
||||
Enabled bool
|
||||
Expires uint
|
||||
} `kv:"password-create"`
|
||||
} `json:"-" kv:"password-create"`
|
||||
|
||||
// Splits credentials check into 2 parts
|
||||
// If user has password credentials it offers him to enter the password
|
||||
// Otherwise we offer the user to choose among the enabled external providers
|
||||
// If only one ext. provider is enabled, user is automatically redirected there
|
||||
SplitCredentialsCheck bool `kv:"split-credentials-check"`
|
||||
}
|
||||
SplitCredentialsCheck bool `json:"-" kv:"split-credentials-check"`
|
||||
|
||||
PasswordConstraints struct {
|
||||
// Should the environment not enforce the constraints
|
||||
PasswordSecurity bool `kv:"-" json:"passwordSecurity"`
|
||||
|
||||
// The min password length
|
||||
MinLength uint `kv:"min-length"`
|
||||
|
||||
// The min number of numeric characters
|
||||
MinNumCount uint `kv:"min-num-count"`
|
||||
|
||||
// The min number of special characters
|
||||
MinSpecialCount uint `kv:"min-special-count"`
|
||||
} `kv:"password-constraints" json:"passwordConstraints"`
|
||||
} `json:"internal"`
|
||||
|
||||
External struct {
|
||||
// Is external authentication
|
||||
@@ -100,7 +114,7 @@ type (
|
||||
|
||||
// all external providers we know
|
||||
Providers ExternalAuthProviderSet
|
||||
}
|
||||
} `json:"-"`
|
||||
|
||||
MultiFactor struct {
|
||||
EmailOTP struct {
|
||||
@@ -129,13 +143,13 @@ type (
|
||||
// TOTP issuer, defaults to "Corteza"
|
||||
Issuer string
|
||||
} `kv:"totp"`
|
||||
} `kv:"multi-factor"`
|
||||
} `json:"-" kv:"multi-factor"`
|
||||
|
||||
Mail struct {
|
||||
FromAddress string `kv:"from-address"`
|
||||
FromName string `kv:"from-name"`
|
||||
} `json:"-"`
|
||||
} `json:"-"`
|
||||
} `json:"auth"`
|
||||
|
||||
Compose struct {
|
||||
// UI related settings
|
||||
|
||||
Generated
Vendored
+1
-1
@@ -19,7 +19,7 @@ errors:
|
||||
notAllowedToRemoveTOTP: not allowed to remove TOTP
|
||||
passwodResetFailedOldPasswordCheckFailed: failed to change password, old password does not match
|
||||
passwordChangeFailedForUnknownUser: failed to change password for the unknown user
|
||||
passwordNotSecure: provided password is not secure; use longer password with more non-alphanumeric character
|
||||
passwordNotSecure: provided password is not secure; use longer password with more special characters
|
||||
passwordResetDisabledByConfig: password reset is disabled
|
||||
profileWithoutValidEmail: external authentication provider returned profile without valid email
|
||||
unconfiguredTOTP: TOTP not configured
|
||||
|
||||
Generated
Vendored
+1
-1
@@ -16,5 +16,5 @@ errors:
|
||||
notAllowedToUpdate: not allowed to update this user
|
||||
notAllowedToUpdateSystem: not allowed to update system users
|
||||
notFound: user not found
|
||||
passwordNotSecure: provided password is not secure; use longer password with more non-alphanumeric character
|
||||
passwordNotSecure: provided password is not secure; use longer password with more special characters
|
||||
usernameNotUnique: username not unique
|
||||
|
||||
Generated
Vendored
+1
-1
@@ -20,7 +20,7 @@ errors:
|
||||
notAllowedToRemoveTOTP: not allowed to remove TOTP
|
||||
passwodResetFailedOldPasswordCheckFailed: failed to change password, old password does not match
|
||||
passwordChangeFailedForUnknownUser: failed to change password for the unknown user
|
||||
passwordNotSecure: provided password is not secure; use longer password with more non-alphanumeric character
|
||||
passwordNotSecure: provided password is not secure; use longer password with more special characters
|
||||
passwordResetDisabledByConfig: password reset is disabled
|
||||
profileWithoutValidEmail: external authentication provider returned profile without valid email
|
||||
unconfiguredTOTP: TOTP not configured
|
||||
|
||||
Generated
Vendored
+1
-1
@@ -17,5 +17,5 @@ errors:
|
||||
notAllowedToUpdate: not allowed to update this user
|
||||
notAllowedToUpdateSystem: not allowed to update system users
|
||||
notFound: user not found
|
||||
passwordNotSecure: provided password is not secure; use longer password with more non-alphanumeric character
|
||||
passwordNotSecure: provided password is not secure; use longer password with more special characters
|
||||
usernameNotUnique: username not unique
|
||||
|
||||
Generated
Vendored
+1
-1
@@ -20,7 +20,7 @@ errors:
|
||||
notAllowedToRemoveTOTP: not allowed to remove TOTP
|
||||
passwodResetFailedOldPasswordCheckFailed: failed to change password, old password does not match
|
||||
passwordChangeFailedForUnknownUser: failed to change password for the unknown user
|
||||
passwordNotSecure: provided password is not secure; use longer password with more non-alphanumeric character
|
||||
passwordNotSecure: provided password is not secure; use longer password with more special characters
|
||||
passwordResetDisabledByConfig: password reset is disabled
|
||||
profileWithoutValidEmail: external authentication provider returned profile without valid email
|
||||
unconfiguredTOTP: TOTP not configured
|
||||
|
||||
Generated
Vendored
+1
-1
@@ -17,5 +17,5 @@ errors:
|
||||
notAllowedToUpdate: not allowed to update this user
|
||||
notAllowedToUpdateSystem: not allowed to update system users
|
||||
notFound: user not found
|
||||
passwordNotSecure: provided password is not secure; use longer password with more non-alphanumeric character
|
||||
passwordNotSecure: provided password is not secure; use longer password with more special characters
|
||||
usernameNotUnique: username not unique
|
||||
|
||||
Generated
Vendored
+1
-1
@@ -20,7 +20,7 @@ errors:
|
||||
notAllowedToRemoveTOTP: not allowed to remove TOTP
|
||||
passwodResetFailedOldPasswordCheckFailed: failed to change password, old password does not match
|
||||
passwordChangeFailedForUnknownUser: failed to change password for the unknown user
|
||||
passwordNotSecure: provided password is not secure; use longer password with more non-alphanumeric character
|
||||
passwordNotSecure: provided password is not secure; use longer password with more special characters
|
||||
passwordResetDisabledByConfig: password reset is disabled
|
||||
profileWithoutValidEmail: external authentication provider returned profile without valid email
|
||||
unconfiguredTOTP: TOTP not configured
|
||||
|
||||
Generated
Vendored
+1
-1
@@ -17,5 +17,5 @@ errors:
|
||||
notAllowedToUpdate: not allowed to update this user
|
||||
notAllowedToUpdateSystem: not allowed to update system users
|
||||
notFound: user not found
|
||||
passwordNotSecure: provided password is not secure; use longer password with more non-alphanumeric character
|
||||
passwordNotSecure: provided password is not secure; use longer password with more special characters
|
||||
usernameNotUnique: username not unique
|
||||
|
||||
Generated
Vendored
+1
-1
@@ -20,7 +20,7 @@ errors:
|
||||
notAllowedToRemoveTOTP: not allowed to remove TOTP
|
||||
passwodResetFailedOldPasswordCheckFailed: failed to change password, old password does not match
|
||||
passwordChangeFailedForUnknownUser: failed to change password for the unknown user
|
||||
passwordNotSecure: provided password is not secure; use longer password with more non-alphanumeric character
|
||||
passwordNotSecure: provided password is not secure; use longer password with more special characters
|
||||
passwordResetDisabledByConfig: password reset is disabled
|
||||
profileWithoutValidEmail: external authentication provider returned profile without valid email
|
||||
unconfiguredTOTP: TOTP not configured
|
||||
|
||||
Generated
Vendored
+1
-1
@@ -17,5 +17,5 @@ errors:
|
||||
notAllowedToUpdate: not allowed to update this user
|
||||
notAllowedToUpdateSystem: not allowed to update system users
|
||||
notFound: user not found
|
||||
passwordNotSecure: provided password is not secure; use longer password with more non-alphanumeric character
|
||||
passwordNotSecure: provided password is not secure; use longer password with more special characters
|
||||
usernameNotUnique: username not unique
|
||||
|
||||
Reference in New Issue
Block a user