diff options
-rw-r--r-- | api/authentication.go | 15 | ||||
-rw-r--r-- | api/user.go | 14 | ||||
-rw-r--r-- | api/user_test.go | 56 | ||||
-rw-r--r-- | i18n/en.json | 2 | ||||
-rw-r--r-- | webapp/i18n/en.json | 4 |
5 files changed, 84 insertions, 7 deletions
diff --git a/api/authentication.go b/api/authentication.go index 8170f0a8e..fbfdb2cf4 100644 --- a/api/authentication.go +++ b/api/authentication.go @@ -13,11 +13,24 @@ import ( ) func checkPasswordAndAllCriteria(user *model.User, password string, mfaToken string) *model.AppError { + if err := checkUserAdditionalAuthenticationCriteria(user, mfaToken); err != nil { + return err + } + if err := checkUserPassword(user, password); err != nil { return err } - if err := checkUserAdditionalAuthenticationCriteria(user, mfaToken); err != nil { + return nil +} + +// This to be used for places we check the users password when they are already logged in +func doubleCheckPassword(user *model.User, password string) *model.AppError { + if err := checkUserLoginAttempts(user); err != nil { + return err + } + + if err := checkUserPassword(user, password); err != nil { return err } diff --git a/api/user.go b/api/user.go index 3666bfd7a..c64315440 100644 --- a/api/user.go +++ b/api/user.go @@ -474,7 +474,11 @@ func login(c *Context, w http.ResponseWriter, r *http.Request) { if user, err = authenticateUser(user, password, mfaToken); err != nil { c.LogAuditWithUserId(user.Id, "failure") //c.Err = model.NewLocAppError("login", "api.user.login.invalid_credentials", nil, err.Error()) - c.Err = model.NewLocAppError("login", "api.user.login.invalid_credentials", nil, "") + if err.Id == "api.user.login.not_verified.app_error" { + c.Err = err + } else { + c.Err = model.NewLocAppError("login", "api.user.login.invalid_credentials", nil, "") + } return } @@ -1386,8 +1390,12 @@ func updatePassword(c *Context, w http.ResponseWriter, r *http.Request) { return } - if !model.ComparePassword(user.Password, currentPassword) { - c.Err = model.NewLocAppError("updatePassword", "api.user.update_password.incorrect.app_error", nil, "") + if err := doubleCheckPassword(user, currentPassword); err != nil { + if err.Id == "api.user.check_user_password.invalid.app_error" { + c.Err = model.NewLocAppError("updatePassword", "api.user.update_password.incorrect.app_error", nil, "") + } else { + c.Err = err + } c.Err.StatusCode = http.StatusForbidden return } diff --git a/api/user_test.go b/api/user_test.go index 12390135e..5fc0a99dc 100644 --- a/api/user_test.go +++ b/api/user_test.go @@ -249,6 +249,42 @@ func TestLoginWithDeviceId(t *testing.T) { } } +func TestPasswordGuessLockout(t *testing.T) { + th := Setup().InitBasic() + Client := th.BasicClient + user := th.BasicUser + Client.Must(Client.Logout()) + + enableSignInWithEmail := *utils.Cfg.EmailSettings.EnableSignInWithEmail + passwordAttempts := utils.Cfg.ServiceSettings.MaximumLoginAttempts + defer func() { + *utils.Cfg.EmailSettings.EnableSignInWithEmail = enableSignInWithEmail + utils.Cfg.ServiceSettings.MaximumLoginAttempts = passwordAttempts + }() + *utils.Cfg.EmailSettings.EnableSignInWithEmail = true + utils.Cfg.ServiceSettings.MaximumLoginAttempts = 2 + + // OK to log in + if _, err := Client.Login(user.Username, user.Password); err != nil { + t.Fatal(err) + } + + Client.Must(Client.Logout()) + + // Fail twice + if _, err := Client.Login(user.Email, "notthepassword"); err == nil { + t.Fatal("Shouldn't be able to login with bad password.") + } + if _, err := Client.Login(user.Email, "notthepassword"); err == nil { + t.Fatal("Shouldn't be able to login with bad password.") + } + + // Locked out + if _, err := Client.Login(user.Email, user.Password); err == nil { + t.Fatal("Shouldn't be able to login with password when account is locked out.") + } +} + func TestSessions(t *testing.T) { th := Setup().InitBasic() Client := th.BasicClient @@ -746,6 +782,26 @@ func TestUserUpdatePassword(t *testing.T) { t.Fatal(err) } + // Test lockout + passwordAttempts := utils.Cfg.ServiceSettings.MaximumLoginAttempts + defer func() { + utils.Cfg.ServiceSettings.MaximumLoginAttempts = passwordAttempts + }() + utils.Cfg.ServiceSettings.MaximumLoginAttempts = 2 + + // Fail twice + if _, err := Client.UpdateUserPassword(user.Id, "badpwd", "newpwd"); err == nil { + t.Fatal("Should have errored") + } + if _, err := Client.UpdateUserPassword(user.Id, "badpwd", "newpwd"); err == nil { + t.Fatal("Should have errored") + } + + // Should fail because account is locked out + if _, err := Client.UpdateUserPassword(user.Id, "newpwd1", "newpwd2"); err == nil { + t.Fatal("Should have errored") + } + user2 := &model.User{Email: strings.ToLower(model.NewId()) + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "passwd1"} user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User) LinkUserToTeam(user2, team) diff --git a/i18n/en.json b/i18n/en.json index e8efcf331..4198b82af 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -1793,7 +1793,7 @@ }, { "id": "api.user.login.invalid_credentials", - "translation": "User ID or password incorrect." + "translation": "Your login credentials are incorrect." }, { "id": "api.user.login.not_provided.app_error", diff --git a/webapp/i18n/en.json b/webapp/i18n/en.json index eeb1572f8..f000dbf55 100644 --- a/webapp/i18n/en.json +++ b/webapp/i18n/en.json @@ -286,13 +286,13 @@ "admin.image.previewHeightTitle": "Image Preview Height:", "admin.image.previewWidthDescription": "Maximum width of preview image. Updating this value changes how preview images render in future, but does not change images created in the past.", "admin.image.previewWidthExample": "Ex \"1024\"", - "admin.image.previewWidthTitle": "Profile Picture Width:", + "admin.image.previewWidthTitle": "Image Preview Width:", "admin.image.profileHeightDescription": "Height of profile picture.", "admin.image.profileHeightExample": "Ex \"0\"", "admin.image.profileHeightTitle": "Profile Picture Height:", "admin.image.profileWidthDescription": "Width of profile picture.", "admin.image.profileWidthExample": "Ex \"1024\"", - "admin.image.profileWidthTitle": "Image Preview Width:", + "admin.image.profileWidthTitle": "Profile Picture Width:", "admin.image.publicLinkDescription": "32-character salt added to signing of public image links. Randomly generated on install. Click \"Regenerate\" to create new salt.", "admin.image.publicLinkExample": "Ex \"gxHVDcKUyP2y1eiyW8S8na1UYQAfq6J6\"", "admin.image.publicLinkTitle": "Public Link Salt:", |