Przeglądaj źródła

all: unwrap `database.TwoFactorsStore` interface (#7707)

Joe Chen 11 miesięcy temu
rodzic
commit
202012887a

+ 4 - 1
internal/database/database.go

@@ -123,7 +123,6 @@ func NewConnection(w logger.Writer) (*gorm.DB, error) {
 	}
 
 	// Initialize stores, sorted in alphabetical order.
-	TwoFactors = &twoFactorsStore{DB: db}
 	Users = NewUsersStore(db)
 
 	Handle = &DB{db: db}
@@ -186,3 +185,7 @@ func (db *DB) PublicKey() *PublicKeysStore {
 func (db *DB) Repositories() *RepositoriesStore {
 	return newReposStore(db.db)
 }
+
+func (db *DB) TwoFactors() *TwoFactorsStore {
+	return newTwoFactorsStore(db.db)
+}

+ 0 - 8
internal/database/mocks.go

@@ -8,14 +8,6 @@ import (
 	"testing"
 )
 
-func SetMockTwoFactorsStore(t *testing.T, mock TwoFactorsStore) {
-	before := TwoFactors
-	TwoFactors = mock
-	t.Cleanup(func() {
-		TwoFactors = before
-	})
-}
-
 func SetMockUsersStore(t *testing.T, mock UsersStore) {
 	before := Users
 	Users = mock

+ 21 - 28
internal/database/two_factors.go

@@ -20,22 +20,6 @@ import (
 	"gogs.io/gogs/internal/strutil"
 )
 
-// TwoFactorsStore is the persistent interface for 2FA.
-type TwoFactorsStore interface {
-	// Create creates a new 2FA token and recovery codes for given user. The "key"
-	// is used to encrypt and later decrypt given "secret", which should be
-	// configured in site-level and change of the "key" will break all existing 2FA
-	// tokens.
-	Create(ctx context.Context, userID int64, key, secret string) error
-	// GetByUserID returns the 2FA token of given user. It returns
-	// ErrTwoFactorNotFound when not found.
-	GetByUserID(ctx context.Context, userID int64) (*TwoFactor, error)
-	// IsEnabled returns true if the user has enabled 2FA.
-	IsEnabled(ctx context.Context, userID int64) bool
-}
-
-var TwoFactors TwoFactorsStore
-
 // BeforeCreate implements the GORM create hook.
 func (t *TwoFactor) BeforeCreate(tx *gorm.DB) error {
 	if t.CreatedUnix == 0 {
@@ -50,13 +34,20 @@ func (t *TwoFactor) AfterFind(_ *gorm.DB) error {
 	return nil
 }
 
-var _ TwoFactorsStore = (*twoFactorsStore)(nil)
+// TwoFactorsStore is the storage layer for two-factor authentication settings.
+type TwoFactorsStore struct {
+	db *gorm.DB
+}
 
-type twoFactorsStore struct {
-	*gorm.DB
+func newTwoFactorsStore(db *gorm.DB) *TwoFactorsStore {
+	return &TwoFactorsStore{db: db}
 }
 
-func (s *twoFactorsStore) Create(ctx context.Context, userID int64, key, secret string) error {
+// Create creates a new 2FA token and recovery codes for given user. The "key"
+// is used to encrypt and later decrypt given "secret", which should be
+// configured in site-level and change of the "key" will break all existing 2FA
+// tokens.
+func (s *TwoFactorsStore) Create(ctx context.Context, userID int64, key, secret string) error {
 	encrypted, err := cryptoutil.AESGCMEncrypt(cryptoutil.MD5Bytes(key), []byte(secret))
 	if err != nil {
 		return errors.Wrap(err, "encrypt secret")
@@ -71,7 +62,7 @@ func (s *twoFactorsStore) Create(ctx context.Context, userID int64, key, secret
 		return errors.Wrap(err, "generate recovery codes")
 	}
 
-	return s.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
+	return s.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
 		err := tx.Create(tf).Error
 		if err != nil {
 			return err
@@ -88,8 +79,7 @@ type ErrTwoFactorNotFound struct {
 }
 
 func IsErrTwoFactorNotFound(err error) bool {
-	_, ok := err.(ErrTwoFactorNotFound)
-	return ok
+	return errors.As(err, &ErrTwoFactorNotFound{})
 }
 
 func (err ErrTwoFactorNotFound) Error() string {
@@ -100,11 +90,13 @@ func (ErrTwoFactorNotFound) NotFound() bool {
 	return true
 }
 
-func (s *twoFactorsStore) GetByUserID(ctx context.Context, userID int64) (*TwoFactor, error) {
+// GetByUserID returns the 2FA token of given user. It returns
+// ErrTwoFactorNotFound when not found.
+func (s *TwoFactorsStore) GetByUserID(ctx context.Context, userID int64) (*TwoFactor, error) {
 	tf := new(TwoFactor)
-	err := s.WithContext(ctx).Where("user_id = ?", userID).First(tf).Error
+	err := s.db.WithContext(ctx).Where("user_id = ?", userID).First(tf).Error
 	if err != nil {
-		if err == gorm.ErrRecordNotFound {
+		if errors.Is(err, gorm.ErrRecordNotFound) {
 			return nil, ErrTwoFactorNotFound{args: errutil.Args{"userID": userID}}
 		}
 		return nil, err
@@ -112,9 +104,10 @@ func (s *twoFactorsStore) GetByUserID(ctx context.Context, userID int64) (*TwoFa
 	return tf, nil
 }
 
-func (s *twoFactorsStore) IsEnabled(ctx context.Context, userID int64) bool {
+// IsEnabled returns true if the user has enabled 2FA.
+func (s *TwoFactorsStore) IsEnabled(ctx context.Context, userID int64) bool {
 	var count int64
-	err := s.WithContext(ctx).Model(new(TwoFactor)).Where("user_id = ?", userID).Count(&count).Error
+	err := s.db.WithContext(ctx).Model(new(TwoFactor)).Where("user_id = ?", userID).Count(&count).Error
 	if err != nil {
 		log.Error("Failed to count two factors [user_id: %d]: %v", userID, err)
 	}

+ 18 - 18
internal/database/two_factors_test.go

@@ -67,13 +67,13 @@ func TestTwoFactors(t *testing.T) {
 	t.Parallel()
 
 	ctx := context.Background()
-	db := &twoFactorsStore{
-		DB: newTestDB(t, "twoFactorsStore"),
+	s := &TwoFactorsStore{
+		db: newTestDB(t, "TwoFactorsStore"),
 	}
 
 	for _, tc := range []struct {
 		name string
-		test func(t *testing.T, ctx context.Context, db *twoFactorsStore)
+		test func(t *testing.T, ctx context.Context, s *TwoFactorsStore)
 	}{
 		{"Create", twoFactorsCreate},
 		{"GetByUserID", twoFactorsGetByUserID},
@@ -81,10 +81,10 @@ func TestTwoFactors(t *testing.T) {
 	} {
 		t.Run(tc.name, func(t *testing.T) {
 			t.Cleanup(func() {
-				err := clearTables(t, db.DB)
+				err := clearTables(t, s.db)
 				require.NoError(t, err)
 			})
-			tc.test(t, ctx, db)
+			tc.test(t, ctx, s)
 		})
 		if t.Failed() {
 			break
@@ -92,43 +92,43 @@ func TestTwoFactors(t *testing.T) {
 	}
 }
 
-func twoFactorsCreate(t *testing.T, ctx context.Context, db *twoFactorsStore) {
+func twoFactorsCreate(t *testing.T, ctx context.Context, s *TwoFactorsStore) {
 	// Create a 2FA token
-	err := db.Create(ctx, 1, "secure-key", "secure-secret")
+	err := s.Create(ctx, 1, "secure-key", "secure-secret")
 	require.NoError(t, err)
 
 	// Get it back and check the Created field
-	tf, err := db.GetByUserID(ctx, 1)
+	tf, err := s.GetByUserID(ctx, 1)
 	require.NoError(t, err)
-	assert.Equal(t, db.NowFunc().Format(time.RFC3339), tf.Created.UTC().Format(time.RFC3339))
+	assert.Equal(t, s.db.NowFunc().Format(time.RFC3339), tf.Created.UTC().Format(time.RFC3339))
 
 	// Verify there are 10 recover codes generated
 	var count int64
-	err = db.Model(new(TwoFactorRecoveryCode)).Count(&count).Error
+	err = s.db.Model(new(TwoFactorRecoveryCode)).Count(&count).Error
 	require.NoError(t, err)
 	assert.Equal(t, int64(10), count)
 }
 
-func twoFactorsGetByUserID(t *testing.T, ctx context.Context, db *twoFactorsStore) {
+func twoFactorsGetByUserID(t *testing.T, ctx context.Context, s *TwoFactorsStore) {
 	// Create a 2FA token for user 1
-	err := db.Create(ctx, 1, "secure-key", "secure-secret")
+	err := s.Create(ctx, 1, "secure-key", "secure-secret")
 	require.NoError(t, err)
 
 	// We should be able to get it back
-	_, err = db.GetByUserID(ctx, 1)
+	_, err = s.GetByUserID(ctx, 1)
 	require.NoError(t, err)
 
 	// Try to get a non-existent 2FA token
-	_, err = db.GetByUserID(ctx, 2)
+	_, err = s.GetByUserID(ctx, 2)
 	wantErr := ErrTwoFactorNotFound{args: errutil.Args{"userID": int64(2)}}
 	assert.Equal(t, wantErr, err)
 }
 
-func twoFactorsIsEnabled(t *testing.T, ctx context.Context, db *twoFactorsStore) {
+func twoFactorsIsEnabled(t *testing.T, ctx context.Context, s *TwoFactorsStore) {
 	// Create a 2FA token for user 1
-	err := db.Create(ctx, 1, "secure-key", "secure-secret")
+	err := s.Create(ctx, 1, "secure-key", "secure-secret")
 	require.NoError(t, err)
 
-	assert.True(t, db.IsEnabled(ctx, 1))
-	assert.False(t, db.IsEnabled(ctx, 2))
+	assert.True(t, s.IsEnabled(ctx, 1))
+	assert.False(t, s.IsEnabled(ctx, 2))
 }

+ 121 - 401
internal/route/lfs/mocks_test.go

@@ -14,407 +14,6 @@ import (
 	lfsutil "gogs.io/gogs/internal/lfsutil"
 )
 
-// MockTwoFactorsStore is a mock implementation of the TwoFactorsStore
-// interface (from the package gogs.io/gogs/internal/database) used for unit
-// testing.
-type MockTwoFactorsStore struct {
-	// CreateFunc is an instance of a mock function object controlling the
-	// behavior of the method Create.
-	CreateFunc *TwoFactorsStoreCreateFunc
-	// GetByUserIDFunc is an instance of a mock function object controlling
-	// the behavior of the method GetByUserID.
-	GetByUserIDFunc *TwoFactorsStoreGetByUserIDFunc
-	// IsEnabledFunc is an instance of a mock function object controlling
-	// the behavior of the method IsEnabled.
-	IsEnabledFunc *TwoFactorsStoreIsEnabledFunc
-}
-
-// NewMockTwoFactorsStore creates a new mock of the TwoFactorsStore
-// interface. All methods return zero values for all results, unless
-// overwritten.
-func NewMockTwoFactorsStore() *MockTwoFactorsStore {
-	return &MockTwoFactorsStore{
-		CreateFunc: &TwoFactorsStoreCreateFunc{
-			defaultHook: func(context.Context, int64, string, string) (r0 error) {
-				return
-			},
-		},
-		GetByUserIDFunc: &TwoFactorsStoreGetByUserIDFunc{
-			defaultHook: func(context.Context, int64) (r0 *database.TwoFactor, r1 error) {
-				return
-			},
-		},
-		IsEnabledFunc: &TwoFactorsStoreIsEnabledFunc{
-			defaultHook: func(context.Context, int64) (r0 bool) {
-				return
-			},
-		},
-	}
-}
-
-// NewStrictMockTwoFactorsStore creates a new mock of the TwoFactorsStore
-// interface. All methods panic on invocation, unless overwritten.
-func NewStrictMockTwoFactorsStore() *MockTwoFactorsStore {
-	return &MockTwoFactorsStore{
-		CreateFunc: &TwoFactorsStoreCreateFunc{
-			defaultHook: func(context.Context, int64, string, string) error {
-				panic("unexpected invocation of MockTwoFactorsStore.Create")
-			},
-		},
-		GetByUserIDFunc: &TwoFactorsStoreGetByUserIDFunc{
-			defaultHook: func(context.Context, int64) (*database.TwoFactor, error) {
-				panic("unexpected invocation of MockTwoFactorsStore.GetByUserID")
-			},
-		},
-		IsEnabledFunc: &TwoFactorsStoreIsEnabledFunc{
-			defaultHook: func(context.Context, int64) bool {
-				panic("unexpected invocation of MockTwoFactorsStore.IsEnabled")
-			},
-		},
-	}
-}
-
-// NewMockTwoFactorsStoreFrom creates a new mock of the MockTwoFactorsStore
-// interface. All methods delegate to the given implementation, unless
-// overwritten.
-func NewMockTwoFactorsStoreFrom(i database.TwoFactorsStore) *MockTwoFactorsStore {
-	return &MockTwoFactorsStore{
-		CreateFunc: &TwoFactorsStoreCreateFunc{
-			defaultHook: i.Create,
-		},
-		GetByUserIDFunc: &TwoFactorsStoreGetByUserIDFunc{
-			defaultHook: i.GetByUserID,
-		},
-		IsEnabledFunc: &TwoFactorsStoreIsEnabledFunc{
-			defaultHook: i.IsEnabled,
-		},
-	}
-}
-
-// TwoFactorsStoreCreateFunc describes the behavior when the Create method
-// of the parent MockTwoFactorsStore instance is invoked.
-type TwoFactorsStoreCreateFunc struct {
-	defaultHook func(context.Context, int64, string, string) error
-	hooks       []func(context.Context, int64, string, string) error
-	history     []TwoFactorsStoreCreateFuncCall
-	mutex       sync.Mutex
-}
-
-// Create delegates to the next hook function in the queue and stores the
-// parameter and result values of this invocation.
-func (m *MockTwoFactorsStore) Create(v0 context.Context, v1 int64, v2 string, v3 string) error {
-	r0 := m.CreateFunc.nextHook()(v0, v1, v2, v3)
-	m.CreateFunc.appendCall(TwoFactorsStoreCreateFuncCall{v0, v1, v2, v3, r0})
-	return r0
-}
-
-// SetDefaultHook sets function that is called when the Create method of the
-// parent MockTwoFactorsStore instance is invoked and the hook queue is
-// empty.
-func (f *TwoFactorsStoreCreateFunc) SetDefaultHook(hook func(context.Context, int64, string, string) error) {
-	f.defaultHook = hook
-}
-
-// PushHook adds a function to the end of hook queue. Each invocation of the
-// Create method of the parent MockTwoFactorsStore instance invokes the hook
-// at the front of the queue and discards it. After the queue is empty, the
-// default hook function is invoked for any future action.
-func (f *TwoFactorsStoreCreateFunc) PushHook(hook func(context.Context, int64, string, string) error) {
-	f.mutex.Lock()
-	f.hooks = append(f.hooks, hook)
-	f.mutex.Unlock()
-}
-
-// SetDefaultReturn calls SetDefaultHook with a function that returns the
-// given values.
-func (f *TwoFactorsStoreCreateFunc) SetDefaultReturn(r0 error) {
-	f.SetDefaultHook(func(context.Context, int64, string, string) error {
-		return r0
-	})
-}
-
-// PushReturn calls PushHook with a function that returns the given values.
-func (f *TwoFactorsStoreCreateFunc) PushReturn(r0 error) {
-	f.PushHook(func(context.Context, int64, string, string) error {
-		return r0
-	})
-}
-
-func (f *TwoFactorsStoreCreateFunc) nextHook() func(context.Context, int64, string, string) error {
-	f.mutex.Lock()
-	defer f.mutex.Unlock()
-
-	if len(f.hooks) == 0 {
-		return f.defaultHook
-	}
-
-	hook := f.hooks[0]
-	f.hooks = f.hooks[1:]
-	return hook
-}
-
-func (f *TwoFactorsStoreCreateFunc) appendCall(r0 TwoFactorsStoreCreateFuncCall) {
-	f.mutex.Lock()
-	f.history = append(f.history, r0)
-	f.mutex.Unlock()
-}
-
-// History returns a sequence of TwoFactorsStoreCreateFuncCall objects
-// describing the invocations of this function.
-func (f *TwoFactorsStoreCreateFunc) History() []TwoFactorsStoreCreateFuncCall {
-	f.mutex.Lock()
-	history := make([]TwoFactorsStoreCreateFuncCall, len(f.history))
-	copy(history, f.history)
-	f.mutex.Unlock()
-
-	return history
-}
-
-// TwoFactorsStoreCreateFuncCall is an object that describes an invocation
-// of method Create on an instance of MockTwoFactorsStore.
-type TwoFactorsStoreCreateFuncCall struct {
-	// Arg0 is the value of the 1st argument passed to this method
-	// invocation.
-	Arg0 context.Context
-	// Arg1 is the value of the 2nd argument passed to this method
-	// invocation.
-	Arg1 int64
-	// Arg2 is the value of the 3rd argument passed to this method
-	// invocation.
-	Arg2 string
-	// Arg3 is the value of the 4th argument passed to this method
-	// invocation.
-	Arg3 string
-	// Result0 is the value of the 1st result returned from this method
-	// invocation.
-	Result0 error
-}
-
-// Args returns an interface slice containing the arguments of this
-// invocation.
-func (c TwoFactorsStoreCreateFuncCall) Args() []interface{} {
-	return []interface{}{c.Arg0, c.Arg1, c.Arg2, c.Arg3}
-}
-
-// Results returns an interface slice containing the results of this
-// invocation.
-func (c TwoFactorsStoreCreateFuncCall) Results() []interface{} {
-	return []interface{}{c.Result0}
-}
-
-// TwoFactorsStoreGetByUserIDFunc describes the behavior when the
-// GetByUserID method of the parent MockTwoFactorsStore instance is invoked.
-type TwoFactorsStoreGetByUserIDFunc struct {
-	defaultHook func(context.Context, int64) (*database.TwoFactor, error)
-	hooks       []func(context.Context, int64) (*database.TwoFactor, error)
-	history     []TwoFactorsStoreGetByUserIDFuncCall
-	mutex       sync.Mutex
-}
-
-// GetByUserID delegates to the next hook function in the queue and stores
-// the parameter and result values of this invocation.
-func (m *MockTwoFactorsStore) GetByUserID(v0 context.Context, v1 int64) (*database.TwoFactor, error) {
-	r0, r1 := m.GetByUserIDFunc.nextHook()(v0, v1)
-	m.GetByUserIDFunc.appendCall(TwoFactorsStoreGetByUserIDFuncCall{v0, v1, r0, r1})
-	return r0, r1
-}
-
-// SetDefaultHook sets function that is called when the GetByUserID method
-// of the parent MockTwoFactorsStore instance is invoked and the hook queue
-// is empty.
-func (f *TwoFactorsStoreGetByUserIDFunc) SetDefaultHook(hook func(context.Context, int64) (*database.TwoFactor, error)) {
-	f.defaultHook = hook
-}
-
-// PushHook adds a function to the end of hook queue. Each invocation of the
-// GetByUserID method of the parent MockTwoFactorsStore instance invokes the
-// hook at the front of the queue and discards it. After the queue is empty,
-// the default hook function is invoked for any future action.
-func (f *TwoFactorsStoreGetByUserIDFunc) PushHook(hook func(context.Context, int64) (*database.TwoFactor, error)) {
-	f.mutex.Lock()
-	f.hooks = append(f.hooks, hook)
-	f.mutex.Unlock()
-}
-
-// SetDefaultReturn calls SetDefaultHook with a function that returns the
-// given values.
-func (f *TwoFactorsStoreGetByUserIDFunc) SetDefaultReturn(r0 *database.TwoFactor, r1 error) {
-	f.SetDefaultHook(func(context.Context, int64) (*database.TwoFactor, error) {
-		return r0, r1
-	})
-}
-
-// PushReturn calls PushHook with a function that returns the given values.
-func (f *TwoFactorsStoreGetByUserIDFunc) PushReturn(r0 *database.TwoFactor, r1 error) {
-	f.PushHook(func(context.Context, int64) (*database.TwoFactor, error) {
-		return r0, r1
-	})
-}
-
-func (f *TwoFactorsStoreGetByUserIDFunc) nextHook() func(context.Context, int64) (*database.TwoFactor, error) {
-	f.mutex.Lock()
-	defer f.mutex.Unlock()
-
-	if len(f.hooks) == 0 {
-		return f.defaultHook
-	}
-
-	hook := f.hooks[0]
-	f.hooks = f.hooks[1:]
-	return hook
-}
-
-func (f *TwoFactorsStoreGetByUserIDFunc) appendCall(r0 TwoFactorsStoreGetByUserIDFuncCall) {
-	f.mutex.Lock()
-	f.history = append(f.history, r0)
-	f.mutex.Unlock()
-}
-
-// History returns a sequence of TwoFactorsStoreGetByUserIDFuncCall objects
-// describing the invocations of this function.
-func (f *TwoFactorsStoreGetByUserIDFunc) History() []TwoFactorsStoreGetByUserIDFuncCall {
-	f.mutex.Lock()
-	history := make([]TwoFactorsStoreGetByUserIDFuncCall, len(f.history))
-	copy(history, f.history)
-	f.mutex.Unlock()
-
-	return history
-}
-
-// TwoFactorsStoreGetByUserIDFuncCall is an object that describes an
-// invocation of method GetByUserID on an instance of MockTwoFactorsStore.
-type TwoFactorsStoreGetByUserIDFuncCall struct {
-	// Arg0 is the value of the 1st argument passed to this method
-	// invocation.
-	Arg0 context.Context
-	// Arg1 is the value of the 2nd argument passed to this method
-	// invocation.
-	Arg1 int64
-	// Result0 is the value of the 1st result returned from this method
-	// invocation.
-	Result0 *database.TwoFactor
-	// Result1 is the value of the 2nd result returned from this method
-	// invocation.
-	Result1 error
-}
-
-// Args returns an interface slice containing the arguments of this
-// invocation.
-func (c TwoFactorsStoreGetByUserIDFuncCall) Args() []interface{} {
-	return []interface{}{c.Arg0, c.Arg1}
-}
-
-// Results returns an interface slice containing the results of this
-// invocation.
-func (c TwoFactorsStoreGetByUserIDFuncCall) Results() []interface{} {
-	return []interface{}{c.Result0, c.Result1}
-}
-
-// TwoFactorsStoreIsEnabledFunc describes the behavior when the IsEnabled
-// method of the parent MockTwoFactorsStore instance is invoked.
-type TwoFactorsStoreIsEnabledFunc struct {
-	defaultHook func(context.Context, int64) bool
-	hooks       []func(context.Context, int64) bool
-	history     []TwoFactorsStoreIsEnabledFuncCall
-	mutex       sync.Mutex
-}
-
-// IsEnabled delegates to the next hook function in the queue and stores the
-// parameter and result values of this invocation.
-func (m *MockTwoFactorsStore) IsEnabled(v0 context.Context, v1 int64) bool {
-	r0 := m.IsEnabledFunc.nextHook()(v0, v1)
-	m.IsEnabledFunc.appendCall(TwoFactorsStoreIsEnabledFuncCall{v0, v1, r0})
-	return r0
-}
-
-// SetDefaultHook sets function that is called when the IsEnabled method of
-// the parent MockTwoFactorsStore instance is invoked and the hook queue is
-// empty.
-func (f *TwoFactorsStoreIsEnabledFunc) SetDefaultHook(hook func(context.Context, int64) bool) {
-	f.defaultHook = hook
-}
-
-// PushHook adds a function to the end of hook queue. Each invocation of the
-// IsEnabled method of the parent MockTwoFactorsStore instance invokes the
-// hook at the front of the queue and discards it. After the queue is empty,
-// the default hook function is invoked for any future action.
-func (f *TwoFactorsStoreIsEnabledFunc) PushHook(hook func(context.Context, int64) bool) {
-	f.mutex.Lock()
-	f.hooks = append(f.hooks, hook)
-	f.mutex.Unlock()
-}
-
-// SetDefaultReturn calls SetDefaultHook with a function that returns the
-// given values.
-func (f *TwoFactorsStoreIsEnabledFunc) SetDefaultReturn(r0 bool) {
-	f.SetDefaultHook(func(context.Context, int64) bool {
-		return r0
-	})
-}
-
-// PushReturn calls PushHook with a function that returns the given values.
-func (f *TwoFactorsStoreIsEnabledFunc) PushReturn(r0 bool) {
-	f.PushHook(func(context.Context, int64) bool {
-		return r0
-	})
-}
-
-func (f *TwoFactorsStoreIsEnabledFunc) nextHook() func(context.Context, int64) bool {
-	f.mutex.Lock()
-	defer f.mutex.Unlock()
-
-	if len(f.hooks) == 0 {
-		return f.defaultHook
-	}
-
-	hook := f.hooks[0]
-	f.hooks = f.hooks[1:]
-	return hook
-}
-
-func (f *TwoFactorsStoreIsEnabledFunc) appendCall(r0 TwoFactorsStoreIsEnabledFuncCall) {
-	f.mutex.Lock()
-	f.history = append(f.history, r0)
-	f.mutex.Unlock()
-}
-
-// History returns a sequence of TwoFactorsStoreIsEnabledFuncCall objects
-// describing the invocations of this function.
-func (f *TwoFactorsStoreIsEnabledFunc) History() []TwoFactorsStoreIsEnabledFuncCall {
-	f.mutex.Lock()
-	history := make([]TwoFactorsStoreIsEnabledFuncCall, len(f.history))
-	copy(history, f.history)
-	f.mutex.Unlock()
-
-	return history
-}
-
-// TwoFactorsStoreIsEnabledFuncCall is an object that describes an
-// invocation of method IsEnabled on an instance of MockTwoFactorsStore.
-type TwoFactorsStoreIsEnabledFuncCall struct {
-	// Arg0 is the value of the 1st argument passed to this method
-	// invocation.
-	Arg0 context.Context
-	// Arg1 is the value of the 2nd argument passed to this method
-	// invocation.
-	Arg1 int64
-	// Result0 is the value of the 1st result returned from this method
-	// invocation.
-	Result0 bool
-}
-
-// Args returns an interface slice containing the arguments of this
-// invocation.
-func (c TwoFactorsStoreIsEnabledFuncCall) Args() []interface{} {
-	return []interface{}{c.Arg0, c.Arg1}
-}
-
-// Results returns an interface slice containing the results of this
-// invocation.
-func (c TwoFactorsStoreIsEnabledFuncCall) Results() []interface{} {
-	return []interface{}{c.Result0}
-}
-
 // MockUsersStore is a mock implementation of the UsersStore interface (from
 // the package gogs.io/gogs/internal/database) used for unit testing.
 type MockUsersStore struct {
@@ -3968,6 +3567,9 @@ type MockStore struct {
 	// GetRepositoryByNameFunc is an instance of a mock function object
 	// controlling the behavior of the method GetRepositoryByName.
 	GetRepositoryByNameFunc *StoreGetRepositoryByNameFunc
+	// IsTwoFactorEnabledFunc is an instance of a mock function object
+	// controlling the behavior of the method IsTwoFactorEnabled.
+	IsTwoFactorEnabledFunc *StoreIsTwoFactorEnabledFunc
 	// TouchAccessTokenByIDFunc is an instance of a mock function object
 	// controlling the behavior of the method TouchAccessTokenByID.
 	TouchAccessTokenByIDFunc *StoreTouchAccessTokenByIDFunc
@@ -4007,6 +3609,11 @@ func NewMockStore() *MockStore {
 				return
 			},
 		},
+		IsTwoFactorEnabledFunc: &StoreIsTwoFactorEnabledFunc{
+			defaultHook: func(context.Context, int64) (r0 bool) {
+				return
+			},
+		},
 		TouchAccessTokenByIDFunc: &StoreTouchAccessTokenByIDFunc{
 			defaultHook: func(context.Context, int64) (r0 error) {
 				return
@@ -4049,6 +3656,11 @@ func NewStrictMockStore() *MockStore {
 				panic("unexpected invocation of MockStore.GetRepositoryByName")
 			},
 		},
+		IsTwoFactorEnabledFunc: &StoreIsTwoFactorEnabledFunc{
+			defaultHook: func(context.Context, int64) bool {
+				panic("unexpected invocation of MockStore.IsTwoFactorEnabled")
+			},
+		},
 		TouchAccessTokenByIDFunc: &StoreTouchAccessTokenByIDFunc{
 			defaultHook: func(context.Context, int64) error {
 				panic("unexpected invocation of MockStore.TouchAccessTokenByID")
@@ -4079,6 +3691,9 @@ func NewMockStoreFrom(i Store) *MockStore {
 		GetRepositoryByNameFunc: &StoreGetRepositoryByNameFunc{
 			defaultHook: i.GetRepositoryByName,
 		},
+		IsTwoFactorEnabledFunc: &StoreIsTwoFactorEnabledFunc{
+			defaultHook: i.IsTwoFactorEnabled,
+		},
 		TouchAccessTokenByIDFunc: &StoreTouchAccessTokenByIDFunc{
 			defaultHook: i.TouchAccessTokenByID,
 		},
@@ -4763,6 +4378,111 @@ func (c StoreGetRepositoryByNameFuncCall) Results() []interface{} {
 	return []interface{}{c.Result0, c.Result1}
 }
 
+// StoreIsTwoFactorEnabledFunc describes the behavior when the
+// IsTwoFactorEnabled method of the parent MockStore instance is invoked.
+type StoreIsTwoFactorEnabledFunc struct {
+	defaultHook func(context.Context, int64) bool
+	hooks       []func(context.Context, int64) bool
+	history     []StoreIsTwoFactorEnabledFuncCall
+	mutex       sync.Mutex
+}
+
+// IsTwoFactorEnabled delegates to the next hook function in the queue and
+// stores the parameter and result values of this invocation.
+func (m *MockStore) IsTwoFactorEnabled(v0 context.Context, v1 int64) bool {
+	r0 := m.IsTwoFactorEnabledFunc.nextHook()(v0, v1)
+	m.IsTwoFactorEnabledFunc.appendCall(StoreIsTwoFactorEnabledFuncCall{v0, v1, r0})
+	return r0
+}
+
+// SetDefaultHook sets function that is called when the IsTwoFactorEnabled
+// method of the parent MockStore instance is invoked and the hook queue is
+// empty.
+func (f *StoreIsTwoFactorEnabledFunc) SetDefaultHook(hook func(context.Context, int64) bool) {
+	f.defaultHook = hook
+}
+
+// PushHook adds a function to the end of hook queue. Each invocation of the
+// IsTwoFactorEnabled method of the parent MockStore instance invokes the
+// hook at the front of the queue and discards it. After the queue is empty,
+// the default hook function is invoked for any future action.
+func (f *StoreIsTwoFactorEnabledFunc) PushHook(hook func(context.Context, int64) bool) {
+	f.mutex.Lock()
+	f.hooks = append(f.hooks, hook)
+	f.mutex.Unlock()
+}
+
+// SetDefaultReturn calls SetDefaultHook with a function that returns the
+// given values.
+func (f *StoreIsTwoFactorEnabledFunc) SetDefaultReturn(r0 bool) {
+	f.SetDefaultHook(func(context.Context, int64) bool {
+		return r0
+	})
+}
+
+// PushReturn calls PushHook with a function that returns the given values.
+func (f *StoreIsTwoFactorEnabledFunc) PushReturn(r0 bool) {
+	f.PushHook(func(context.Context, int64) bool {
+		return r0
+	})
+}
+
+func (f *StoreIsTwoFactorEnabledFunc) nextHook() func(context.Context, int64) bool {
+	f.mutex.Lock()
+	defer f.mutex.Unlock()
+
+	if len(f.hooks) == 0 {
+		return f.defaultHook
+	}
+
+	hook := f.hooks[0]
+	f.hooks = f.hooks[1:]
+	return hook
+}
+
+func (f *StoreIsTwoFactorEnabledFunc) appendCall(r0 StoreIsTwoFactorEnabledFuncCall) {
+	f.mutex.Lock()
+	f.history = append(f.history, r0)
+	f.mutex.Unlock()
+}
+
+// History returns a sequence of StoreIsTwoFactorEnabledFuncCall objects
+// describing the invocations of this function.
+func (f *StoreIsTwoFactorEnabledFunc) History() []StoreIsTwoFactorEnabledFuncCall {
+	f.mutex.Lock()
+	history := make([]StoreIsTwoFactorEnabledFuncCall, len(f.history))
+	copy(history, f.history)
+	f.mutex.Unlock()
+
+	return history
+}
+
+// StoreIsTwoFactorEnabledFuncCall is an object that describes an invocation
+// of method IsTwoFactorEnabled on an instance of MockStore.
+type StoreIsTwoFactorEnabledFuncCall struct {
+	// Arg0 is the value of the 1st argument passed to this method
+	// invocation.
+	Arg0 context.Context
+	// Arg1 is the value of the 2nd argument passed to this method
+	// invocation.
+	Arg1 int64
+	// Result0 is the value of the 1st result returned from this method
+	// invocation.
+	Result0 bool
+}
+
+// Args returns an interface slice containing the arguments of this
+// invocation.
+func (c StoreIsTwoFactorEnabledFuncCall) Args() []interface{} {
+	return []interface{}{c.Arg0, c.Arg1}
+}
+
+// Results returns an interface slice containing the results of this
+// invocation.
+func (c StoreIsTwoFactorEnabledFuncCall) Results() []interface{} {
+	return []interface{}{c.Result0}
+}
+
 // StoreTouchAccessTokenByIDFunc describes the behavior when the
 // TouchAccessTokenByID method of the parent MockStore instance is invoked.
 type StoreTouchAccessTokenByIDFunc struct {

+ 1 - 1
internal/route/lfs/route.go

@@ -69,7 +69,7 @@ func authenticate(store Store) macaron.Handler {
 			return
 		}
 
-		if err == nil && database.TwoFactors.IsEnabled(c.Req.Context(), user.ID) {
+		if err == nil && store.IsTwoFactorEnabled(c.Req.Context(), user.ID) {
 			c.Error(http.StatusBadRequest, "Users with 2FA enabled are not allowed to authenticate via username and password.")
 			return
 		}

+ 15 - 19
internal/route/lfs/route_test.go

@@ -22,14 +22,13 @@ import (
 
 func TestAuthenticate(t *testing.T) {
 	tests := []struct {
-		name                string
-		header              http.Header
-		mockUsersStore      func() database.UsersStore
-		mockTwoFactorsStore func() database.TwoFactorsStore
-		mockStore           func() *MockStore
-		expStatusCode       int
-		expHeader           http.Header
-		expBody             string
+		name           string
+		header         http.Header
+		mockUsersStore func() database.UsersStore
+		mockStore      func() *MockStore
+		expStatusCode  int
+		expHeader      http.Header
+		expBody        string
 	}{
 		{
 			name:          "no authorization",
@@ -50,10 +49,10 @@ func TestAuthenticate(t *testing.T) {
 				mock.AuthenticateFunc.SetDefaultReturn(&database.User{}, nil)
 				return mock
 			},
-			mockTwoFactorsStore: func() database.TwoFactorsStore {
-				mock := NewMockTwoFactorsStore()
-				mock.IsEnabledFunc.SetDefaultReturn(true)
-				return mock
+			mockStore: func() *MockStore {
+				mockStore := NewMockStore()
+				mockStore.IsTwoFactorEnabledFunc.SetDefaultReturn(true)
+				return mockStore
 			},
 			expStatusCode: http.StatusBadRequest,
 			expHeader:     http.Header{},
@@ -92,10 +91,10 @@ func TestAuthenticate(t *testing.T) {
 				mock.AuthenticateFunc.SetDefaultReturn(&database.User{ID: 1, Name: "unknwon"}, nil)
 				return mock
 			},
-			mockTwoFactorsStore: func() database.TwoFactorsStore {
-				mock := NewMockTwoFactorsStore()
-				mock.IsEnabledFunc.SetDefaultReturn(false)
-				return mock
+			mockStore: func() *MockStore {
+				mockStore := NewMockStore()
+				mockStore.IsTwoFactorEnabledFunc.SetDefaultReturn(false)
+				return mockStore
 			},
 			expStatusCode: http.StatusOK,
 			expHeader:     http.Header{},
@@ -152,9 +151,6 @@ func TestAuthenticate(t *testing.T) {
 			if test.mockUsersStore != nil {
 				database.SetMockUsersStore(t, test.mockUsersStore())
 			}
-			if test.mockTwoFactorsStore != nil {
-				database.SetMockTwoFactorsStore(t, test.mockTwoFactorsStore())
-			}
 			if test.mockStore == nil {
 				test.mockStore = NewMockStore
 			}

+ 7 - 0
internal/route/lfs/store.go

@@ -34,6 +34,9 @@ type Store interface {
 	// GetRepositoryByName returns the repository with given owner and name. It
 	// returns database.ErrRepoNotExist when not found.
 	GetRepositoryByName(ctx context.Context, ownerID int64, name string) (*database.Repository, error)
+
+	// IsTwoFactorEnabled returns true if the user has enabled 2FA.
+	IsTwoFactorEnabled(ctx context.Context, userID int64) bool
 }
 
 type store struct{}
@@ -70,3 +73,7 @@ func (*store) AuthorizeRepositoryAccess(ctx context.Context, userID, repoID int6
 func (*store) GetRepositoryByName(ctx context.Context, ownerID int64, name string) (*database.Repository, error) {
 	return database.Handle.Repositories().GetByName(ctx, ownerID, name)
 }
+
+func (*store) IsTwoFactorEnabled(ctx context.Context, userID int64) bool {
+	return database.Handle.TwoFactors().IsEnabled(ctx, userID)
+}

+ 1 - 1
internal/route/repo/http.go

@@ -152,7 +152,7 @@ func HTTPContexter(store Store) macaron.Handler {
 					return
 				}
 			}
-		} else if database.TwoFactors.IsEnabled(c.Req.Context(), authUser.ID) {
+		} else if store.IsTwoFactorEnabled(c.Req.Context(), authUser.ID) {
 			askCredentials(c, http.StatusUnauthorized, `User with two-factor authentication enabled cannot perform HTTP/HTTPS operations via plain username and password
 Please create and use personal access token on user settings page`)
 			return

+ 7 - 0
internal/route/repo/store.go

@@ -20,6 +20,9 @@ type Store interface {
 	// GetRepositoryByName returns the repository with given owner and name. It
 	// returns database.ErrRepoNotExist when not found.
 	GetRepositoryByName(ctx context.Context, ownerID int64, name string) (*database.Repository, error)
+
+	// IsTwoFactorEnabled returns true if the user has enabled 2FA.
+	IsTwoFactorEnabled(ctx context.Context, userID int64) bool
 }
 
 type store struct{}
@@ -40,3 +43,7 @@ func (*store) TouchAccessTokenByID(ctx context.Context, id int64) error {
 func (*store) GetRepositoryByName(ctx context.Context, ownerID int64, name string) (*database.Repository, error) {
 	return database.Handle.Repositories().GetByName(ctx, ownerID, name)
 }
+
+func (*store) IsTwoFactorEnabled(ctx context.Context, userID int64) bool {
+	return database.Handle.TwoFactors().IsEnabled(ctx, userID)
+}

+ 2 - 2
internal/route/user/auth.go

@@ -187,7 +187,7 @@ func LoginPost(c *context.Context, f form.SignIn) {
 		return
 	}
 
-	if !database.TwoFactors.IsEnabled(c.Req.Context(), u.ID) {
+	if !database.Handle.TwoFactors().IsEnabled(c.Req.Context(), u.ID) {
 		afterLogin(c, u, f.Remember)
 		return
 	}
@@ -214,7 +214,7 @@ func LoginTwoFactorPost(c *context.Context) {
 		return
 	}
 
-	t, err := database.TwoFactors.GetByUserID(c.Req.Context(), userID)
+	t, err := database.Handle.TwoFactors().GetByUserID(c.Req.Context(), userID)
 	if err != nil {
 		c.Error(err, "get two factor by user ID")
 		return

+ 6 - 6
internal/route/user/setting.go

@@ -398,7 +398,7 @@ func SettingsSecurity(c *context.Context) {
 	c.Title("settings.security")
 	c.PageIs("SettingsSecurity")
 
-	t, err := database.TwoFactors.GetByUserID(c.Req.Context(), c.UserID())
+	t, err := database.Handle.TwoFactors().GetByUserID(c.Req.Context(), c.UserID())
 	if err != nil && !database.IsErrTwoFactorNotFound(err) {
 		c.Errorf(err, "get two factor by user ID")
 		return
@@ -409,7 +409,7 @@ func SettingsSecurity(c *context.Context) {
 }
 
 func SettingsTwoFactorEnable(c *context.Context) {
-	if database.TwoFactors.IsEnabled(c.Req.Context(), c.User.ID) {
+	if database.Handle.TwoFactors().IsEnabled(c.Req.Context(), c.User.ID) {
 		c.NotFound()
 		return
 	}
@@ -466,7 +466,7 @@ func SettingsTwoFactorEnablePost(c *context.Context) {
 		return
 	}
 
-	if err := database.TwoFactors.Create(c.Req.Context(), c.UserID(), conf.Security.SecretKey, secret); err != nil {
+	if err := database.Handle.TwoFactors().Create(c.Req.Context(), c.UserID(), conf.Security.SecretKey, secret); err != nil {
 		c.Flash.Error(c.Tr("settings.two_factor_enable_error", err))
 		c.RedirectSubpath("/user/settings/security/two_factor_enable")
 		return
@@ -479,7 +479,7 @@ func SettingsTwoFactorEnablePost(c *context.Context) {
 }
 
 func SettingsTwoFactorRecoveryCodes(c *context.Context) {
-	if !database.TwoFactors.IsEnabled(c.Req.Context(), c.User.ID) {
+	if !database.Handle.TwoFactors().IsEnabled(c.Req.Context(), c.User.ID) {
 		c.NotFound()
 		return
 	}
@@ -498,7 +498,7 @@ func SettingsTwoFactorRecoveryCodes(c *context.Context) {
 }
 
 func SettingsTwoFactorRecoveryCodesPost(c *context.Context) {
-	if !database.TwoFactors.IsEnabled(c.Req.Context(), c.User.ID) {
+	if !database.Handle.TwoFactors().IsEnabled(c.Req.Context(), c.User.ID) {
 		c.NotFound()
 		return
 	}
@@ -513,7 +513,7 @@ func SettingsTwoFactorRecoveryCodesPost(c *context.Context) {
 }
 
 func SettingsTwoFactorDisable(c *context.Context) {
-	if !database.TwoFactors.IsEnabled(c.Req.Context(), c.User.ID) {
+	if !database.Handle.TwoFactors().IsEnabled(c.Req.Context(), c.User.ID) {
 		c.NotFound()
 		return
 	}

+ 0 - 1
mockgen.yaml

@@ -37,7 +37,6 @@ mocks:
       - path: gogs.io/gogs/internal/database
         interfaces:
           - UsersStore
-          - TwoFactorsStore
       - path: gogs.io/gogs/internal/route/lfs
         interfaces:
           - Store