diff options
author | Lanre Adelowo <adelowomailbox@gmail.com> | 2018-09-13 13:04:25 +0100 |
---|---|---|
committer | Lauris BH <lauris@nix.lv> | 2018-09-13 15:04:25 +0300 |
commit | 126ba796dcc9ccdf9c25ed7d441786478be2825b (patch) | |
tree | 63f0ceb0a89495cd86cf664b9ceba6b4cdca589b /routers | |
parent | 10a2a904d7938e26f6d64fe9a9788185b802d4df (diff) | |
download | gitea-126ba796dcc9ccdf9c25ed7d441786478be2825b.tar.gz gitea-126ba796dcc9ccdf9c25ed7d441786478be2825b.zip |
Force user to change password (#4489)
* redirect to login page after successfully activating account
* force users to change password if account was created by an admin
* force users to change password if account was created by an admin
* fixed build
* fixed build
* fix pending issues with translation and wrong routes
* make sure path check is safe
* remove unneccessary newline
* make sure users that don't have to view the form get redirected
* move route to use /settings prefix so as to make sure unauthenticated users can't view the page
* update as per @lafriks review
* add necessary comment
* remove unrelated changes
* support redirecting to location the user actually want to go to before being forced to change his/her password
* run make fmt
* added tests
* improve assertions
* add assertion
* fix copyright year
Signed-off-by: Lanre Adelowo <yo@lanre.wtf>
Diffstat (limited to 'routers')
-rw-r--r-- | routers/admin/main_test.go | 16 | ||||
-rw-r--r-- | routers/admin/users.go | 11 | ||||
-rw-r--r-- | routers/admin/users_test.go | 50 | ||||
-rw-r--r-- | routers/routes/routes.go | 2 | ||||
-rw-r--r-- | routers/user/auth.go | 73 |
5 files changed, 146 insertions, 6 deletions
diff --git a/routers/admin/main_test.go b/routers/admin/main_test.go new file mode 100644 index 0000000000..9a7191d471 --- /dev/null +++ b/routers/admin/main_test.go @@ -0,0 +1,16 @@ +// Copyright 2018 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 admin + +import ( + "path/filepath" + "testing" + + "code.gitea.io/gitea/models" +) + +func TestMain(m *testing.M) { + models.MainTest(m, filepath.Join("..", "..")) +} diff --git a/routers/admin/users.go b/routers/admin/users.go index 9aa78db103..ae8882ac12 100644 --- a/routers/admin/users.go +++ b/routers/admin/users.go @@ -77,11 +77,12 @@ func NewUserPost(ctx *context.Context, form auth.AdminCreateUserForm) { } u := &models.User{ - Name: form.UserName, - Email: form.Email, - Passwd: form.Password, - IsActive: true, - LoginType: models.LoginPlain, + Name: form.UserName, + Email: form.Email, + Passwd: form.Password, + IsActive: true, + LoginType: models.LoginPlain, + MustChangePassword: true, } if len(form.LoginType) > 0 { diff --git a/routers/admin/users_test.go b/routers/admin/users_test.go new file mode 100644 index 0000000000..8f6859940d --- /dev/null +++ b/routers/admin/users_test.go @@ -0,0 +1,50 @@ +// Copyright 2017 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 admin + +import ( + "testing" + + "code.gitea.io/gitea/models" + "code.gitea.io/gitea/modules/auth" + "code.gitea.io/gitea/modules/test" + "github.com/stretchr/testify/assert" +) + +func TestNewUserPost_MustChangePassword(t *testing.T) { + + models.PrepareTestEnv(t) + ctx := test.MockContext(t, "admin/users/new") + + u := models.AssertExistsAndLoadBean(t, &models.User{ + IsAdmin: true, + ID: 2, + }).(*models.User) + + ctx.User = u + + username := "gitea" + email := "gitea@gitea.io" + + form := auth.AdminCreateUserForm{ + LoginType: "local", + LoginName: "local", + UserName: username, + Email: email, + Password: "xxxxxxxx", + SendNotify: false, + } + + NewUserPost(ctx, form) + + assert.NotEmpty(t, ctx.Flash.SuccessMsg) + + u, err := models.GetUserByName(username) + + assert.NoError(t, err) + assert.Equal(t, username, u.Name) + assert.Equal(t, email, u.Email) + assert.True(t, u.MustChangePassword) +} diff --git a/routers/routes/routes.go b/routers/routes/routes.go index e5476fd227..bc4879b51a 100644 --- a/routers/routes/routes.go +++ b/routers/routes/routes.go @@ -230,6 +230,8 @@ func RegisterRoutes(m *macaron.Macaron) { m.Group("/user/settings", func() { m.Get("", userSetting.Profile) m.Post("", bindIgnErr(auth.UpdateProfileForm{}), userSetting.ProfilePost) + m.Get("/change_password", user.MustChangePassword) + m.Post("/change_password", bindIgnErr(auth.MustChangePasswordForm{}), user.MustChangePasswordPost) m.Post("/avatar", binding.MultipartForm(auth.AvatarForm{}), userSetting.AvatarPost) m.Post("/avatar/delete", userSetting.DeleteAvatar) m.Group("/account", func() { diff --git a/routers/user/auth.go b/routers/user/auth.go index da4663f452..a4a0ee3e6a 100644 --- a/routers/user/auth.go +++ b/routers/user/auth.go @@ -28,6 +28,8 @@ import ( ) const ( + // tplMustChangePassword template for updating a user's password + tplMustChangePassword = "user/auth/change_passwd" // tplSignIn template for sign in page tplSignIn base.TplName = "user/auth/signin" // tplSignUp template path for sign up page @@ -1178,7 +1180,8 @@ func ResetPasswdPost(ctx *context.Context) { return } u.HashPassword(passwd) - if err := models.UpdateUserCols(u, "passwd", "rands", "salt"); err != nil { + u.MustChangePassword = false + if err := models.UpdateUserCols(u, "must_change_password", "passwd", "rands", "salt"); err != nil { ctx.ServerError("UpdateUser", err) return } @@ -1191,3 +1194,71 @@ func ResetPasswdPost(ctx *context.Context) { ctx.Data["IsResetFailed"] = true ctx.HTML(200, tplResetPassword) } + +// MustChangePassword renders the page to change a user's password +func MustChangePassword(ctx *context.Context) { + ctx.Data["Title"] = ctx.Tr("auth.must_change_password") + ctx.Data["ChangePasscodeLink"] = setting.AppSubURL + "/user/settings/change_password" + + ctx.HTML(200, tplMustChangePassword) +} + +// MustChangePasswordPost response for updating a user's password after his/her +// account was created by an admin +func MustChangePasswordPost(ctx *context.Context, cpt *captcha.Captcha, form auth.MustChangePasswordForm) { + ctx.Data["Title"] = ctx.Tr("auth.must_change_password") + + ctx.Data["ChangePasscodeLink"] = setting.AppSubURL + "/user/settings/change_password" + + if ctx.HasError() { + ctx.HTML(200, tplMustChangePassword) + return + } + + u := ctx.User + + // Make sure only requests for users who are eligible to change their password via + // this method passes through + if !u.MustChangePassword { + ctx.ServerError("MustUpdatePassword", errors.New("cannot update password.. Please visit the settings page")) + return + } + + if form.Password != form.Retype { + ctx.Data["Err_Password"] = true + ctx.RenderWithErr(ctx.Tr("form.password_not_match"), tplMustChangePassword, &form) + return + } + + if len(form.Password) < setting.MinPasswordLength { + ctx.Data["Err_Password"] = true + ctx.RenderWithErr(ctx.Tr("auth.password_too_short", setting.MinPasswordLength), tplMustChangePassword, &form) + return + } + + var err error + if u.Salt, err = models.GetUserSalt(); err != nil { + ctx.ServerError("UpdateUser", err) + return + } + + u.HashPassword(form.Password) + u.MustChangePassword = false + + if err := models.UpdateUserCols(u, "must_change_password", "passwd", "salt"); err != nil { + ctx.ServerError("UpdateUser", err) + return + } + + ctx.Flash.Success(ctx.Tr("settings.change_password_success")) + + log.Trace("User updated password: %s", u.Name) + + if redirectTo, _ := url.QueryUnescape(ctx.GetCookie("redirect_to")); len(redirectTo) > 0 && !util.IsExternalURL(redirectTo) { + ctx.SetCookie("redirect_to", "", -1, setting.AppSubURL) + ctx.RedirectToFirst(redirectTo) + return + } + + ctx.Redirect(setting.AppSubURL + "/") +} |