| @@ -176,6 +176,8 @@ var migrations = []Migration{ | |||||
| NewMigration("add is_fsck_enabled column for repos", addFsckEnabledToRepo), | NewMigration("add is_fsck_enabled column for repos", addFsckEnabledToRepo), | ||||
| // v61 -> v62 | // v61 -> v62 | ||||
| NewMigration("add size column for attachments", addSizeToAttachment), | NewMigration("add size column for attachments", addSizeToAttachment), | ||||
| // v62 -> v63 | |||||
| NewMigration("add last used passcode column for TOTP", addLastUsedPasscodeTOTP), | |||||
| } | } | ||||
| // Migrate database to current version | // Migrate database to current version | ||||
| @@ -0,0 +1,22 @@ | |||||
| // 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 migrations | |||||
| import ( | |||||
| "fmt" | |||||
| "github.com/go-xorm/xorm" | |||||
| ) | |||||
| func addLastUsedPasscodeTOTP(x *xorm.Engine) error { | |||||
| type TwoFactor struct { | |||||
| LastUsedPasscode string `xorm:"VARCHAR(10)"` | |||||
| } | |||||
| if err := x.Sync2(new(TwoFactor)); err != nil { | |||||
| return fmt.Errorf("Sync2: %v", err) | |||||
| } | |||||
| return nil | |||||
| } | |||||
| @@ -23,12 +23,13 @@ import ( | |||||
| // TwoFactor represents a two-factor authentication token. | // TwoFactor represents a two-factor authentication token. | ||||
| type TwoFactor struct { | type TwoFactor struct { | ||||
| ID int64 `xorm:"pk autoincr"` | |||||
| UID int64 `xorm:"UNIQUE"` | |||||
| Secret string | |||||
| ScratchToken string | |||||
| CreatedUnix util.TimeStamp `xorm:"INDEX created"` | |||||
| UpdatedUnix util.TimeStamp `xorm:"INDEX updated"` | |||||
| ID int64 `xorm:"pk autoincr"` | |||||
| UID int64 `xorm:"UNIQUE"` | |||||
| Secret string | |||||
| ScratchToken string | |||||
| LastUsedPasscode string `xorm:"VARCHAR(10)"` | |||||
| CreatedUnix util.TimeStamp `xorm:"INDEX created"` | |||||
| UpdatedUnix util.TimeStamp `xorm:"INDEX updated"` | |||||
| } | } | ||||
| // GenerateScratchToken recreates the scratch token the user is using. | // GenerateScratchToken recreates the scratch token the user is using. | ||||
| @@ -221,7 +221,7 @@ func TwoFactorPost(ctx *context.Context, form auth.TwoFactorAuthForm) { | |||||
| return | return | ||||
| } | } | ||||
| if ok { | |||||
| if ok && twofa.LastUsedPasscode != form.Passcode { | |||||
| remember := ctx.Session.Get("twofaRemember").(bool) | remember := ctx.Session.Get("twofaRemember").(bool) | ||||
| u, err := models.GetUserByID(id) | u, err := models.GetUserByID(id) | ||||
| if err != nil { | if err != nil { | ||||
| @@ -243,6 +243,12 @@ func TwoFactorPost(ctx *context.Context, form auth.TwoFactorAuthForm) { | |||||
| } | } | ||||
| } | } | ||||
| twofa.LastUsedPasscode = form.Passcode | |||||
| if err = models.UpdateTwoFactor(twofa); err != nil { | |||||
| ctx.ServerError("UserSignIn", err) | |||||
| return | |||||
| } | |||||
| handleSignIn(ctx, u, remember) | handleSignIn(ctx, u, remember) | ||||
| return | return | ||||
| } | } | ||||