@@ -1282,7 +1282,7 @@ func ForgotPasswdPost(ctx *context.Context) {
1282
1282
ctx .HTML (200 , tplForgotPassword )
1283
1283
}
1284
1284
1285
- func commonResetPassword (ctx * context.Context ) * models.User {
1285
+ func commonResetPassword (ctx * context.Context ) ( * models.User , * models. TwoFactor ) {
1286
1286
code := ctx .Query ("code" )
1287
1287
1288
1288
ctx .Data ["Title" ] = ctx .Tr ("auth.reset_password" )
@@ -1294,39 +1294,56 @@ func commonResetPassword(ctx *context.Context) *models.User {
1294
1294
1295
1295
if len (code ) == 0 {
1296
1296
ctx .Flash .Error (ctx .Tr ("auth.invalid_code" ))
1297
- return nil
1297
+ return nil , nil
1298
1298
}
1299
1299
1300
1300
// Fail early, don't frustrate the user
1301
1301
u := models .VerifyUserActiveCode (code )
1302
1302
if u == nil {
1303
1303
ctx .Flash .Error (ctx .Tr ("auth.invalid_code" ))
1304
- return nil
1304
+ return nil , nil
1305
+ }
1306
+
1307
+ twofa , err := models .GetTwoFactorByUID (u .ID )
1308
+ if err != nil {
1309
+ if ! models .IsErrTwoFactorNotEnrolled (err ) {
1310
+ ctx .Error (http .StatusInternalServerError , "CommonResetPassword" , err .Error ())
1311
+ return nil , nil
1312
+ }
1313
+ } else {
1314
+ ctx .Data ["has_two_factor" ] = true
1315
+ ctx .Data ["scratch_code" ] = ctx .QueryBool ("scratch_code" )
1305
1316
}
1306
1317
1307
1318
// Show the user that they are affecting the account that they intended to
1308
1319
ctx .Data ["user_email" ] = u .Email
1309
1320
1310
1321
if nil != ctx .User && u .ID != ctx .User .ID {
1311
1322
ctx .Flash .Error (ctx .Tr ("auth.reset_password_wrong_user" , ctx .User .Email , u .Email ))
1312
- return nil
1323
+ return nil , nil
1313
1324
}
1314
1325
1315
- return u
1326
+ return u , twofa
1316
1327
}
1317
1328
1318
1329
// ResetPasswd render the account recovery page
1319
1330
func ResetPasswd (ctx * context.Context ) {
1320
1331
ctx .Data ["IsResetForm" ] = true
1321
1332
1322
1333
commonResetPassword (ctx )
1334
+ if ctx .Written () {
1335
+ return
1336
+ }
1323
1337
1324
1338
ctx .HTML (200 , tplResetPassword )
1325
1339
}
1326
1340
1327
1341
// ResetPasswdPost response from account recovery request
1328
1342
func ResetPasswdPost (ctx * context.Context ) {
1329
- u := commonResetPassword (ctx )
1343
+ u , twofa := commonResetPassword (ctx )
1344
+ if ctx .Written () {
1345
+ return
1346
+ }
1330
1347
1331
1348
if u == nil {
1332
1349
// Flash error has been set
@@ -1348,6 +1365,39 @@ func ResetPasswdPost(ctx *context.Context) {
1348
1365
return
1349
1366
}
1350
1367
1368
+ // Handle two-factor
1369
+ regenerateScratchToken := false
1370
+ if twofa != nil {
1371
+ if ctx .QueryBool ("scratch_code" ) {
1372
+ if ! twofa .VerifyScratchToken (ctx .Query ("token" )) {
1373
+ ctx .Data ["IsResetForm" ] = true
1374
+ ctx .Data ["Err_Token" ] = true
1375
+ ctx .RenderWithErr (ctx .Tr ("auth.twofa_scratch_token_incorrect" ), tplResetPassword , nil )
1376
+ return
1377
+ }
1378
+ regenerateScratchToken = true
1379
+ } else {
1380
+ passcode := ctx .Query ("passcode" )
1381
+ ok , err := twofa .ValidateTOTP (passcode )
1382
+ if err != nil {
1383
+ ctx .Error (http .StatusInternalServerError , "ValidateTOTP" , err .Error ())
1384
+ return
1385
+ }
1386
+ if ! ok || twofa .LastUsedPasscode == passcode {
1387
+ ctx .Data ["IsResetForm" ] = true
1388
+ ctx .Data ["Err_Passcode" ] = true
1389
+ ctx .RenderWithErr (ctx .Tr ("auth.twofa_passcode_incorrect" ), tplResetPassword , nil )
1390
+ return
1391
+ }
1392
+
1393
+ twofa .LastUsedPasscode = passcode
1394
+ if err = models .UpdateTwoFactor (twofa ); err != nil {
1395
+ ctx .ServerError ("ResetPasswdPost: UpdateTwoFactor" , err )
1396
+ return
1397
+ }
1398
+ }
1399
+ }
1400
+
1351
1401
var err error
1352
1402
if u .Rands , err = models .GetUserSalt (); err != nil {
1353
1403
ctx .ServerError ("UpdateUser" , err )
@@ -1357,7 +1407,6 @@ func ResetPasswdPost(ctx *context.Context) {
1357
1407
ctx .ServerError ("UpdateUser" , err )
1358
1408
return
1359
1409
}
1360
-
1361
1410
u .HashPassword (passwd )
1362
1411
u .MustChangePassword = false
1363
1412
if err := models .UpdateUserCols (u , "must_change_password" , "passwd" , "rands" , "salt" ); err != nil {
@@ -1366,9 +1415,27 @@ func ResetPasswdPost(ctx *context.Context) {
1366
1415
}
1367
1416
1368
1417
log .Trace ("User password reset: %s" , u .Name )
1369
-
1370
1418
ctx .Data ["IsResetFailed" ] = true
1371
1419
remember := len (ctx .Query ("remember" )) != 0
1420
+
1421
+ if regenerateScratchToken {
1422
+ // Invalidate the scratch token.
1423
+ _ , err = twofa .GenerateScratchToken ()
1424
+ if err != nil {
1425
+ ctx .ServerError ("UserSignIn" , err )
1426
+ return
1427
+ }
1428
+ if err = models .UpdateTwoFactor (twofa ); err != nil {
1429
+ ctx .ServerError ("UserSignIn" , err )
1430
+ return
1431
+ }
1432
+
1433
+ handleSignInFull (ctx , u , remember , false )
1434
+ ctx .Flash .Info (ctx .Tr ("auth.twofa_scratch_used" ))
1435
+ ctx .Redirect (setting .AppSubURL + "/user/settings/security" )
1436
+ return
1437
+ }
1438
+
1372
1439
handleSignInFull (ctx , u , remember , true )
1373
1440
}
1374
1441
0 commit comments