Password Complexity Checks (#6230)
Add password complexity checks. The default settings require a lowercase, uppercase, number and a special character within passwords. Co-Authored-By: T-M-A <maxim.tkachenko@gmail.com> Co-Authored-By: Lanre Adelowo <adelowomailbox@gmail.com> Co-Authored-By: guillep2k <18600385+guillep2k@users.noreply.github.com> Co-Authored-By: Lauris BH <lauris@nix.lv>
This commit is contained in:
parent
f9aba9ba0f
commit
db657192d0
11 changed files with 207 additions and 37 deletions
19
cmd/admin.go
19
cmd/admin.go
|
@ -13,9 +13,9 @@ import (
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
"code.gitea.io/gitea/models"
|
||||||
"code.gitea.io/gitea/modules/auth/oauth2"
|
"code.gitea.io/gitea/modules/auth/oauth2"
|
||||||
"code.gitea.io/gitea/modules/generate"
|
|
||||||
"code.gitea.io/gitea/modules/git"
|
"code.gitea.io/gitea/modules/git"
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
|
pwd "code.gitea.io/gitea/modules/password"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
|
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
|
@ -233,7 +233,9 @@ func runChangePassword(c *cli.Context) error {
|
||||||
if err := initDB(); err != nil {
|
if err := initDB(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if !pwd.IsComplexEnough(c.String("password")) {
|
||||||
|
return errors.New("Password does not meet complexity requirements")
|
||||||
|
}
|
||||||
uname := c.String("username")
|
uname := c.String("username")
|
||||||
user, err := models.GetUserByName(uname)
|
user, err := models.GetUserByName(uname)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -243,6 +245,7 @@ func runChangePassword(c *cli.Context) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
user.HashPassword(c.String("password"))
|
user.HashPassword(c.String("password"))
|
||||||
|
|
||||||
if err := models.UpdateUserCols(user, "passwd", "salt"); err != nil {
|
if err := models.UpdateUserCols(user, "passwd", "salt"); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -275,26 +278,24 @@ func runCreateUser(c *cli.Context) error {
|
||||||
fmt.Fprintf(os.Stderr, "--name flag is deprecated. Use --username instead.\n")
|
fmt.Fprintf(os.Stderr, "--name flag is deprecated. Use --username instead.\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
var password string
|
if err := initDB(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var password string
|
||||||
if c.IsSet("password") {
|
if c.IsSet("password") {
|
||||||
password = c.String("password")
|
password = c.String("password")
|
||||||
} else if c.IsSet("random-password") {
|
} else if c.IsSet("random-password") {
|
||||||
var err error
|
var err error
|
||||||
password, err = generate.GetRandomString(c.Int("random-password-length"))
|
password, err = pwd.Generate(c.Int("random-password-length"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("generated random password is '%s'\n", password)
|
fmt.Printf("generated random password is '%s'\n", password)
|
||||||
} else {
|
} else {
|
||||||
return errors.New("must set either password or random-password flag")
|
return errors.New("must set either password or random-password flag")
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := initDB(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// always default to true
|
// always default to true
|
||||||
var changePassword = true
|
var changePassword = true
|
||||||
|
|
||||||
|
|
|
@ -332,6 +332,9 @@ MIN_PASSWORD_LENGTH = 6
|
||||||
IMPORT_LOCAL_PATHS = false
|
IMPORT_LOCAL_PATHS = false
|
||||||
; Set to true to prevent all users (including admin) from creating custom git hooks
|
; Set to true to prevent all users (including admin) from creating custom git hooks
|
||||||
DISABLE_GIT_HOOKS = false
|
DISABLE_GIT_HOOKS = false
|
||||||
|
;Comma separated list of character classes required to pass minimum complexity.
|
||||||
|
;If left empty or no valid values are specified, the default values (`lower,upper,digit,spec`) will be used.
|
||||||
|
PASSWORD_COMPLEXITY = lower,upper,digit,spec
|
||||||
; Password Hash algorithm, either "pbkdf2", "argon2", "scrypt" or "bcrypt"
|
; Password Hash algorithm, either "pbkdf2", "argon2", "scrypt" or "bcrypt"
|
||||||
PASSWORD_HASH_ALGO = pbkdf2
|
PASSWORD_HASH_ALGO = pbkdf2
|
||||||
; Set false to allow JavaScript to read CSRF cookie
|
; Set false to allow JavaScript to read CSRF cookie
|
||||||
|
@ -415,7 +418,7 @@ DEFAULT_ALLOW_CREATE_ORGANIZATION = true
|
||||||
; Public is for everyone
|
; Public is for everyone
|
||||||
DEFAULT_ORG_VISIBILITY = public
|
DEFAULT_ORG_VISIBILITY = public
|
||||||
; Default value for DefaultOrgMemberVisible
|
; Default value for DefaultOrgMemberVisible
|
||||||
; True will make the membership of the users visible when added to the organisation
|
; True will make the membership of the users visible when added to the organisation
|
||||||
DEFAULT_ORG_MEMBER_VISIBLE = false
|
DEFAULT_ORG_MEMBER_VISIBLE = false
|
||||||
; Default value for EnableDependencies
|
; Default value for EnableDependencies
|
||||||
; Repositories will use dependencies by default depending on this setting
|
; Repositories will use dependencies by default depending on this setting
|
||||||
|
|
|
@ -208,6 +208,11 @@ Values containing `#` or `;` must be quoted using `` ` `` or `"""`.
|
||||||
- `INTERNAL_TOKEN_URI`: **<empty>**: Instead of defining internal token in the configuration, this configuration option can be used to give Gitea a path to a file that contains the internal token (example value: `file:/etc/gitea/internal_token`)
|
- `INTERNAL_TOKEN_URI`: **<empty>**: Instead of defining internal token in the configuration, this configuration option can be used to give Gitea a path to a file that contains the internal token (example value: `file:/etc/gitea/internal_token`)
|
||||||
- `PASSWORD_HASH_ALGO`: **pbkdf2**: The hash algorithm to use \[pbkdf2, argon2, scrypt, bcrypt\].
|
- `PASSWORD_HASH_ALGO`: **pbkdf2**: The hash algorithm to use \[pbkdf2, argon2, scrypt, bcrypt\].
|
||||||
- `CSRF_COOKIE_HTTP_ONLY`: **true**: Set false to allow JavaScript to read CSRF cookie.
|
- `CSRF_COOKIE_HTTP_ONLY`: **true**: Set false to allow JavaScript to read CSRF cookie.
|
||||||
|
- `PASSWORD_COMPLEXITY`: **lower,upper,digit,spec**: Comma separated list of character classes required to pass minimum complexity. If left empty or no valid values are specified, the default values will be used. Possible values are:
|
||||||
|
- lower - use one or more lower latin characters
|
||||||
|
- upper - use one or more upper latin characters
|
||||||
|
- digit - use one or more digits
|
||||||
|
- spec - use one or more special characters as ``][!"#$%&'()*+,./:;<=>?@\^_{|}~`-`` and space symbol.
|
||||||
|
|
||||||
## OpenID (`openid`)
|
## OpenID (`openid`)
|
||||||
|
|
||||||
|
|
73
modules/password/password.go
Normal file
73
modules/password/password.go
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package password
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rand"
|
||||||
|
"math/big"
|
||||||
|
"regexp"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/modules/setting"
|
||||||
|
)
|
||||||
|
|
||||||
|
var matchComplexities = map[string]regexp.Regexp{}
|
||||||
|
var matchComplexityOnce sync.Once
|
||||||
|
var validChars string
|
||||||
|
var validComplexities = map[string]string{
|
||||||
|
"lower": "abcdefghijklmnopqrstuvwxyz",
|
||||||
|
"upper": "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
|
||||||
|
"digit": "0123456789",
|
||||||
|
"spec": `][ !"#$%&'()*+,./:;<=>?@\^_{|}~` + "`-",
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewComplexity for preparation
|
||||||
|
func NewComplexity() {
|
||||||
|
matchComplexityOnce.Do(func() {
|
||||||
|
if len(setting.PasswordComplexity) > 0 {
|
||||||
|
for key, val := range setting.PasswordComplexity {
|
||||||
|
matchComplexity := regexp.MustCompile(val)
|
||||||
|
matchComplexities[key] = *matchComplexity
|
||||||
|
validChars += validComplexities[key]
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for _, val := range validComplexities {
|
||||||
|
validChars += val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsComplexEnough return True if password is Complexity
|
||||||
|
func IsComplexEnough(pwd string) bool {
|
||||||
|
if len(setting.PasswordComplexity) > 0 {
|
||||||
|
NewComplexity()
|
||||||
|
for _, val := range matchComplexities {
|
||||||
|
if !val.MatchString(pwd) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate a random password
|
||||||
|
func Generate(n int) (string, error) {
|
||||||
|
NewComplexity()
|
||||||
|
buffer := make([]byte, n)
|
||||||
|
max := big.NewInt(int64(len(validChars)))
|
||||||
|
for {
|
||||||
|
for j := 0; j < n; j++ {
|
||||||
|
rnd, err := rand.Int(rand.Reader, max)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
buffer[j] = validChars[rnd.Int64()]
|
||||||
|
}
|
||||||
|
if IsComplexEnough(string(buffer)) && string(buffer[0]) != " " && string(buffer[n-1]) != " " {
|
||||||
|
return string(buffer), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -146,6 +146,7 @@ var (
|
||||||
MinPasswordLength int
|
MinPasswordLength int
|
||||||
ImportLocalPaths bool
|
ImportLocalPaths bool
|
||||||
DisableGitHooks bool
|
DisableGitHooks bool
|
||||||
|
PasswordComplexity map[string]string
|
||||||
PasswordHashAlgo string
|
PasswordHashAlgo string
|
||||||
|
|
||||||
// UI settings
|
// UI settings
|
||||||
|
@ -774,6 +775,27 @@ func NewContext() {
|
||||||
|
|
||||||
InternalToken = loadInternalToken(sec)
|
InternalToken = loadInternalToken(sec)
|
||||||
|
|
||||||
|
var dictPC = map[string]string{
|
||||||
|
"lower": "[a-z]+",
|
||||||
|
"upper": "[A-Z]+",
|
||||||
|
"digit": "[0-9]+",
|
||||||
|
"spec": `][ !"#$%&'()*+,./:;<=>?@\\^_{|}~` + "`-",
|
||||||
|
}
|
||||||
|
PasswordComplexity = make(map[string]string)
|
||||||
|
cfgdata := sec.Key("PASSWORD_COMPLEXITY").Strings(",")
|
||||||
|
for _, y := range cfgdata {
|
||||||
|
ts := strings.TrimSpace(y)
|
||||||
|
for a := range dictPC {
|
||||||
|
if strings.ToLower(ts) == a {
|
||||||
|
PasswordComplexity[ts] = dictPC[ts]
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(PasswordComplexity) == 0 {
|
||||||
|
PasswordComplexity = dictPC
|
||||||
|
}
|
||||||
|
|
||||||
sec = Cfg.Section("attachment")
|
sec = Cfg.Section("attachment")
|
||||||
AttachmentPath = sec.Key("PATH").MustString(path.Join(AppDataPath, "attachments"))
|
AttachmentPath = sec.Key("PATH").MustString(path.Join(AppDataPath, "attachments"))
|
||||||
if !filepath.IsAbs(AttachmentPath) {
|
if !filepath.IsAbs(AttachmentPath) {
|
||||||
|
|
|
@ -315,6 +315,7 @@ team_no_units_error = Allow access to at least one repository section.
|
||||||
email_been_used = The email address is already used.
|
email_been_used = The email address is already used.
|
||||||
openid_been_used = The OpenID address '%s' is already used.
|
openid_been_used = The OpenID address '%s' is already used.
|
||||||
username_password_incorrect = Username or password is incorrect.
|
username_password_incorrect = Username or password is incorrect.
|
||||||
|
password_complexity = Password does not pass complexity requirements.
|
||||||
enterred_invalid_repo_name = The repository name you entered is incorrect.
|
enterred_invalid_repo_name = The repository name you entered is incorrect.
|
||||||
enterred_invalid_owner_name = The new owner name is not valid.
|
enterred_invalid_owner_name = The new owner name is not valid.
|
||||||
enterred_invalid_password = The password you entered is incorrect.
|
enterred_invalid_password = The password you entered is incorrect.
|
||||||
|
|
|
@ -12,6 +12,7 @@ import (
|
||||||
"code.gitea.io/gitea/modules/base"
|
"code.gitea.io/gitea/modules/base"
|
||||||
"code.gitea.io/gitea/modules/context"
|
"code.gitea.io/gitea/modules/context"
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
|
"code.gitea.io/gitea/modules/password"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
"code.gitea.io/gitea/routers"
|
"code.gitea.io/gitea/routers"
|
||||||
"code.gitea.io/gitea/services/mailer"
|
"code.gitea.io/gitea/services/mailer"
|
||||||
|
@ -94,7 +95,10 @@ func NewUserPost(ctx *context.Context, form auth.AdminCreateUserForm) {
|
||||||
u.LoginName = form.LoginName
|
u.LoginName = form.LoginName
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if !password.IsComplexEnough(form.Password) {
|
||||||
|
ctx.RenderWithErr(ctx.Tr("form.password_complexity"), tplUserNew, &form)
|
||||||
|
return
|
||||||
|
}
|
||||||
if err := models.CreateUser(u); err != nil {
|
if err := models.CreateUser(u); err != nil {
|
||||||
switch {
|
switch {
|
||||||
case models.IsErrUserAlreadyExist(err):
|
case models.IsErrUserAlreadyExist(err):
|
||||||
|
@ -201,6 +205,10 @@ func EditUserPost(ctx *context.Context, form auth.AdminEditUserForm) {
|
||||||
ctx.ServerError("UpdateUser", err)
|
ctx.ServerError("UpdateUser", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if !password.IsComplexEnough(form.Password) {
|
||||||
|
ctx.RenderWithErr(ctx.Tr("form.password_complexity"), tplUserEdit, &form)
|
||||||
|
return
|
||||||
|
}
|
||||||
u.HashPassword(form.Password)
|
u.HashPassword(form.Password)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,9 +6,12 @@
|
||||||
package admin
|
package admin
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
"code.gitea.io/gitea/models"
|
||||||
"code.gitea.io/gitea/modules/context"
|
"code.gitea.io/gitea/modules/context"
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
|
"code.gitea.io/gitea/modules/password"
|
||||||
api "code.gitea.io/gitea/modules/structs"
|
api "code.gitea.io/gitea/modules/structs"
|
||||||
"code.gitea.io/gitea/routers/api/v1/convert"
|
"code.gitea.io/gitea/routers/api/v1/convert"
|
||||||
"code.gitea.io/gitea/routers/api/v1/user"
|
"code.gitea.io/gitea/routers/api/v1/user"
|
||||||
|
@ -73,7 +76,11 @@ func CreateUser(ctx *context.APIContext, form api.CreateUserOption) {
|
||||||
if ctx.Written() {
|
if ctx.Written() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if !password.IsComplexEnough(form.Password) {
|
||||||
|
err := errors.New("PasswordComplexity")
|
||||||
|
ctx.Error(400, "PasswordComplexity", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
if err := models.CreateUser(u); err != nil {
|
if err := models.CreateUser(u); err != nil {
|
||||||
if models.IsErrUserAlreadyExist(err) ||
|
if models.IsErrUserAlreadyExist(err) ||
|
||||||
models.IsErrEmailAlreadyUsed(err) ||
|
models.IsErrEmailAlreadyUsed(err) ||
|
||||||
|
@ -131,6 +138,11 @@ func EditUser(ctx *context.APIContext, form api.EditUserOption) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(form.Password) > 0 {
|
if len(form.Password) > 0 {
|
||||||
|
if !password.IsComplexEnough(form.Password) {
|
||||||
|
err := errors.New("PasswordComplexity")
|
||||||
|
ctx.Error(400, "PasswordComplexity", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
var err error
|
var err error
|
||||||
if u.Salt, err = models.GetUserSalt(); err != nil {
|
if u.Salt, err = models.GetUserSalt(); err != nil {
|
||||||
ctx.Error(500, "UpdateUser", err)
|
ctx.Error(500, "UpdateUser", err)
|
||||||
|
|
|
@ -17,6 +17,7 @@ import (
|
||||||
"code.gitea.io/gitea/modules/base"
|
"code.gitea.io/gitea/modules/base"
|
||||||
"code.gitea.io/gitea/modules/context"
|
"code.gitea.io/gitea/modules/context"
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
|
"code.gitea.io/gitea/modules/password"
|
||||||
"code.gitea.io/gitea/modules/recaptcha"
|
"code.gitea.io/gitea/modules/recaptcha"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
"code.gitea.io/gitea/modules/timeutil"
|
"code.gitea.io/gitea/modules/timeutil"
|
||||||
|
@ -1334,6 +1335,11 @@ func ResetPasswdPost(ctx *context.Context) {
|
||||||
ctx.Data["Err_Password"] = true
|
ctx.Data["Err_Password"] = true
|
||||||
ctx.RenderWithErr(ctx.Tr("auth.password_too_short", setting.MinPasswordLength), tplResetPassword, nil)
|
ctx.RenderWithErr(ctx.Tr("auth.password_too_short", setting.MinPasswordLength), tplResetPassword, nil)
|
||||||
return
|
return
|
||||||
|
} else if !password.IsComplexEnough(passwd) {
|
||||||
|
ctx.Data["IsResetForm"] = true
|
||||||
|
ctx.Data["Err_Password"] = true
|
||||||
|
ctx.RenderWithErr(ctx.Tr("form.password_complexity"), tplResetPassword, nil)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
|
@ -1364,7 +1370,6 @@ func ResetPasswdPost(ctx *context.Context) {
|
||||||
func MustChangePassword(ctx *context.Context) {
|
func MustChangePassword(ctx *context.Context) {
|
||||||
ctx.Data["Title"] = ctx.Tr("auth.must_change_password")
|
ctx.Data["Title"] = ctx.Tr("auth.must_change_password")
|
||||||
ctx.Data["ChangePasscodeLink"] = setting.AppSubURL + "/user/settings/change_password"
|
ctx.Data["ChangePasscodeLink"] = setting.AppSubURL + "/user/settings/change_password"
|
||||||
|
|
||||||
ctx.HTML(200, tplMustChangePassword)
|
ctx.HTML(200, tplMustChangePassword)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1372,16 +1377,12 @@ func MustChangePassword(ctx *context.Context) {
|
||||||
// account was created by an admin
|
// account was created by an admin
|
||||||
func MustChangePasswordPost(ctx *context.Context, cpt *captcha.Captcha, form auth.MustChangePasswordForm) {
|
func MustChangePasswordPost(ctx *context.Context, cpt *captcha.Captcha, form auth.MustChangePasswordForm) {
|
||||||
ctx.Data["Title"] = ctx.Tr("auth.must_change_password")
|
ctx.Data["Title"] = ctx.Tr("auth.must_change_password")
|
||||||
|
|
||||||
ctx.Data["ChangePasscodeLink"] = setting.AppSubURL + "/user/settings/change_password"
|
ctx.Data["ChangePasscodeLink"] = setting.AppSubURL + "/user/settings/change_password"
|
||||||
|
|
||||||
if ctx.HasError() {
|
if ctx.HasError() {
|
||||||
ctx.HTML(200, tplMustChangePassword)
|
ctx.HTML(200, tplMustChangePassword)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
u := ctx.User
|
u := ctx.User
|
||||||
|
|
||||||
// Make sure only requests for users who are eligible to change their password via
|
// Make sure only requests for users who are eligible to change their password via
|
||||||
// this method passes through
|
// this method passes through
|
||||||
if !u.MustChangePassword {
|
if !u.MustChangePassword {
|
||||||
|
|
|
@ -13,6 +13,7 @@ import (
|
||||||
"code.gitea.io/gitea/modules/base"
|
"code.gitea.io/gitea/modules/base"
|
||||||
"code.gitea.io/gitea/modules/context"
|
"code.gitea.io/gitea/modules/context"
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
|
"code.gitea.io/gitea/modules/password"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
"code.gitea.io/gitea/modules/timeutil"
|
"code.gitea.io/gitea/modules/timeutil"
|
||||||
"code.gitea.io/gitea/services/mailer"
|
"code.gitea.io/gitea/services/mailer"
|
||||||
|
@ -52,6 +53,8 @@ func AccountPost(ctx *context.Context, form auth.ChangePasswordForm) {
|
||||||
ctx.Flash.Error(ctx.Tr("settings.password_incorrect"))
|
ctx.Flash.Error(ctx.Tr("settings.password_incorrect"))
|
||||||
} else if form.Password != form.Retype {
|
} else if form.Password != form.Retype {
|
||||||
ctx.Flash.Error(ctx.Tr("form.password_not_match"))
|
ctx.Flash.Error(ctx.Tr("form.password_not_match"))
|
||||||
|
} else if !password.IsComplexEnough(form.Password) {
|
||||||
|
ctx.Flash.Error(ctx.Tr("settings.password_complexity"))
|
||||||
} else {
|
} else {
|
||||||
var err error
|
var err error
|
||||||
if ctx.User.Salt, err = models.GetUserSalt(); err != nil {
|
if ctx.User.Salt, err = models.GetUserSalt(); err != nil {
|
||||||
|
|
|
@ -19,36 +19,77 @@ import (
|
||||||
func TestChangePassword(t *testing.T) {
|
func TestChangePassword(t *testing.T) {
|
||||||
oldPassword := "password"
|
oldPassword := "password"
|
||||||
setting.MinPasswordLength = 6
|
setting.MinPasswordLength = 6
|
||||||
|
setting.PasswordComplexity = map[string]string{
|
||||||
|
"lower": "[a-z]+",
|
||||||
|
"upper": "[A-Z]+",
|
||||||
|
"digit": "[0-9]+",
|
||||||
|
"spec": "[-_]+",
|
||||||
|
}
|
||||||
|
var pcLUN = map[string]string{
|
||||||
|
"lower": "[a-z]+",
|
||||||
|
"upper": "[A-Z]+",
|
||||||
|
"digit": "[0-9]+",
|
||||||
|
}
|
||||||
|
var pcLU = map[string]string{
|
||||||
|
"lower": "[a-z]+",
|
||||||
|
"upper": "[A-Z]+",
|
||||||
|
}
|
||||||
|
|
||||||
for _, req := range []struct {
|
for _, req := range []struct {
|
||||||
OldPassword string
|
OldPassword string
|
||||||
NewPassword string
|
NewPassword string
|
||||||
Retype string
|
Retype string
|
||||||
Message string
|
Message string
|
||||||
|
PasswordComplexity map[string]string
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
OldPassword: oldPassword,
|
OldPassword: oldPassword,
|
||||||
NewPassword: "123456",
|
NewPassword: "Qwerty123456-",
|
||||||
Retype: "123456",
|
Retype: "Qwerty123456-",
|
||||||
Message: "",
|
Message: "",
|
||||||
|
PasswordComplexity: setting.PasswordComplexity,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
OldPassword: oldPassword,
|
OldPassword: oldPassword,
|
||||||
NewPassword: "12345",
|
NewPassword: "12345",
|
||||||
Retype: "12345",
|
Retype: "12345",
|
||||||
Message: "auth.password_too_short",
|
Message: "auth.password_too_short",
|
||||||
|
PasswordComplexity: setting.PasswordComplexity,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
OldPassword: "12334",
|
OldPassword: "12334",
|
||||||
NewPassword: "123456",
|
NewPassword: "123456",
|
||||||
Retype: "123456",
|
Retype: "123456",
|
||||||
Message: "settings.password_incorrect",
|
Message: "settings.password_incorrect",
|
||||||
|
PasswordComplexity: setting.PasswordComplexity,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
OldPassword: oldPassword,
|
OldPassword: oldPassword,
|
||||||
NewPassword: "123456",
|
NewPassword: "123456",
|
||||||
Retype: "12345",
|
Retype: "12345",
|
||||||
Message: "form.password_not_match",
|
Message: "form.password_not_match",
|
||||||
|
PasswordComplexity: setting.PasswordComplexity,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
OldPassword: oldPassword,
|
||||||
|
NewPassword: "Qwerty",
|
||||||
|
Retype: "Qwerty",
|
||||||
|
Message: "settings.password_complexity",
|
||||||
|
PasswordComplexity: setting.PasswordComplexity,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
OldPassword: oldPassword,
|
||||||
|
NewPassword: "Qwerty",
|
||||||
|
Retype: "Qwerty",
|
||||||
|
Message: "settings.password_complexity",
|
||||||
|
PasswordComplexity: pcLUN,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
OldPassword: oldPassword,
|
||||||
|
NewPassword: "QWERTY",
|
||||||
|
Retype: "QWERTY",
|
||||||
|
Message: "settings.password_complexity",
|
||||||
|
PasswordComplexity: pcLU,
|
||||||
},
|
},
|
||||||
} {
|
} {
|
||||||
models.PrepareTestEnv(t)
|
models.PrepareTestEnv(t)
|
||||||
|
|
Loading…
Reference in a new issue