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>
This commit is contained in:
parent
10a2a904d7
commit
126ba796dc
13 changed files with 255 additions and 22 deletions
16
routers/admin/main_test.go
Normal file
16
routers/admin/main_test.go
Normal file
|
@ -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("..", ".."))
|
||||
}
|
|
@ -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 {
|
||||
|
|
50
routers/admin/users_test.go
Normal file
50
routers/admin/users_test.go
Normal file
|
@ -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)
|
||||
}
|
|
@ -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() {
|
||||
|
|
|
@ -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 + "/")
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue