Skip to content

Commit a93b4ef

Browse files
author
AJ ONeal
committed
handle reset password edge cases properly and consistently
1 parent 4e0b451 commit a93b4ef

File tree

3 files changed

+77
-49
lines changed

3 files changed

+77
-49
lines changed

options/locale/locale_en-US.ini

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,8 @@ email_not_associate = The email address is not associated with any account.
218218
send_reset_mail = Click here to resend your password reset email
219219
reset_password = Reset Your Password
220220
invalid_code = Your confirmation code is invalid or has expired.
221-
reset_password_helper = Click here to reset your password
221+
reset_password_helper = Reset Password
222+
reset_password_wrong_user = You are signed in as %s, but the password reset link is for %s
222223
password_too_short = Password length cannot be less than %d characters.
223224
non_local_account = Non-local users can not update their password through the Gitea web interface.
224225
verify = Verify

routers/user/auth.go

Lines changed: 60 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1178,77 +1178,89 @@ func ForgotPasswdPost(ctx *context.Context) {
11781178
ctx.HTML(200, tplForgotPassword)
11791179
}
11801180

1181-
// ResetPasswd render the reset password page
1182-
func ResetPasswd(ctx *context.Context) {
1181+
func commonResetPassword(ctx *context.Context) *models.User {
1182+
code := ctx.Query("code")
1183+
11831184
ctx.Data["Title"] = ctx.Tr("auth.reset_password")
1185+
ctx.Data["Code"] = code
11841186

1185-
// TODO for security and convenience, show the username / email here
1187+
if nil != ctx.User {
1188+
ctx.Data["user_signed_in"] = true
1189+
}
11861190

1187-
code := ctx.Query("code")
11881191
if len(code) == 0 {
1189-
ctx.Error(404)
1190-
return
1192+
ctx.Flash.Error(ctx.Tr("auth.invalid_code"))
1193+
return nil
11911194
}
1192-
ctx.Data["Code"] = code
11931195

1194-
if u := models.VerifyUserActiveCode(code); u != nil {
1195-
ctx.Data["IsResetForm"] = true
1196+
// Fail early, don't frustrate the user
1197+
u := models.VerifyUserActiveCode(code)
1198+
if u == nil {
1199+
ctx.Flash.Error(ctx.Tr("auth.invalid_code"))
1200+
return nil
11961201
}
11971202

1203+
// Show the user that they are affecting the account that they intended to
1204+
ctx.Data["user_email"] = u.Email
1205+
1206+
if nil != ctx.User && u.ID != ctx.User.ID {
1207+
ctx.Flash.Error(ctx.Tr("auth.reset_password_wrong_user", ctx.User.Email, u.Email))
1208+
return nil
1209+
}
1210+
1211+
return u
1212+
}
1213+
1214+
// ResetPasswd render the reset password page
1215+
func ResetPasswd(ctx *context.Context) {
1216+
ctx.Data["IsResetForm"] = true
1217+
1218+
_ = commonResetPassword(ctx)
1219+
11981220
ctx.HTML(200, tplResetPassword)
11991221
}
12001222

12011223
// ResetPasswdPost response from reset password request
12021224
func ResetPasswdPost(ctx *context.Context) {
1203-
ctx.Data["Title"] = ctx.Tr("auth.reset_password")
1225+
u := commonResetPassword(ctx)
12041226

1205-
code := ctx.Query("code")
1206-
if len(code) == 0 {
1207-
ctx.Error(404)
1227+
if u == nil {
1228+
// Flash error has been set
1229+
ctx.HTML(200, tplResetPassword)
12081230
return
12091231
}
1210-
ctx.Data["Code"] = code
12111232

1212-
if u := models.VerifyUserActiveCode(code); u != nil {
1213-
// Validate password length.
1214-
passwd := ctx.Query("password")
1215-
if len(passwd) < setting.MinPasswordLength {
1216-
ctx.Data["IsResetForm"] = true
1217-
ctx.Data["Err_Password"] = true
1218-
ctx.RenderWithErr(ctx.Tr("auth.password_too_short", setting.MinPasswordLength), tplResetPassword, nil)
1219-
return
1220-
}
1221-
1222-
var err error
1223-
if u.Rands, err = models.GetUserSalt(); err != nil {
1224-
ctx.ServerError("UpdateUser", err)
1225-
return
1226-
}
1227-
if u.Salt, err = models.GetUserSalt(); err != nil {
1228-
ctx.ServerError("UpdateUser", err)
1229-
return
1230-
}
1231-
1232-
// Just in case the user is signed in to another account
1233-
handleSignOut(ctx)
1234-
1235-
u.HashPassword(passwd)
1236-
u.MustChangePassword = false
1237-
if err := models.UpdateUserCols(u, "must_change_password", "passwd", "rands", "salt"); err != nil {
1238-
ctx.ServerError("UpdateUser", err)
1239-
return
1240-
}
1233+
// Validate password length.
1234+
passwd := ctx.Query("password")
1235+
if len(passwd) < setting.MinPasswordLength {
1236+
ctx.Data["IsResetForm"] = true
1237+
ctx.Data["Err_Password"] = true
1238+
ctx.RenderWithErr(ctx.Tr("auth.password_too_short", setting.MinPasswordLength), tplResetPassword, nil)
1239+
return
1240+
}
12411241

1242-
log.Trace("User password reset: %s", u.Name)
1242+
var err error
1243+
if u.Rands, err = models.GetUserSalt(); err != nil {
1244+
ctx.ServerError("UpdateUser", err)
1245+
return
1246+
}
1247+
if u.Salt, err = models.GetUserSalt(); err != nil {
1248+
ctx.ServerError("UpdateUser", err)
1249+
return
1250+
}
12431251

1244-
// TODO change the former form to have password retype and remember me,
1245-
// then sign in here instead of redirecting
1246-
ctx.Redirect(setting.AppSubURL + "/user/login")
1252+
u.HashPassword(passwd)
1253+
u.MustChangePassword = false
1254+
if err := models.UpdateUserCols(u, "must_change_password", "passwd", "rands", "salt"); err != nil {
1255+
ctx.ServerError("UpdateUser", err)
12471256
return
12481257
}
12491258

1259+
log.Trace("User password reset: %s", u.Name)
1260+
12501261
ctx.Data["IsResetFailed"] = true
1251-
ctx.HTML(200, tplResetPassword)
1262+
remember := len(ctx.Query("remember")) != 0
1263+
handleSignInFull(ctx, u, remember, true)
12521264
}
12531265

12541266
// MustChangePassword renders the page to change a user's password

templates/user/auth/reset_passwd.tmpl

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,26 @@
1010
</h2>
1111
<div class="ui attached segment">
1212
{{template "base/alert" .}}
13+
{{if .user_email }}
14+
<div class="inline field">
15+
<label for="user_name">{{.i18n.Tr "email"}}</label>
16+
<input id="user_name" type="text" value="{{ .user_email }}" disabled>
17+
</div>
18+
{{end}}
1319
{{if .IsResetForm}}
1420
<div class="required inline field {{if .Err_Password}}error{{end}}">
1521
<label for="password">{{.i18n.Tr "password"}}</label>
1622
<input id="password" name="password" type="password" value="{{.password}}" autocomplete="off" autofocus required>
1723
</div>
24+
{{if not .user_signed_in}}
25+
<div class="inline field">
26+
<label></label>
27+
<div class="ui checkbox">
28+
<label>{{.i18n.Tr "auth.remember_me"}}</label>
29+
<input name="remember" type="checkbox">
30+
</div>
31+
</div>
32+
{{end}}
1833
<div class="ui divider"></div>
1934
<div class="inline field">
2035
<label></label>

0 commit comments

Comments
 (0)