mirror of
				https://gitee.com/gitea/gitea
				synced 2025-11-04 16:40:24 +08:00 
			
		
		
		
	Move twofactor to models/login (#17143)
This commit is contained in:
		@@ -1876,25 +1876,6 @@ func (err ErrTeamNotExist) Error() string {
 | 
				
			|||||||
	return fmt.Sprintf("team does not exist [org_id %d, team_id %d, name: %s]", err.OrgID, err.TeamID, err.Name)
 | 
						return fmt.Sprintf("team does not exist [org_id %d, team_id %d, name: %s]", err.OrgID, err.TeamID, err.Name)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// Two-factor authentication
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// ErrTwoFactorNotEnrolled indicates that a user is not enrolled in two-factor authentication.
 | 
					 | 
				
			||||||
type ErrTwoFactorNotEnrolled struct {
 | 
					 | 
				
			||||||
	UID int64
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// IsErrTwoFactorNotEnrolled checks if an error is a ErrTwoFactorNotEnrolled.
 | 
					 | 
				
			||||||
func IsErrTwoFactorNotEnrolled(err error) bool {
 | 
					 | 
				
			||||||
	_, ok := err.(ErrTwoFactorNotEnrolled)
 | 
					 | 
				
			||||||
	return ok
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (err ErrTwoFactorNotEnrolled) Error() string {
 | 
					 | 
				
			||||||
	return fmt.Sprintf("user not enrolled in 2FA [uid: %d]", err.UID)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
//  ____ ___        .__                    .___
 | 
					//  ____ ___        .__                    .___
 | 
				
			||||||
// |    |   \______ |  |   _________     __| _/
 | 
					// |    |   \______ |  |   _________     __| _/
 | 
				
			||||||
// |    |   /\____ \|  |  /  _ \__  \   / __ |
 | 
					// |    |   /\____ \|  |  /  _ \__  \   / __ |
 | 
				
			||||||
@@ -1959,28 +1940,6 @@ func (err ErrExternalLoginUserNotExist) Error() string {
 | 
				
			|||||||
	return fmt.Sprintf("external login user link does not exists [userID: %d, loginSourceID: %d]", err.UserID, err.LoginSourceID)
 | 
						return fmt.Sprintf("external login user link does not exists [userID: %d, loginSourceID: %d]", err.UserID, err.LoginSourceID)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ____ ________________________________              .__          __                 __  .__
 | 
					 | 
				
			||||||
// |    |   \_____  \_   _____/\______   \ ____   ____ |__| _______/  |_____________ _/  |_|__| ____   ____
 | 
					 | 
				
			||||||
// |    |   //  ____/|    __)   |       _// __ \ / ___\|  |/  ___/\   __\_  __ \__  \\   __\  |/  _ \ /    \
 | 
					 | 
				
			||||||
// |    |  //       \|     \    |    |   \  ___// /_/  >  |\___ \  |  |  |  | \// __ \|  | |  (  <_> )   |  \
 | 
					 | 
				
			||||||
// |______/ \_______ \___  /    |____|_  /\___  >___  /|__/____  > |__|  |__|  (____  /__| |__|\____/|___|  /
 | 
					 | 
				
			||||||
// \/   \/            \/     \/_____/         \/                   \/                    \/
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// ErrU2FRegistrationNotExist represents a "ErrU2FRegistrationNotExist" kind of error.
 | 
					 | 
				
			||||||
type ErrU2FRegistrationNotExist struct {
 | 
					 | 
				
			||||||
	ID int64
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (err ErrU2FRegistrationNotExist) Error() string {
 | 
					 | 
				
			||||||
	return fmt.Sprintf("U2F registration does not exist [id: %d]", err.ID)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// IsErrU2FRegistrationNotExist checks if an error is a ErrU2FRegistrationNotExist.
 | 
					 | 
				
			||||||
func IsErrU2FRegistrationNotExist(err error) bool {
 | 
					 | 
				
			||||||
	_, ok := err.(ErrU2FRegistrationNotExist)
 | 
					 | 
				
			||||||
	return ok
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// .___                            ________                                   .___                   .__
 | 
					// .___                            ________                                   .___                   .__
 | 
				
			||||||
// |   | ______ ________ __   ____ \______ \   ____ ______   ____   ____    __| _/____   ____   ____ |__| ____   ______
 | 
					// |   | ______ ________ __   ____ \______ \   ____ ______   ____   ____    __| _/____   ____   ____ |__| ____   ______
 | 
				
			||||||
// |   |/  ___//  ___/  |  \_/ __ \ |    |  \_/ __ \\____ \_/ __ \ /    \  / __ |/ __ \ /    \_/ ___\|  |/ __ \ /  ___/
 | 
					// |   |/  ___//  ___/  |  \_/ __ \ |    |  \_/ __ \\____ \_/ __ \ /    \  / __ |/ __ \ /    \_/ ___\|  |/ __ \ /  ___/
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -17,5 +17,6 @@ func TestMain(m *testing.M) {
 | 
				
			|||||||
		"oauth2_application.yml",
 | 
							"oauth2_application.yml",
 | 
				
			||||||
		"oauth2_authorization_code.yml",
 | 
							"oauth2_authorization_code.yml",
 | 
				
			||||||
		"oauth2_grant.yml",
 | 
							"oauth2_grant.yml",
 | 
				
			||||||
 | 
							"u2f_registration.yml",
 | 
				
			||||||
	)
 | 
						)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,7 +2,7 @@
 | 
				
			|||||||
// Use of this source code is governed by a MIT-style
 | 
					// Use of this source code is governed by a MIT-style
 | 
				
			||||||
// license that can be found in the LICENSE file.
 | 
					// license that can be found in the LICENSE file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
package models
 | 
					package login
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"crypto/md5"
 | 
						"crypto/md5"
 | 
				
			||||||
@@ -21,6 +21,25 @@ import (
 | 
				
			|||||||
	"golang.org/x/crypto/pbkdf2"
 | 
						"golang.org/x/crypto/pbkdf2"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Two-factor authentication
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ErrTwoFactorNotEnrolled indicates that a user is not enrolled in two-factor authentication.
 | 
				
			||||||
 | 
					type ErrTwoFactorNotEnrolled struct {
 | 
				
			||||||
 | 
						UID int64
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// IsErrTwoFactorNotEnrolled checks if an error is a ErrTwoFactorNotEnrolled.
 | 
				
			||||||
 | 
					func IsErrTwoFactorNotEnrolled(err error) bool {
 | 
				
			||||||
 | 
						_, ok := err.(ErrTwoFactorNotEnrolled)
 | 
				
			||||||
 | 
						return ok
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (err ErrTwoFactorNotEnrolled) Error() string {
 | 
				
			||||||
 | 
						return fmt.Sprintf("user not enrolled in 2FA [uid: %d]", err.UID)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 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"`
 | 
						ID               int64 `xorm:"pk autoincr"`
 | 
				
			||||||
@@ -44,11 +63,12 @@ func (t *TwoFactor) GenerateScratchToken() (string, error) {
 | 
				
			|||||||
		return "", err
 | 
							return "", err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	t.ScratchSalt, _ = util.RandomString(10)
 | 
						t.ScratchSalt, _ = util.RandomString(10)
 | 
				
			||||||
	t.ScratchHash = hashToken(token, t.ScratchSalt)
 | 
						t.ScratchHash = HashToken(token, t.ScratchSalt)
 | 
				
			||||||
	return token, nil
 | 
						return token, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func hashToken(token, salt string) string {
 | 
					// HashToken return the hashable salt
 | 
				
			||||||
 | 
					func HashToken(token, salt string) string {
 | 
				
			||||||
	tempHash := pbkdf2.Key([]byte(token), []byte(salt), 10000, 50, sha256.New)
 | 
						tempHash := pbkdf2.Key([]byte(token), []byte(salt), 10000, 50, sha256.New)
 | 
				
			||||||
	return fmt.Sprintf("%x", tempHash)
 | 
						return fmt.Sprintf("%x", tempHash)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -58,7 +78,7 @@ func (t *TwoFactor) VerifyScratchToken(token string) bool {
 | 
				
			|||||||
	if len(token) == 0 {
 | 
						if len(token) == 0 {
 | 
				
			||||||
		return false
 | 
							return false
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	tempHash := hashToken(token, t.ScratchSalt)
 | 
						tempHash := HashToken(token, t.ScratchSalt)
 | 
				
			||||||
	return subtle.ConstantTimeCompare([]byte(t.ScratchHash), []byte(tempHash)) == 1
 | 
						return subtle.ConstantTimeCompare([]byte(t.ScratchHash), []byte(tempHash)) == 1
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -2,9 +2,11 @@
 | 
				
			|||||||
// Use of this source code is governed by a MIT-style
 | 
					// Use of this source code is governed by a MIT-style
 | 
				
			||||||
// license that can be found in the LICENSE file.
 | 
					// license that can be found in the LICENSE file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
package models
 | 
					package login
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"code.gitea.io/gitea/models/db"
 | 
						"code.gitea.io/gitea/models/db"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/log"
 | 
						"code.gitea.io/gitea/modules/log"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/timeutil"
 | 
						"code.gitea.io/gitea/modules/timeutil"
 | 
				
			||||||
@@ -12,6 +14,28 @@ import (
 | 
				
			|||||||
	"github.com/tstranex/u2f"
 | 
						"github.com/tstranex/u2f"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ____ ________________________________              .__          __                 __  .__
 | 
				
			||||||
 | 
					// |    |   \_____  \_   _____/\______   \ ____   ____ |__| _______/  |_____________ _/  |_|__| ____   ____
 | 
				
			||||||
 | 
					// |    |   //  ____/|    __)   |       _// __ \ / ___\|  |/  ___/\   __\_  __ \__  \\   __\  |/  _ \ /    \
 | 
				
			||||||
 | 
					// |    |  //       \|     \    |    |   \  ___// /_/  >  |\___ \  |  |  |  | \// __ \|  | |  (  <_> )   |  \
 | 
				
			||||||
 | 
					// |______/ \_______ \___  /    |____|_  /\___  >___  /|__/____  > |__|  |__|  (____  /__| |__|\____/|___|  /
 | 
				
			||||||
 | 
					// \/   \/            \/     \/_____/         \/                   \/                    \/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ErrU2FRegistrationNotExist represents a "ErrU2FRegistrationNotExist" kind of error.
 | 
				
			||||||
 | 
					type ErrU2FRegistrationNotExist struct {
 | 
				
			||||||
 | 
						ID int64
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (err ErrU2FRegistrationNotExist) Error() string {
 | 
				
			||||||
 | 
						return fmt.Sprintf("U2F registration does not exist [id: %d]", err.ID)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// IsErrU2FRegistrationNotExist checks if an error is a ErrU2FRegistrationNotExist.
 | 
				
			||||||
 | 
					func IsErrU2FRegistrationNotExist(err error) bool {
 | 
				
			||||||
 | 
						_, ok := err.(ErrU2FRegistrationNotExist)
 | 
				
			||||||
 | 
						return ok
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// U2FRegistration represents the registration data and counter of a security key
 | 
					// U2FRegistration represents the registration data and counter of a security key
 | 
				
			||||||
type U2FRegistration struct {
 | 
					type U2FRegistration struct {
 | 
				
			||||||
	ID          int64 `xorm:"pk autoincr"`
 | 
						ID          int64 `xorm:"pk autoincr"`
 | 
				
			||||||
@@ -91,13 +115,13 @@ func GetU2FRegistrationsByUID(uid int64) (U2FRegistrationList, error) {
 | 
				
			|||||||
	return getU2FRegistrationsByUID(db.GetEngine(db.DefaultContext), uid)
 | 
						return getU2FRegistrationsByUID(db.GetEngine(db.DefaultContext), uid)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func createRegistration(e db.Engine, user *User, name string, reg *u2f.Registration) (*U2FRegistration, error) {
 | 
					func createRegistration(e db.Engine, userID int64, name string, reg *u2f.Registration) (*U2FRegistration, error) {
 | 
				
			||||||
	raw, err := reg.MarshalBinary()
 | 
						raw, err := reg.MarshalBinary()
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	r := &U2FRegistration{
 | 
						r := &U2FRegistration{
 | 
				
			||||||
		UserID:  user.ID,
 | 
							UserID:  userID,
 | 
				
			||||||
		Name:    name,
 | 
							Name:    name,
 | 
				
			||||||
		Counter: 0,
 | 
							Counter: 0,
 | 
				
			||||||
		Raw:     raw,
 | 
							Raw:     raw,
 | 
				
			||||||
@@ -110,8 +134,8 @@ func createRegistration(e db.Engine, user *User, name string, reg *u2f.Registrat
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// CreateRegistration will create a new U2FRegistration from the given Registration
 | 
					// CreateRegistration will create a new U2FRegistration from the given Registration
 | 
				
			||||||
func CreateRegistration(user *User, name string, reg *u2f.Registration) (*U2FRegistration, error) {
 | 
					func CreateRegistration(userID int64, name string, reg *u2f.Registration) (*U2FRegistration, error) {
 | 
				
			||||||
	return createRegistration(db.GetEngine(db.DefaultContext), user, name, reg)
 | 
						return createRegistration(db.GetEngine(db.DefaultContext), userID, name, reg)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// DeleteRegistration will delete U2FRegistration
 | 
					// DeleteRegistration will delete U2FRegistration
 | 
				
			||||||
@@ -2,12 +2,13 @@
 | 
				
			|||||||
// Use of this source code is governed by a MIT-style
 | 
					// Use of this source code is governed by a MIT-style
 | 
				
			||||||
// license that can be found in the LICENSE file.
 | 
					// license that can be found in the LICENSE file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
package models
 | 
					package login
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"testing"
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"code.gitea.io/gitea/models/db"
 | 
						"code.gitea.io/gitea/models/db"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/stretchr/testify/assert"
 | 
						"github.com/stretchr/testify/assert"
 | 
				
			||||||
	"github.com/tstranex/u2f"
 | 
						"github.com/tstranex/u2f"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
@@ -55,14 +56,13 @@ func TestU2FRegistration_UpdateLargeCounter(t *testing.T) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
func TestCreateRegistration(t *testing.T) {
 | 
					func TestCreateRegistration(t *testing.T) {
 | 
				
			||||||
	assert.NoError(t, db.PrepareTestDatabase())
 | 
						assert.NoError(t, db.PrepareTestDatabase())
 | 
				
			||||||
	user := db.AssertExistsAndLoadBean(t, &User{ID: 1}).(*User)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	res, err := CreateRegistration(user, "U2F Created Key", &u2f.Registration{Raw: []byte("Test")})
 | 
						res, err := CreateRegistration(1, "U2F Created Key", &u2f.Registration{Raw: []byte("Test")})
 | 
				
			||||||
	assert.NoError(t, err)
 | 
						assert.NoError(t, err)
 | 
				
			||||||
	assert.Equal(t, "U2F Created Key", res.Name)
 | 
						assert.Equal(t, "U2F Created Key", res.Name)
 | 
				
			||||||
	assert.Equal(t, []byte("Test"), res.Raw)
 | 
						assert.Equal(t, []byte("Test"), res.Raw)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	db.AssertExistsIf(t, true, &U2FRegistration{Name: "U2F Created Key", UserID: user.ID})
 | 
						db.AssertExistsIf(t, true, &U2FRegistration{Name: "U2F Created Key", UserID: 1})
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestDeleteRegistration(t *testing.T) {
 | 
					func TestDeleteRegistration(t *testing.T) {
 | 
				
			||||||
@@ -6,6 +6,7 @@ package models
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"code.gitea.io/gitea/models/db"
 | 
						"code.gitea.io/gitea/models/db"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/models/login"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/git"
 | 
						"code.gitea.io/gitea/modules/git"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/log"
 | 
						"code.gitea.io/gitea/modules/log"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/setting"
 | 
						"code.gitea.io/gitea/modules/setting"
 | 
				
			||||||
@@ -44,8 +45,8 @@ Loop:
 | 
				
			|||||||
				return false, "", nil, &ErrWontSign{pubkey}
 | 
									return false, "", nil, &ErrWontSign{pubkey}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		case twofa:
 | 
							case twofa:
 | 
				
			||||||
			twofaModel, err := GetTwoFactorByUID(u.ID)
 | 
								twofaModel, err := login.GetTwoFactorByUID(u.ID)
 | 
				
			||||||
			if err != nil && !IsErrTwoFactorNotEnrolled(err) {
 | 
								if err != nil && !login.IsErrTwoFactorNotEnrolled(err) {
 | 
				
			||||||
				return false, "", nil, err
 | 
									return false, "", nil, err
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			if twofaModel == nil {
 | 
								if twofaModel == nil {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,6 +8,7 @@ import (
 | 
				
			|||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"code.gitea.io/gitea/models/db"
 | 
						"code.gitea.io/gitea/models/db"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/models/login"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/git"
 | 
						"code.gitea.io/gitea/modules/git"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/log"
 | 
						"code.gitea.io/gitea/modules/log"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/process"
 | 
						"code.gitea.io/gitea/modules/process"
 | 
				
			||||||
@@ -129,8 +130,8 @@ Loop:
 | 
				
			|||||||
				return false, "", nil, &ErrWontSign{pubkey}
 | 
									return false, "", nil, &ErrWontSign{pubkey}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		case twofa:
 | 
							case twofa:
 | 
				
			||||||
			twofaModel, err := GetTwoFactorByUID(u.ID)
 | 
								twofaModel, err := login.GetTwoFactorByUID(u.ID)
 | 
				
			||||||
			if err != nil && !IsErrTwoFactorNotEnrolled(err) {
 | 
								if err != nil && !login.IsErrTwoFactorNotEnrolled(err) {
 | 
				
			||||||
				return false, "", nil, err
 | 
									return false, "", nil, err
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			if twofaModel == nil {
 | 
								if twofaModel == nil {
 | 
				
			||||||
@@ -165,8 +166,8 @@ Loop:
 | 
				
			|||||||
				return false, "", nil, &ErrWontSign{pubkey}
 | 
									return false, "", nil, &ErrWontSign{pubkey}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		case twofa:
 | 
							case twofa:
 | 
				
			||||||
			twofaModel, err := GetTwoFactorByUID(u.ID)
 | 
								twofaModel, err := login.GetTwoFactorByUID(u.ID)
 | 
				
			||||||
			if err != nil && !IsErrTwoFactorNotEnrolled(err) {
 | 
								if err != nil && !login.IsErrTwoFactorNotEnrolled(err) {
 | 
				
			||||||
				return false, "", nil, err
 | 
									return false, "", nil, err
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			if twofaModel == nil {
 | 
								if twofaModel == nil {
 | 
				
			||||||
@@ -218,8 +219,8 @@ Loop:
 | 
				
			|||||||
				return false, "", nil, &ErrWontSign{pubkey}
 | 
									return false, "", nil, &ErrWontSign{pubkey}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		case twofa:
 | 
							case twofa:
 | 
				
			||||||
			twofaModel, err := GetTwoFactorByUID(u.ID)
 | 
								twofaModel, err := login.GetTwoFactorByUID(u.ID)
 | 
				
			||||||
			if err != nil && !IsErrTwoFactorNotEnrolled(err) {
 | 
								if err != nil && !login.IsErrTwoFactorNotEnrolled(err) {
 | 
				
			||||||
				return false, "", nil, err
 | 
									return false, "", nil, err
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			if twofaModel == nil {
 | 
								if twofaModel == nil {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -11,6 +11,7 @@ import (
 | 
				
			|||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"code.gitea.io/gitea/models/db"
 | 
						"code.gitea.io/gitea/models/db"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/models/login"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/base"
 | 
						"code.gitea.io/gitea/modules/base"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/setting"
 | 
						"code.gitea.io/gitea/modules/setting"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/timeutil"
 | 
						"code.gitea.io/gitea/modules/timeutil"
 | 
				
			||||||
@@ -67,7 +68,7 @@ func NewAccessToken(t *AccessToken) error {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	t.TokenSalt = salt
 | 
						t.TokenSalt = salt
 | 
				
			||||||
	t.Token = base.EncodeSha1(gouuid.New().String())
 | 
						t.Token = base.EncodeSha1(gouuid.New().String())
 | 
				
			||||||
	t.TokenHash = hashToken(t.Token, t.TokenSalt)
 | 
						t.TokenHash = login.HashToken(t.Token, t.TokenSalt)
 | 
				
			||||||
	t.TokenLastEight = t.Token[len(t.Token)-8:]
 | 
						t.TokenLastEight = t.Token[len(t.Token)-8:]
 | 
				
			||||||
	_, err = db.GetEngine(db.DefaultContext).Insert(t)
 | 
						_, err = db.GetEngine(db.DefaultContext).Insert(t)
 | 
				
			||||||
	return err
 | 
						return err
 | 
				
			||||||
@@ -129,7 +130,7 @@ func GetAccessTokenBySHA(token string) (*AccessToken, error) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for _, t := range tokens {
 | 
						for _, t := range tokens {
 | 
				
			||||||
		tempHash := hashToken(token, t.TokenSalt)
 | 
							tempHash := login.HashToken(token, t.TokenSalt)
 | 
				
			||||||
		if subtle.ConstantTimeCompare([]byte(t.TokenHash), []byte(tempHash)) == 1 {
 | 
							if subtle.ConstantTimeCompare([]byte(t.TokenHash), []byte(tempHash)) == 1 {
 | 
				
			||||||
			if successfulAccessTokenCache != nil {
 | 
								if successfulAccessTokenCache != nil {
 | 
				
			||||||
				successfulAccessTokenCache.Add(token, t.ID)
 | 
									successfulAccessTokenCache.Add(token, t.ID)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,6 +8,7 @@ import (
 | 
				
			|||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"code.gitea.io/gitea/models/db"
 | 
						"code.gitea.io/gitea/models/db"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/models/login"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/log"
 | 
						"code.gitea.io/gitea/modules/log"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -79,13 +80,13 @@ func (users UserList) GetTwoFaStatus() map[int64]bool {
 | 
				
			|||||||
	return results
 | 
						return results
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (users UserList) loadTwoFactorStatus(e db.Engine) (map[int64]*TwoFactor, error) {
 | 
					func (users UserList) loadTwoFactorStatus(e db.Engine) (map[int64]*login.TwoFactor, error) {
 | 
				
			||||||
	if len(users) == 0 {
 | 
						if len(users) == 0 {
 | 
				
			||||||
		return nil, nil
 | 
							return nil, nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	userIDs := users.getUserIDs()
 | 
						userIDs := users.getUserIDs()
 | 
				
			||||||
	tokenMaps := make(map[int64]*TwoFactor, len(userIDs))
 | 
						tokenMaps := make(map[int64]*login.TwoFactor, len(userIDs))
 | 
				
			||||||
	err := e.
 | 
						err := e.
 | 
				
			||||||
		In("uid", userIDs).
 | 
							In("uid", userIDs).
 | 
				
			||||||
		Find(&tokenMaps)
 | 
							Find(&tokenMaps)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,6 +14,7 @@ import (
 | 
				
			|||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"code.gitea.io/gitea/models"
 | 
						"code.gitea.io/gitea/models"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/models/login"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/git"
 | 
						"code.gitea.io/gitea/modules/git"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/log"
 | 
						"code.gitea.io/gitea/modules/log"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/setting"
 | 
						"code.gitea.io/gitea/modules/setting"
 | 
				
			||||||
@@ -219,9 +220,9 @@ func (ctx *APIContext) CheckForOTP() {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	otpHeader := ctx.Req.Header.Get("X-Gitea-OTP")
 | 
						otpHeader := ctx.Req.Header.Get("X-Gitea-OTP")
 | 
				
			||||||
	twofa, err := models.GetTwoFactorByUID(ctx.Context.User.ID)
 | 
						twofa, err := login.GetTwoFactorByUID(ctx.Context.User.ID)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		if models.IsErrTwoFactorNotEnrolled(err) {
 | 
							if login.IsErrTwoFactorNotEnrolled(err) {
 | 
				
			||||||
			return // No 2FA enrollment for this user
 | 
								return // No 2FA enrollment for this user
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		ctx.Context.Error(http.StatusInternalServerError)
 | 
							ctx.Context.Error(http.StatusInternalServerError)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,7 +8,7 @@ package context
 | 
				
			|||||||
import (
 | 
					import (
 | 
				
			||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"code.gitea.io/gitea/models"
 | 
						"code.gitea.io/gitea/models/login"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/log"
 | 
						"code.gitea.io/gitea/modules/log"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/setting"
 | 
						"code.gitea.io/gitea/modules/setting"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/web/middleware"
 | 
						"code.gitea.io/gitea/modules/web/middleware"
 | 
				
			||||||
@@ -154,9 +154,9 @@ func ToggleAPI(options *ToggleOptions) func(ctx *APIContext) {
 | 
				
			|||||||
				if skip, ok := ctx.Data["SkipLocalTwoFA"]; ok && skip.(bool) {
 | 
									if skip, ok := ctx.Data["SkipLocalTwoFA"]; ok && skip.(bool) {
 | 
				
			||||||
					return // Skip 2FA
 | 
										return // Skip 2FA
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
				twofa, err := models.GetTwoFactorByUID(ctx.User.ID)
 | 
									twofa, err := login.GetTwoFactorByUID(ctx.User.ID)
 | 
				
			||||||
				if err != nil {
 | 
									if err != nil {
 | 
				
			||||||
					if models.IsErrTwoFactorNotEnrolled(err) {
 | 
										if login.IsErrTwoFactorNotEnrolled(err) {
 | 
				
			||||||
						return // No 2FA enrollment for this user
 | 
											return // No 2FA enrollment for this user
 | 
				
			||||||
					}
 | 
										}
 | 
				
			||||||
					ctx.InternalServerError(err)
 | 
										ctx.InternalServerError(err)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -195,9 +195,9 @@ func prepareUserInfo(ctx *context.Context) *models.User {
 | 
				
			|||||||
	ctx.Data["Sources"] = sources
 | 
						ctx.Data["Sources"] = sources
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ctx.Data["TwoFactorEnabled"] = true
 | 
						ctx.Data["TwoFactorEnabled"] = true
 | 
				
			||||||
	_, err = models.GetTwoFactorByUID(u.ID)
 | 
						_, err = login.GetTwoFactorByUID(u.ID)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		if !models.IsErrTwoFactorNotEnrolled(err) {
 | 
							if !login.IsErrTwoFactorNotEnrolled(err) {
 | 
				
			||||||
			ctx.ServerError("IsErrTwoFactorNotEnrolled", err)
 | 
								ctx.ServerError("IsErrTwoFactorNotEnrolled", err)
 | 
				
			||||||
			return nil
 | 
								return nil
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@@ -295,13 +295,13 @@ func EditUserPost(ctx *context.Context) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if form.Reset2FA {
 | 
						if form.Reset2FA {
 | 
				
			||||||
		tf, err := models.GetTwoFactorByUID(u.ID)
 | 
							tf, err := login.GetTwoFactorByUID(u.ID)
 | 
				
			||||||
		if err != nil && !models.IsErrTwoFactorNotEnrolled(err) {
 | 
							if err != nil && !login.IsErrTwoFactorNotEnrolled(err) {
 | 
				
			||||||
			ctx.ServerError("GetTwoFactorByUID", err)
 | 
								ctx.ServerError("GetTwoFactorByUID", err)
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if err = models.DeleteTwoFactorByID(tf.ID, u.ID); err != nil {
 | 
							if err = login.DeleteTwoFactorByID(tf.ID, u.ID); err != nil {
 | 
				
			||||||
			ctx.ServerError("DeleteTwoFactorByID", err)
 | 
								ctx.ServerError("DeleteTwoFactorByID", err)
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -21,6 +21,7 @@ import (
 | 
				
			|||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"code.gitea.io/gitea/models"
 | 
						"code.gitea.io/gitea/models"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/models/login"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/context"
 | 
						"code.gitea.io/gitea/modules/context"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/git"
 | 
						"code.gitea.io/gitea/modules/git"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/log"
 | 
						"code.gitea.io/gitea/modules/log"
 | 
				
			||||||
@@ -174,12 +175,12 @@ func httpBase(ctx *context.Context) (h *serviceHandler) {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if ctx.IsBasicAuth && ctx.Data["IsApiToken"] != true {
 | 
							if ctx.IsBasicAuth && ctx.Data["IsApiToken"] != true {
 | 
				
			||||||
			_, err = models.GetTwoFactorByUID(ctx.User.ID)
 | 
								_, err = login.GetTwoFactorByUID(ctx.User.ID)
 | 
				
			||||||
			if err == nil {
 | 
								if err == nil {
 | 
				
			||||||
				// TODO: This response should be changed to "invalid credentials" for security reasons once the expectation behind it (creating an app token to authenticate) is properly documented
 | 
									// TODO: This response should be changed to "invalid credentials" for security reasons once the expectation behind it (creating an app token to authenticate) is properly documented
 | 
				
			||||||
				ctx.HandleText(http.StatusUnauthorized, "Users with two-factor authentication enabled cannot perform HTTP/HTTPS operations via plain username and password. Please create and use a personal access token on the user settings page")
 | 
									ctx.HandleText(http.StatusUnauthorized, "Users with two-factor authentication enabled cannot perform HTTP/HTTPS operations via plain username and password. Please create and use a personal access token on the user settings page")
 | 
				
			||||||
				return
 | 
									return
 | 
				
			||||||
			} else if !models.IsErrTwoFactorNotEnrolled(err) {
 | 
								} else if !login.IsErrTwoFactorNotEnrolled(err) {
 | 
				
			||||||
				ctx.ServerError("IsErrTwoFactorNotEnrolled", err)
 | 
									ctx.ServerError("IsErrTwoFactorNotEnrolled", err)
 | 
				
			||||||
				return
 | 
									return
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -213,9 +213,9 @@ func SignInPost(ctx *context.Context) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	// If this user is enrolled in 2FA, we can't sign the user in just yet.
 | 
						// If this user is enrolled in 2FA, we can't sign the user in just yet.
 | 
				
			||||||
	// Instead, redirect them to the 2FA authentication page.
 | 
						// Instead, redirect them to the 2FA authentication page.
 | 
				
			||||||
	_, err = models.GetTwoFactorByUID(u.ID)
 | 
						_, err = login.GetTwoFactorByUID(u.ID)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		if models.IsErrTwoFactorNotEnrolled(err) {
 | 
							if login.IsErrTwoFactorNotEnrolled(err) {
 | 
				
			||||||
			handleSignIn(ctx, u, form.Remember)
 | 
								handleSignIn(ctx, u, form.Remember)
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			ctx.ServerError("UserSignIn", err)
 | 
								ctx.ServerError("UserSignIn", err)
 | 
				
			||||||
@@ -237,7 +237,7 @@ func SignInPost(ctx *context.Context) {
 | 
				
			|||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	regs, err := models.GetU2FRegistrationsByUID(u.ID)
 | 
						regs, err := login.GetU2FRegistrationsByUID(u.ID)
 | 
				
			||||||
	if err == nil && len(regs) > 0 {
 | 
						if err == nil && len(regs) > 0 {
 | 
				
			||||||
		ctx.Redirect(setting.AppSubURL + "/user/u2f")
 | 
							ctx.Redirect(setting.AppSubURL + "/user/u2f")
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
@@ -277,7 +277,7 @@ func TwoFactorPost(ctx *context.Context) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	id := idSess.(int64)
 | 
						id := idSess.(int64)
 | 
				
			||||||
	twofa, err := models.GetTwoFactorByUID(id)
 | 
						twofa, err := login.GetTwoFactorByUID(id)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		ctx.ServerError("UserSignIn", err)
 | 
							ctx.ServerError("UserSignIn", err)
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
@@ -313,7 +313,7 @@ func TwoFactorPost(ctx *context.Context) {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		twofa.LastUsedPasscode = form.Passcode
 | 
							twofa.LastUsedPasscode = form.Passcode
 | 
				
			||||||
		if err = models.UpdateTwoFactor(twofa); err != nil {
 | 
							if err = login.UpdateTwoFactor(twofa); err != nil {
 | 
				
			||||||
			ctx.ServerError("UserSignIn", err)
 | 
								ctx.ServerError("UserSignIn", err)
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@@ -356,7 +356,7 @@ func TwoFactorScratchPost(ctx *context.Context) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	id := idSess.(int64)
 | 
						id := idSess.(int64)
 | 
				
			||||||
	twofa, err := models.GetTwoFactorByUID(id)
 | 
						twofa, err := login.GetTwoFactorByUID(id)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		ctx.ServerError("UserSignIn", err)
 | 
							ctx.ServerError("UserSignIn", err)
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
@@ -370,7 +370,7 @@ func TwoFactorScratchPost(ctx *context.Context) {
 | 
				
			|||||||
			ctx.ServerError("UserSignIn", err)
 | 
								ctx.ServerError("UserSignIn", err)
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if err = models.UpdateTwoFactor(twofa); err != nil {
 | 
							if err = login.UpdateTwoFactor(twofa); err != nil {
 | 
				
			||||||
			ctx.ServerError("UserSignIn", err)
 | 
								ctx.ServerError("UserSignIn", err)
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@@ -418,7 +418,7 @@ func U2FChallenge(ctx *context.Context) {
 | 
				
			|||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	id := idSess.(int64)
 | 
						id := idSess.(int64)
 | 
				
			||||||
	regs, err := models.GetU2FRegistrationsByUID(id)
 | 
						regs, err := login.GetU2FRegistrationsByUID(id)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		ctx.ServerError("UserSignIn", err)
 | 
							ctx.ServerError("UserSignIn", err)
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
@@ -454,7 +454,7 @@ func U2FSign(ctx *context.Context) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	challenge := challSess.(*u2f.Challenge)
 | 
						challenge := challSess.(*u2f.Challenge)
 | 
				
			||||||
	id := idSess.(int64)
 | 
						id := idSess.(int64)
 | 
				
			||||||
	regs, err := models.GetU2FRegistrationsByUID(id)
 | 
						regs, err := login.GetU2FRegistrationsByUID(id)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		ctx.ServerError("UserSignIn", err)
 | 
							ctx.ServerError("UserSignIn", err)
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
@@ -717,8 +717,8 @@ func handleOAuth2SignIn(ctx *context.Context, source *login.Source, u *models.Us
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	needs2FA := false
 | 
						needs2FA := false
 | 
				
			||||||
	if !source.Cfg.(*oauth2.Source).SkipLocalTwoFA {
 | 
						if !source.Cfg.(*oauth2.Source).SkipLocalTwoFA {
 | 
				
			||||||
		_, err := models.GetTwoFactorByUID(u.ID)
 | 
							_, err := login.GetTwoFactorByUID(u.ID)
 | 
				
			||||||
		if err != nil && !models.IsErrTwoFactorNotEnrolled(err) {
 | 
							if err != nil && !login.IsErrTwoFactorNotEnrolled(err) {
 | 
				
			||||||
			ctx.ServerError("UserSignIn", err)
 | 
								ctx.ServerError("UserSignIn", err)
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@@ -775,7 +775,7 @@ func handleOAuth2SignIn(ctx *context.Context, source *login.Source, u *models.Us
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// If U2F is enrolled -> Redirect to U2F instead
 | 
						// If U2F is enrolled -> Redirect to U2F instead
 | 
				
			||||||
	regs, err := models.GetU2FRegistrationsByUID(u.ID)
 | 
						regs, err := login.GetU2FRegistrationsByUID(u.ID)
 | 
				
			||||||
	if err == nil && len(regs) > 0 {
 | 
						if err == nil && len(regs) > 0 {
 | 
				
			||||||
		ctx.Redirect(setting.AppSubURL + "/user/u2f")
 | 
							ctx.Redirect(setting.AppSubURL + "/user/u2f")
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
@@ -935,9 +935,9 @@ func linkAccount(ctx *context.Context, u *models.User, gothUser goth.User, remem
 | 
				
			|||||||
	// If this user is enrolled in 2FA, we can't sign the user in just yet.
 | 
						// If this user is enrolled in 2FA, we can't sign the user in just yet.
 | 
				
			||||||
	// Instead, redirect them to the 2FA authentication page.
 | 
						// Instead, redirect them to the 2FA authentication page.
 | 
				
			||||||
	// We deliberately ignore the skip local 2fa setting here because we are linking to a previous user here
 | 
						// We deliberately ignore the skip local 2fa setting here because we are linking to a previous user here
 | 
				
			||||||
	_, err := models.GetTwoFactorByUID(u.ID)
 | 
						_, err := login.GetTwoFactorByUID(u.ID)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		if !models.IsErrTwoFactorNotEnrolled(err) {
 | 
							if !login.IsErrTwoFactorNotEnrolled(err) {
 | 
				
			||||||
			ctx.ServerError("UserLinkAccount", err)
 | 
								ctx.ServerError("UserLinkAccount", err)
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@@ -967,7 +967,7 @@ func linkAccount(ctx *context.Context, u *models.User, gothUser goth.User, remem
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// If U2F is enrolled -> Redirect to U2F instead
 | 
						// If U2F is enrolled -> Redirect to U2F instead
 | 
				
			||||||
	regs, err := models.GetU2FRegistrationsByUID(u.ID)
 | 
						regs, err := login.GetU2FRegistrationsByUID(u.ID)
 | 
				
			||||||
	if err == nil && len(regs) > 0 {
 | 
						if err == nil && len(regs) > 0 {
 | 
				
			||||||
		ctx.Redirect(setting.AppSubURL + "/user/u2f")
 | 
							ctx.Redirect(setting.AppSubURL + "/user/u2f")
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
@@ -1561,7 +1561,7 @@ func ForgotPasswdPost(ctx *context.Context) {
 | 
				
			|||||||
	ctx.HTML(http.StatusOK, tplForgotPassword)
 | 
						ctx.HTML(http.StatusOK, tplForgotPassword)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func commonResetPassword(ctx *context.Context) (*models.User, *models.TwoFactor) {
 | 
					func commonResetPassword(ctx *context.Context) (*models.User, *login.TwoFactor) {
 | 
				
			||||||
	code := ctx.FormString("code")
 | 
						code := ctx.FormString("code")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ctx.Data["Title"] = ctx.Tr("auth.reset_password")
 | 
						ctx.Data["Title"] = ctx.Tr("auth.reset_password")
 | 
				
			||||||
@@ -1583,9 +1583,9 @@ func commonResetPassword(ctx *context.Context) (*models.User, *models.TwoFactor)
 | 
				
			|||||||
		return nil, nil
 | 
							return nil, nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	twofa, err := models.GetTwoFactorByUID(u.ID)
 | 
						twofa, err := login.GetTwoFactorByUID(u.ID)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		if !models.IsErrTwoFactorNotEnrolled(err) {
 | 
							if !login.IsErrTwoFactorNotEnrolled(err) {
 | 
				
			||||||
			ctx.Error(http.StatusInternalServerError, "CommonResetPassword", err.Error())
 | 
								ctx.Error(http.StatusInternalServerError, "CommonResetPassword", err.Error())
 | 
				
			||||||
			return nil, nil
 | 
								return nil, nil
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@@ -1680,7 +1680,7 @@ func ResetPasswdPost(ctx *context.Context) {
 | 
				
			|||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			twofa.LastUsedPasscode = passcode
 | 
								twofa.LastUsedPasscode = passcode
 | 
				
			||||||
			if err = models.UpdateTwoFactor(twofa); err != nil {
 | 
								if err = login.UpdateTwoFactor(twofa); err != nil {
 | 
				
			||||||
				ctx.ServerError("ResetPasswdPost: UpdateTwoFactor", err)
 | 
									ctx.ServerError("ResetPasswdPost: UpdateTwoFactor", err)
 | 
				
			||||||
				return
 | 
									return
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
@@ -1712,7 +1712,7 @@ func ResetPasswdPost(ctx *context.Context) {
 | 
				
			|||||||
			ctx.ServerError("UserSignIn", err)
 | 
								ctx.ServerError("UserSignIn", err)
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if err = models.UpdateTwoFactor(twofa); err != nil {
 | 
							if err = login.UpdateTwoFactor(twofa); err != nil {
 | 
				
			||||||
			ctx.ServerError("UserSignIn", err)
 | 
								ctx.ServerError("UserSignIn", err)
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -56,9 +56,9 @@ func DeleteAccountLink(ctx *context.Context) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
func loadSecurityData(ctx *context.Context) {
 | 
					func loadSecurityData(ctx *context.Context) {
 | 
				
			||||||
	enrolled := true
 | 
						enrolled := true
 | 
				
			||||||
	_, err := models.GetTwoFactorByUID(ctx.User.ID)
 | 
						_, err := login.GetTwoFactorByUID(ctx.User.ID)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		if models.IsErrTwoFactorNotEnrolled(err) {
 | 
							if login.IsErrTwoFactorNotEnrolled(err) {
 | 
				
			||||||
			enrolled = false
 | 
								enrolled = false
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			ctx.ServerError("SettingsTwoFactor", err)
 | 
								ctx.ServerError("SettingsTwoFactor", err)
 | 
				
			||||||
@@ -67,7 +67,7 @@ func loadSecurityData(ctx *context.Context) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	ctx.Data["TwofaEnrolled"] = enrolled
 | 
						ctx.Data["TwofaEnrolled"] = enrolled
 | 
				
			||||||
	if enrolled {
 | 
						if enrolled {
 | 
				
			||||||
		ctx.Data["U2FRegistrations"], err = models.GetU2FRegistrationsByUID(ctx.User.ID)
 | 
							ctx.Data["U2FRegistrations"], err = login.GetU2FRegistrationsByUID(ctx.User.ID)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			ctx.ServerError("GetU2FRegistrationsByUID", err)
 | 
								ctx.ServerError("GetU2FRegistrationsByUID", err)
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -13,7 +13,7 @@ import (
 | 
				
			|||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"code.gitea.io/gitea/models"
 | 
						"code.gitea.io/gitea/models/login"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/context"
 | 
						"code.gitea.io/gitea/modules/context"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/log"
 | 
						"code.gitea.io/gitea/modules/log"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/setting"
 | 
						"code.gitea.io/gitea/modules/setting"
 | 
				
			||||||
@@ -29,9 +29,9 @@ func RegenerateScratchTwoFactor(ctx *context.Context) {
 | 
				
			|||||||
	ctx.Data["Title"] = ctx.Tr("settings")
 | 
						ctx.Data["Title"] = ctx.Tr("settings")
 | 
				
			||||||
	ctx.Data["PageIsSettingsSecurity"] = true
 | 
						ctx.Data["PageIsSettingsSecurity"] = true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	t, err := models.GetTwoFactorByUID(ctx.User.ID)
 | 
						t, err := login.GetTwoFactorByUID(ctx.User.ID)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		if models.IsErrTwoFactorNotEnrolled(err) {
 | 
							if login.IsErrTwoFactorNotEnrolled(err) {
 | 
				
			||||||
			ctx.Flash.Error(ctx.Tr("setting.twofa_not_enrolled"))
 | 
								ctx.Flash.Error(ctx.Tr("setting.twofa_not_enrolled"))
 | 
				
			||||||
			ctx.Redirect(setting.AppSubURL + "/user/settings/security")
 | 
								ctx.Redirect(setting.AppSubURL + "/user/settings/security")
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@@ -45,7 +45,7 @@ func RegenerateScratchTwoFactor(ctx *context.Context) {
 | 
				
			|||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if err = models.UpdateTwoFactor(t); err != nil {
 | 
						if err = login.UpdateTwoFactor(t); err != nil {
 | 
				
			||||||
		ctx.ServerError("SettingsTwoFactor: Failed to UpdateTwoFactor", err)
 | 
							ctx.ServerError("SettingsTwoFactor: Failed to UpdateTwoFactor", err)
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -59,9 +59,9 @@ func DisableTwoFactor(ctx *context.Context) {
 | 
				
			|||||||
	ctx.Data["Title"] = ctx.Tr("settings")
 | 
						ctx.Data["Title"] = ctx.Tr("settings")
 | 
				
			||||||
	ctx.Data["PageIsSettingsSecurity"] = true
 | 
						ctx.Data["PageIsSettingsSecurity"] = true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	t, err := models.GetTwoFactorByUID(ctx.User.ID)
 | 
						t, err := login.GetTwoFactorByUID(ctx.User.ID)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		if models.IsErrTwoFactorNotEnrolled(err) {
 | 
							if login.IsErrTwoFactorNotEnrolled(err) {
 | 
				
			||||||
			ctx.Flash.Error(ctx.Tr("setting.twofa_not_enrolled"))
 | 
								ctx.Flash.Error(ctx.Tr("setting.twofa_not_enrolled"))
 | 
				
			||||||
			ctx.Redirect(setting.AppSubURL + "/user/settings/security")
 | 
								ctx.Redirect(setting.AppSubURL + "/user/settings/security")
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@@ -69,8 +69,8 @@ func DisableTwoFactor(ctx *context.Context) {
 | 
				
			|||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if err = models.DeleteTwoFactorByID(t.ID, ctx.User.ID); err != nil {
 | 
						if err = login.DeleteTwoFactorByID(t.ID, ctx.User.ID); err != nil {
 | 
				
			||||||
		if models.IsErrTwoFactorNotEnrolled(err) {
 | 
							if login.IsErrTwoFactorNotEnrolled(err) {
 | 
				
			||||||
			// There is a potential DB race here - we must have been disabled by another request in the intervening period
 | 
								// There is a potential DB race here - we must have been disabled by another request in the intervening period
 | 
				
			||||||
			ctx.Flash.Success(ctx.Tr("settings.twofa_disabled"))
 | 
								ctx.Flash.Success(ctx.Tr("settings.twofa_disabled"))
 | 
				
			||||||
			ctx.Redirect(setting.AppSubURL + "/user/settings/security")
 | 
								ctx.Redirect(setting.AppSubURL + "/user/settings/security")
 | 
				
			||||||
@@ -146,7 +146,7 @@ func EnrollTwoFactor(ctx *context.Context) {
 | 
				
			|||||||
	ctx.Data["Title"] = ctx.Tr("settings")
 | 
						ctx.Data["Title"] = ctx.Tr("settings")
 | 
				
			||||||
	ctx.Data["PageIsSettingsSecurity"] = true
 | 
						ctx.Data["PageIsSettingsSecurity"] = true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	t, err := models.GetTwoFactorByUID(ctx.User.ID)
 | 
						t, err := login.GetTwoFactorByUID(ctx.User.ID)
 | 
				
			||||||
	if t != nil {
 | 
						if t != nil {
 | 
				
			||||||
		// already enrolled - we should redirect back!
 | 
							// already enrolled - we should redirect back!
 | 
				
			||||||
		log.Warn("Trying to re-enroll %-v in twofa when already enrolled", ctx.User)
 | 
							log.Warn("Trying to re-enroll %-v in twofa when already enrolled", ctx.User)
 | 
				
			||||||
@@ -154,7 +154,7 @@ func EnrollTwoFactor(ctx *context.Context) {
 | 
				
			|||||||
		ctx.Redirect(setting.AppSubURL + "/user/settings/security")
 | 
							ctx.Redirect(setting.AppSubURL + "/user/settings/security")
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if err != nil && !models.IsErrTwoFactorNotEnrolled(err) {
 | 
						if err != nil && !login.IsErrTwoFactorNotEnrolled(err) {
 | 
				
			||||||
		ctx.ServerError("SettingsTwoFactor: GetTwoFactorByUID", err)
 | 
							ctx.ServerError("SettingsTwoFactor: GetTwoFactorByUID", err)
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -172,14 +172,14 @@ func EnrollTwoFactorPost(ctx *context.Context) {
 | 
				
			|||||||
	ctx.Data["Title"] = ctx.Tr("settings")
 | 
						ctx.Data["Title"] = ctx.Tr("settings")
 | 
				
			||||||
	ctx.Data["PageIsSettingsSecurity"] = true
 | 
						ctx.Data["PageIsSettingsSecurity"] = true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	t, err := models.GetTwoFactorByUID(ctx.User.ID)
 | 
						t, err := login.GetTwoFactorByUID(ctx.User.ID)
 | 
				
			||||||
	if t != nil {
 | 
						if t != nil {
 | 
				
			||||||
		// already enrolled
 | 
							// already enrolled
 | 
				
			||||||
		ctx.Flash.Error(ctx.Tr("setting.twofa_is_enrolled"))
 | 
							ctx.Flash.Error(ctx.Tr("setting.twofa_is_enrolled"))
 | 
				
			||||||
		ctx.Redirect(setting.AppSubURL + "/user/settings/security")
 | 
							ctx.Redirect(setting.AppSubURL + "/user/settings/security")
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if err != nil && !models.IsErrTwoFactorNotEnrolled(err) {
 | 
						if err != nil && !login.IsErrTwoFactorNotEnrolled(err) {
 | 
				
			||||||
		ctx.ServerError("SettingsTwoFactor: Failed to check if already enrolled with GetTwoFactorByUID", err)
 | 
							ctx.ServerError("SettingsTwoFactor: Failed to check if already enrolled with GetTwoFactorByUID", err)
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -209,7 +209,7 @@ func EnrollTwoFactorPost(ctx *context.Context) {
 | 
				
			|||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	t = &models.TwoFactor{
 | 
						t = &login.TwoFactor{
 | 
				
			||||||
		UID: ctx.User.ID,
 | 
							UID: ctx.User.ID,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	err = t.SetSecret(secret)
 | 
						err = t.SetSecret(secret)
 | 
				
			||||||
@@ -238,7 +238,7 @@ func EnrollTwoFactorPost(ctx *context.Context) {
 | 
				
			|||||||
		log.Error("Unable to save changes to the session: %v", err)
 | 
							log.Error("Unable to save changes to the session: %v", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if err = models.NewTwoFactor(t); err != nil {
 | 
						if err = login.NewTwoFactor(t); err != nil {
 | 
				
			||||||
		// FIXME: We need to handle a unique constraint fail here it's entirely possible that another request has beaten us.
 | 
							// FIXME: We need to handle a unique constraint fail here it's entirely possible that another request has beaten us.
 | 
				
			||||||
		// If there is a unique constraint fail we should just tolerate the error
 | 
							// If there is a unique constraint fail we should just tolerate the error
 | 
				
			||||||
		ctx.ServerError("SettingsTwoFactor: Failed to save two factor", err)
 | 
							ctx.ServerError("SettingsTwoFactor: Failed to save two factor", err)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,7 +8,7 @@ import (
 | 
				
			|||||||
	"errors"
 | 
						"errors"
 | 
				
			||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"code.gitea.io/gitea/models"
 | 
						"code.gitea.io/gitea/models/login"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/context"
 | 
						"code.gitea.io/gitea/modules/context"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/log"
 | 
						"code.gitea.io/gitea/modules/log"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/setting"
 | 
						"code.gitea.io/gitea/modules/setting"
 | 
				
			||||||
@@ -34,7 +34,7 @@ func U2FRegister(ctx *context.Context) {
 | 
				
			|||||||
		ctx.ServerError("Unable to set session key for u2fChallenge", err)
 | 
							ctx.ServerError("Unable to set session key for u2fChallenge", err)
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	regs, err := models.GetU2FRegistrationsByUID(ctx.User.ID)
 | 
						regs, err := login.GetU2FRegistrationsByUID(ctx.User.ID)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		ctx.ServerError("GetU2FRegistrationsByUID", err)
 | 
							ctx.ServerError("GetU2FRegistrationsByUID", err)
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
@@ -78,7 +78,7 @@ func U2FRegisterPost(ctx *context.Context) {
 | 
				
			|||||||
		ctx.ServerError("u2f.Register", err)
 | 
							ctx.ServerError("u2f.Register", err)
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if _, err = models.CreateRegistration(ctx.User, name, reg); err != nil {
 | 
						if _, err = login.CreateRegistration(ctx.User.ID, name, reg); err != nil {
 | 
				
			||||||
		ctx.ServerError("u2f.Register", err)
 | 
							ctx.ServerError("u2f.Register", err)
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -88,9 +88,9 @@ func U2FRegisterPost(ctx *context.Context) {
 | 
				
			|||||||
// U2FDelete deletes an security key by id
 | 
					// U2FDelete deletes an security key by id
 | 
				
			||||||
func U2FDelete(ctx *context.Context) {
 | 
					func U2FDelete(ctx *context.Context) {
 | 
				
			||||||
	form := web.GetForm(ctx).(*forms.U2FDeleteForm)
 | 
						form := web.GetForm(ctx).(*forms.U2FDeleteForm)
 | 
				
			||||||
	reg, err := models.GetU2FRegistrationByID(form.ID)
 | 
						reg, err := login.GetU2FRegistrationByID(form.ID)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		if models.IsErrU2FRegistrationNotExist(err) {
 | 
							if login.IsErrU2FRegistrationNotExist(err) {
 | 
				
			||||||
			ctx.Status(200)
 | 
								ctx.Status(200)
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@@ -101,7 +101,7 @@ func U2FDelete(ctx *context.Context) {
 | 
				
			|||||||
		ctx.Status(401)
 | 
							ctx.Status(401)
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if err := models.DeleteRegistration(reg); err != nil {
 | 
						if err := login.DeleteRegistration(reg); err != nil {
 | 
				
			||||||
		ctx.ServerError("DeleteRegistration", err)
 | 
							ctx.ServerError("DeleteRegistration", err)
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user