mirror of
https://github.com/netbirdio/netbird.git
synced 2026-04-27 20:56:44 +00:00
initial implementation
This commit is contained in:
@@ -13,6 +13,8 @@ type Store interface {
|
||||
Get(ctx context.Context, accountID string, offset, limit int, descending bool) ([]*Event, error)
|
||||
// Close the sink flushing events if necessary
|
||||
Close(ctx context.Context) error
|
||||
// UpdateUserID re-keys all references to oldUserID in events and deleted_users tables.
|
||||
UpdateUserID(ctx context.Context, oldUserID, newUserID string) error
|
||||
}
|
||||
|
||||
// InMemoryEventStore implements the Store interface storing data in-memory
|
||||
@@ -55,3 +57,8 @@ func (store *InMemoryEventStore) Close(_ context.Context) error {
|
||||
store.events = make([]*Event, 0)
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateUserID is a no-op for the in-memory store.
|
||||
func (store *InMemoryEventStore) UpdateUserID(_ context.Context, _, _ string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -227,6 +227,32 @@ func (store *Store) saveDeletedUserEmailAndNameInEncrypted(event *activity.Event
|
||||
return event.Meta, nil
|
||||
}
|
||||
|
||||
// UpdateUserID updates all references to oldUserID in events and deleted_users tables.
|
||||
func (store *Store) UpdateUserID(ctx context.Context, oldUserID, newUserID string) error {
|
||||
return store.db.Transaction(func(tx *gorm.DB) error {
|
||||
if err := tx.Model(&activity.Event{}).
|
||||
Where("initiator_id = ?", oldUserID).
|
||||
Update("initiator_id", newUserID).Error; err != nil {
|
||||
return fmt.Errorf("update events.initiator_id: %w", err)
|
||||
}
|
||||
|
||||
if err := tx.Model(&activity.Event{}).
|
||||
Where("target_id = ?", oldUserID).
|
||||
Update("target_id", newUserID).Error; err != nil {
|
||||
return fmt.Errorf("update events.target_id: %w", err)
|
||||
}
|
||||
|
||||
// Raw exec: GORM can't update a PK via Model().Update()
|
||||
if err := tx.Exec(
|
||||
"UPDATE deleted_users SET id = ? WHERE id = ?", newUserID, oldUserID,
|
||||
).Error; err != nil {
|
||||
return fmt.Errorf("update deleted_users.id: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// Close the Store
|
||||
func (store *Store) Close(_ context.Context) error {
|
||||
if store.db != nil {
|
||||
|
||||
152
management/server/idp/migration/migration.go
Normal file
152
management/server/idp/migration/migration.go
Normal file
@@ -0,0 +1,152 @@
|
||||
// Package migration provides utility functions for migrating from an external IdP
|
||||
// to NetBird's embedded DEX-based IdP. It re-keys user IDs in the main store and
|
||||
// activity store so that they match DEX's encoded format.
|
||||
package migration
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/netbirdio/netbird/idp/dex"
|
||||
"github.com/netbirdio/netbird/management/server/types"
|
||||
)
|
||||
|
||||
// MainStoreUpdater is the subset of the main store needed for migration.
|
||||
type MainStoreUpdater interface {
|
||||
ListUsers(ctx context.Context) ([]*types.User, error)
|
||||
UpdateUserID(ctx context.Context, accountID, oldUserID, newUserID string) error
|
||||
}
|
||||
|
||||
// ActivityStoreUpdater is the subset of the activity store needed for migration.
|
||||
type ActivityStoreUpdater interface {
|
||||
UpdateUserID(ctx context.Context, oldUserID, newUserID string) error
|
||||
}
|
||||
|
||||
// Config holds migration parameters.
|
||||
type Config struct {
|
||||
ConnectorID string
|
||||
DryRun bool
|
||||
MainStore MainStoreUpdater
|
||||
ActivityStore ActivityStoreUpdater // nil if activity store is unavailable
|
||||
}
|
||||
|
||||
// Result holds migration outcome counts.
|
||||
type Result struct {
|
||||
Migrated int
|
||||
Skipped int
|
||||
}
|
||||
|
||||
// progressInterval controls how often progress is logged for large user counts.
|
||||
const progressInterval = 100
|
||||
|
||||
// Migrate re-keys every user ID in both stores so that it encodes the given
|
||||
// connector ID. Already-migrated users (detectable via DecodeDexUserID) are
|
||||
// skipped, making the operation idempotent.
|
||||
func Migrate(ctx context.Context, cfg *Config) (*Result, error) {
|
||||
if cfg.ConnectorID == "" {
|
||||
return nil, fmt.Errorf("connector ID must not be empty")
|
||||
}
|
||||
|
||||
users, err := cfg.MainStore.ListUsers(ctx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("list users: %w", err)
|
||||
}
|
||||
|
||||
if len(users) == 0 {
|
||||
log.Info("no users found, nothing to migrate")
|
||||
return &Result{}, nil
|
||||
}
|
||||
|
||||
log.Infof("found %d users to process", len(users))
|
||||
|
||||
// Reconciliation pass: fix activity store for users already migrated in
|
||||
// the main DB but whose activity references may still use old IDs (from
|
||||
// a previous partial failure).
|
||||
if cfg.ActivityStore != nil && !cfg.DryRun {
|
||||
if err := reconcileActivityStore(ctx, cfg.ActivityStore, users); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
res := &Result{}
|
||||
|
||||
for i, user := range users {
|
||||
if user.Id == "" {
|
||||
log.Warnf("skipping user with empty ID in account %s", user.AccountID)
|
||||
res.Skipped++
|
||||
continue
|
||||
}
|
||||
|
||||
_, _, decErr := dex.DecodeDexUserID(user.Id)
|
||||
if decErr == nil {
|
||||
// Already encoded in DEX format — skip.
|
||||
res.Skipped++
|
||||
continue
|
||||
}
|
||||
|
||||
newUserID := dex.EncodeDexUserID(user.Id, cfg.ConnectorID)
|
||||
|
||||
if cfg.DryRun {
|
||||
log.Infof("[DRY RUN] would migrate user %s -> %s (account: %s)",
|
||||
user.Id, newUserID, user.AccountID)
|
||||
res.Migrated++
|
||||
continue
|
||||
}
|
||||
|
||||
if err := migrateUser(ctx, cfg, user.Id, user.AccountID, newUserID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res.Migrated++
|
||||
|
||||
if (i+1)%progressInterval == 0 {
|
||||
log.Infof("progress: %d/%d users processed", i+1, len(users))
|
||||
}
|
||||
}
|
||||
|
||||
if cfg.DryRun {
|
||||
log.Infof("[DRY RUN] migration summary: %d users would be migrated, %d already migrated",
|
||||
res.Migrated, res.Skipped)
|
||||
} else {
|
||||
log.Infof("migration complete: %d users migrated, %d already migrated",
|
||||
res.Migrated, res.Skipped)
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// reconcileActivityStore updates activity store references for users already
|
||||
// migrated in the main DB whose activity entries may still use old IDs from a
|
||||
// previous partial failure.
|
||||
func reconcileActivityStore(ctx context.Context, activityStore ActivityStoreUpdater, users []*types.User) error {
|
||||
for _, user := range users {
|
||||
originalID, _, err := dex.DecodeDexUserID(user.Id)
|
||||
if err != nil {
|
||||
// Not yet migrated — will be handled in the main loop.
|
||||
continue
|
||||
}
|
||||
if err := activityStore.UpdateUserID(ctx, originalID, user.Id); err != nil {
|
||||
return fmt.Errorf("reconcile activity store for user %s: %w", user.Id, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// migrateUser updates a single user's ID in both the main store and the activity store.
|
||||
func migrateUser(ctx context.Context, cfg *Config, oldID, accountID, newID string) error {
|
||||
if err := cfg.MainStore.UpdateUserID(ctx, accountID, oldID, newID); err != nil {
|
||||
return fmt.Errorf("update user ID for user %s: %w", oldID, err)
|
||||
}
|
||||
|
||||
if cfg.ActivityStore == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := cfg.ActivityStore.UpdateUserID(ctx, oldID, newID); err != nil {
|
||||
return fmt.Errorf("update activity store user ID for user %s: %w", oldID, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
287
management/server/idp/migration/migration_test.go
Normal file
287
management/server/idp/migration/migration_test.go
Normal file
@@ -0,0 +1,287 @@
|
||||
package migration
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/netbirdio/netbird/idp/dex"
|
||||
"github.com/netbirdio/netbird/management/server/types"
|
||||
)
|
||||
|
||||
const testConnectorID = "oidc"
|
||||
|
||||
// mockMainStore implements MainStoreUpdater for testing.
|
||||
type mockMainStore struct {
|
||||
users []*types.User
|
||||
listErr error
|
||||
updateErr error
|
||||
updateCalls []updateCall
|
||||
}
|
||||
|
||||
type updateCall struct {
|
||||
AccountID string
|
||||
OldID string
|
||||
NewID string
|
||||
}
|
||||
|
||||
func (m *mockMainStore) ListUsers(_ context.Context) ([]*types.User, error) {
|
||||
return m.users, m.listErr
|
||||
}
|
||||
|
||||
func (m *mockMainStore) UpdateUserID(_ context.Context, accountID, oldUserID, newUserID string) error {
|
||||
m.updateCalls = append(m.updateCalls, updateCall{accountID, oldUserID, newUserID})
|
||||
return m.updateErr
|
||||
}
|
||||
|
||||
// mockActivityStore implements ActivityStoreUpdater for testing.
|
||||
type mockActivityStore struct {
|
||||
updateErr error
|
||||
updateCalls []activityUpdateCall
|
||||
}
|
||||
|
||||
type activityUpdateCall struct {
|
||||
OldID string
|
||||
NewID string
|
||||
}
|
||||
|
||||
func (m *mockActivityStore) UpdateUserID(_ context.Context, oldUserID, newUserID string) error {
|
||||
m.updateCalls = append(m.updateCalls, activityUpdateCall{oldUserID, newUserID})
|
||||
return m.updateErr
|
||||
}
|
||||
|
||||
func TestMigrate_NormalMigration(t *testing.T) {
|
||||
mainStore := &mockMainStore{
|
||||
users: []*types.User{
|
||||
{Id: "user-1", AccountID: "acc-1"},
|
||||
{Id: "user-2", AccountID: "acc-1"},
|
||||
},
|
||||
}
|
||||
actStore := &mockActivityStore{}
|
||||
|
||||
res, err := Migrate(context.Background(), &Config{
|
||||
ConnectorID: testConnectorID,
|
||||
MainStore: mainStore,
|
||||
ActivityStore: actStore,
|
||||
})
|
||||
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 2, res.Migrated)
|
||||
assert.Equal(t, 0, res.Skipped)
|
||||
assert.Len(t, mainStore.updateCalls, 2)
|
||||
assert.Len(t, actStore.updateCalls, 2)
|
||||
|
||||
// Verify the new IDs are DEX-encoded
|
||||
for _, call := range mainStore.updateCalls {
|
||||
userID, connID, decErr := dex.DecodeDexUserID(call.NewID)
|
||||
require.NoError(t, decErr)
|
||||
assert.Equal(t, testConnectorID, connID)
|
||||
assert.Equal(t, call.OldID, userID)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMigrate_SkipAlreadyMigrated(t *testing.T) {
|
||||
alreadyMigrated := dex.EncodeDexUserID("original-user", testConnectorID)
|
||||
mainStore := &mockMainStore{
|
||||
users: []*types.User{
|
||||
{Id: alreadyMigrated, AccountID: "acc-1"},
|
||||
{Id: "not-migrated", AccountID: "acc-1"},
|
||||
},
|
||||
}
|
||||
actStore := &mockActivityStore{}
|
||||
|
||||
res, err := Migrate(context.Background(), &Config{
|
||||
ConnectorID: testConnectorID,
|
||||
MainStore: mainStore,
|
||||
ActivityStore: actStore,
|
||||
})
|
||||
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 1, res.Migrated)
|
||||
assert.Equal(t, 1, res.Skipped)
|
||||
assert.Len(t, mainStore.updateCalls, 1)
|
||||
assert.Equal(t, "not-migrated", mainStore.updateCalls[0].OldID)
|
||||
}
|
||||
|
||||
func TestMigrate_DryRun(t *testing.T) {
|
||||
mainStore := &mockMainStore{
|
||||
users: []*types.User{
|
||||
{Id: "user-1", AccountID: "acc-1"},
|
||||
},
|
||||
}
|
||||
actStore := &mockActivityStore{}
|
||||
|
||||
res, err := Migrate(context.Background(), &Config{
|
||||
ConnectorID: testConnectorID,
|
||||
DryRun: true,
|
||||
MainStore: mainStore,
|
||||
ActivityStore: actStore,
|
||||
})
|
||||
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 1, res.Migrated)
|
||||
// No actual updates should have been made
|
||||
assert.Empty(t, mainStore.updateCalls)
|
||||
assert.Empty(t, actStore.updateCalls)
|
||||
}
|
||||
|
||||
func TestMigrate_EmptyUserList(t *testing.T) {
|
||||
mainStore := &mockMainStore{users: []*types.User{}}
|
||||
actStore := &mockActivityStore{}
|
||||
|
||||
res, err := Migrate(context.Background(), &Config{
|
||||
ConnectorID: testConnectorID,
|
||||
MainStore: mainStore,
|
||||
ActivityStore: actStore,
|
||||
})
|
||||
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 0, res.Migrated)
|
||||
assert.Equal(t, 0, res.Skipped)
|
||||
}
|
||||
|
||||
func TestMigrate_EmptyUserID(t *testing.T) {
|
||||
mainStore := &mockMainStore{
|
||||
users: []*types.User{
|
||||
{Id: "", AccountID: "acc-1"},
|
||||
{Id: "user-1", AccountID: "acc-1"},
|
||||
},
|
||||
}
|
||||
actStore := &mockActivityStore{}
|
||||
|
||||
res, err := Migrate(context.Background(), &Config{
|
||||
ConnectorID: testConnectorID,
|
||||
MainStore: mainStore,
|
||||
ActivityStore: actStore,
|
||||
})
|
||||
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 1, res.Migrated)
|
||||
assert.Equal(t, 1, res.Skipped)
|
||||
}
|
||||
|
||||
func TestMigrate_NilActivityStore(t *testing.T) {
|
||||
mainStore := &mockMainStore{
|
||||
users: []*types.User{
|
||||
{Id: "user-1", AccountID: "acc-1"},
|
||||
},
|
||||
}
|
||||
|
||||
res, err := Migrate(context.Background(), &Config{
|
||||
ConnectorID: testConnectorID,
|
||||
MainStore: mainStore,
|
||||
// ActivityStore is nil
|
||||
})
|
||||
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 1, res.Migrated)
|
||||
assert.Len(t, mainStore.updateCalls, 1)
|
||||
}
|
||||
|
||||
func TestMigrate_EmptyConnectorID(t *testing.T) {
|
||||
mainStore := &mockMainStore{}
|
||||
|
||||
_, err := Migrate(context.Background(), &Config{
|
||||
ConnectorID: "",
|
||||
MainStore: mainStore,
|
||||
})
|
||||
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "connector ID must not be empty")
|
||||
}
|
||||
|
||||
func TestMigrate_ListUsersError(t *testing.T) {
|
||||
mainStore := &mockMainStore{listErr: errors.New("db error")}
|
||||
|
||||
_, err := Migrate(context.Background(), &Config{
|
||||
ConnectorID: testConnectorID,
|
||||
MainStore: mainStore,
|
||||
})
|
||||
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "list users")
|
||||
}
|
||||
|
||||
func TestMigrate_UpdateError(t *testing.T) {
|
||||
mainStore := &mockMainStore{
|
||||
users: []*types.User{{Id: "user-1", AccountID: "acc-1"}},
|
||||
updateErr: errors.New("tx error"),
|
||||
}
|
||||
|
||||
_, err := Migrate(context.Background(), &Config{
|
||||
ConnectorID: testConnectorID,
|
||||
MainStore: mainStore,
|
||||
})
|
||||
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "update user ID")
|
||||
}
|
||||
|
||||
func TestMigrate_Reconciliation(t *testing.T) {
|
||||
// Simulate a previously migrated user whose activity store wasn't updated
|
||||
alreadyMigrated := dex.EncodeDexUserID("original-user", testConnectorID)
|
||||
mainStore := &mockMainStore{
|
||||
users: []*types.User{
|
||||
{Id: alreadyMigrated, AccountID: "acc-1"},
|
||||
},
|
||||
}
|
||||
actStore := &mockActivityStore{}
|
||||
|
||||
res, err := Migrate(context.Background(), &Config{
|
||||
ConnectorID: testConnectorID,
|
||||
MainStore: mainStore,
|
||||
ActivityStore: actStore,
|
||||
})
|
||||
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 0, res.Migrated)
|
||||
assert.Equal(t, 1, res.Skipped)
|
||||
// Reconciliation should have called activity store with the original -> new mapping
|
||||
require.Len(t, actStore.updateCalls, 1)
|
||||
assert.Equal(t, "original-user", actStore.updateCalls[0].OldID)
|
||||
assert.Equal(t, alreadyMigrated, actStore.updateCalls[0].NewID)
|
||||
}
|
||||
|
||||
func TestMigrate_Idempotent(t *testing.T) {
|
||||
mainStore := &mockMainStore{
|
||||
users: []*types.User{
|
||||
{Id: "user-1", AccountID: "acc-1"},
|
||||
{Id: "user-2", AccountID: "acc-1"},
|
||||
},
|
||||
}
|
||||
actStore := &mockActivityStore{}
|
||||
|
||||
// First run
|
||||
res1, err := Migrate(context.Background(), &Config{
|
||||
ConnectorID: testConnectorID,
|
||||
MainStore: mainStore,
|
||||
ActivityStore: actStore,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 2, res1.Migrated)
|
||||
|
||||
// Simulate that the store now has the migrated IDs
|
||||
for _, call := range mainStore.updateCalls {
|
||||
for i, u := range mainStore.users {
|
||||
if u.Id == call.OldID {
|
||||
mainStore.users[i].Id = call.NewID
|
||||
}
|
||||
}
|
||||
}
|
||||
mainStore.updateCalls = nil
|
||||
actStore.updateCalls = nil
|
||||
|
||||
// Second run should skip all
|
||||
res2, err := Migrate(context.Background(), &Config{
|
||||
ConnectorID: testConnectorID,
|
||||
MainStore: mainStore,
|
||||
ActivityStore: actStore,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 0, res2.Migrated)
|
||||
assert.Equal(t, 2, res2.Skipped)
|
||||
assert.Empty(t, mainStore.updateCalls)
|
||||
}
|
||||
@@ -3445,6 +3445,80 @@ func (s *SqlStore) GetDB() *gorm.DB {
|
||||
return s.db
|
||||
}
|
||||
|
||||
// ListUsers returns all users across all accounts with decrypted sensitive fields.
|
||||
func (s *SqlStore) ListUsers(ctx context.Context) ([]*types.User, error) {
|
||||
var users []*types.User
|
||||
if err := s.db.Find(&users).Error; err != nil {
|
||||
return nil, status.Errorf(status.Internal, "failed to list users")
|
||||
}
|
||||
for _, user := range users {
|
||||
if err := user.DecryptSensitiveData(s.fieldEncrypt); err != nil {
|
||||
log.WithContext(ctx).Errorf("failed to decrypt user data for user %s: %v", user.Id, err)
|
||||
return nil, status.Errorf(status.Internal, "failed to decrypt user data")
|
||||
}
|
||||
}
|
||||
return users, nil
|
||||
}
|
||||
|
||||
// txDeferFKConstraints defers foreign key constraint checks for the duration of the transaction.
|
||||
// MySQL is already handled by s.transaction (SET FOREIGN_KEY_CHECKS = 0).
|
||||
func (s *SqlStore) txDeferFKConstraints(tx *gorm.DB) error {
|
||||
switch s.storeEngine {
|
||||
case types.PostgresStoreEngine:
|
||||
return tx.Exec("SET CONSTRAINTS ALL DEFERRED").Error
|
||||
case types.SqliteStoreEngine:
|
||||
return tx.Exec("PRAGMA defer_foreign_keys = ON").Error
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// UpdateUserID re-keys a user's ID from oldUserID to newUserID, updating all FK references first,
|
||||
// then the users.id primary key last. All updates happen in a single transaction.
|
||||
func (s *SqlStore) UpdateUserID(ctx context.Context, accountID, oldUserID, newUserID string) error {
|
||||
type fkUpdate struct {
|
||||
model any
|
||||
column string
|
||||
where string
|
||||
}
|
||||
|
||||
updates := []fkUpdate{
|
||||
{&types.PersonalAccessToken{}, "user_id", "user_id = ?"},
|
||||
{&types.PersonalAccessToken{}, "created_by", "created_by = ?"},
|
||||
{&nbpeer.Peer{}, "user_id", "user_id = ?"},
|
||||
{&types.UserInviteRecord{}, "created_by", "created_by = ?"},
|
||||
{&types.Account{}, "created_by", "created_by = ?"},
|
||||
{&types.ProxyAccessToken{}, "created_by", "created_by = ?"},
|
||||
{&types.Job{}, "triggered_by", "triggered_by = ?"},
|
||||
{&types.PolicyRule{}, "authorized_user", "authorized_user = ?"},
|
||||
{&accesslogs.AccessLogEntry{}, "user_id", "user_id = ?"},
|
||||
}
|
||||
|
||||
err := s.transaction(func(tx *gorm.DB) error {
|
||||
if err := s.txDeferFKConstraints(tx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, u := range updates {
|
||||
if err := tx.Model(u.model).Where(u.where, oldUserID).Update(u.column, newUserID).Error; err != nil {
|
||||
return fmt.Errorf("update %s: %w", u.column, err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := tx.Model(&types.User{}).Where(accountAndIDQueryCondition, accountID, oldUserID).Update("id", newUserID).Error; err != nil {
|
||||
return fmt.Errorf("update users: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
log.WithContext(ctx).Errorf("failed to update user ID in the store: %s", err)
|
||||
return status.Errorf(status.Internal, "failed to update user ID in store")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetFieldEncrypt sets the field encryptor for encrypting sensitive user data.
|
||||
func (s *SqlStore) SetFieldEncrypt(enc *crypt.FieldEncrypt) {
|
||||
s.fieldEncrypt = enc
|
||||
|
||||
@@ -275,6 +275,11 @@ type Store interface {
|
||||
|
||||
// GetCustomDomainsCounts returns the total and validated custom domain counts.
|
||||
GetCustomDomainsCounts(ctx context.Context) (total int64, validated int64, err error)
|
||||
|
||||
// ListUsers returns all users across all accounts.
|
||||
ListUsers(ctx context.Context) ([]*types.User, error)
|
||||
// UpdateUserID re-keys a user's ID from oldUserID to newUserID, updating all foreign key references.
|
||||
UpdateUserID(ctx context.Context, accountID, oldUserID, newUserID string) error
|
||||
}
|
||||
|
||||
const (
|
||||
|
||||
@@ -1109,21 +1109,6 @@ func (mr *MockStoreMockRecorder) GetAccountServices(ctx, lockStrength, accountID
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAccountServices", reflect.TypeOf((*MockStore)(nil).GetAccountServices), ctx, lockStrength, accountID)
|
||||
}
|
||||
|
||||
// GetServicesByAccountID mocks base method.
|
||||
func (m *MockStore) GetServicesByAccountID(ctx context.Context, lockStrength LockingStrength, accountID string) ([]*reverseproxy.Service, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetServicesByAccountID", ctx, lockStrength, accountID)
|
||||
ret0, _ := ret[0].([]*reverseproxy.Service)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// GetServicesByAccountID indicates an expected call of GetServicesByAccountID.
|
||||
func (mr *MockStoreMockRecorder) GetServicesByAccountID(ctx, lockStrength, accountID interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetServicesByAccountID", reflect.TypeOf((*MockStore)(nil).GetServicesByAccountID), ctx, lockStrength, accountID)
|
||||
}
|
||||
|
||||
// GetAccountSettings mocks base method.
|
||||
func (m *MockStore) GetAccountSettings(ctx context.Context, lockStrength LockingStrength, accountID string) (*types2.Settings, error) {
|
||||
m.ctrl.T.Helper()
|
||||
@@ -1288,6 +1273,22 @@ func (mr *MockStoreMockRecorder) GetCustomDomain(ctx, accountID, domainID interf
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCustomDomain", reflect.TypeOf((*MockStore)(nil).GetCustomDomain), ctx, accountID, domainID)
|
||||
}
|
||||
|
||||
// GetCustomDomainsCounts mocks base method.
|
||||
func (m *MockStore) GetCustomDomainsCounts(ctx context.Context) (int64, int64, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetCustomDomainsCounts", ctx)
|
||||
ret0, _ := ret[0].(int64)
|
||||
ret1, _ := ret[1].(int64)
|
||||
ret2, _ := ret[2].(error)
|
||||
return ret0, ret1, ret2
|
||||
}
|
||||
|
||||
// GetCustomDomainsCounts indicates an expected call of GetCustomDomainsCounts.
|
||||
func (mr *MockStoreMockRecorder) GetCustomDomainsCounts(ctx interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCustomDomainsCounts", reflect.TypeOf((*MockStore)(nil).GetCustomDomainsCounts), ctx)
|
||||
}
|
||||
|
||||
// GetDNSRecordByID mocks base method.
|
||||
func (m *MockStore) GetDNSRecordByID(ctx context.Context, lockStrength LockingStrength, accountID, zoneID, recordID string) (*records.Record, error) {
|
||||
m.ctrl.T.Helper()
|
||||
@@ -1872,22 +1873,6 @@ func (mr *MockStoreMockRecorder) GetServiceTargetByTargetID(ctx, lockStrength, a
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetServiceTargetByTargetID", reflect.TypeOf((*MockStore)(nil).GetServiceTargetByTargetID), ctx, lockStrength, accountID, targetID)
|
||||
}
|
||||
|
||||
// GetCustomDomainsCounts mocks base method.
|
||||
func (m *MockStore) GetCustomDomainsCounts(ctx context.Context) (int64, int64, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetCustomDomainsCounts", ctx)
|
||||
ret0, _ := ret[0].(int64)
|
||||
ret1, _ := ret[1].(int64)
|
||||
ret2, _ := ret[2].(error)
|
||||
return ret0, ret1, ret2
|
||||
}
|
||||
|
||||
// GetCustomDomainsCounts indicates an expected call of GetCustomDomainsCounts.
|
||||
func (mr *MockStoreMockRecorder) GetCustomDomainsCounts(ctx interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCustomDomainsCounts", reflect.TypeOf((*MockStore)(nil).GetCustomDomainsCounts), ctx)
|
||||
}
|
||||
|
||||
// GetServices mocks base method.
|
||||
func (m *MockStore) GetServices(ctx context.Context, lockStrength LockingStrength) ([]*reverseproxy.Service, error) {
|
||||
m.ctrl.T.Helper()
|
||||
@@ -1903,6 +1888,21 @@ func (mr *MockStoreMockRecorder) GetServices(ctx, lockStrength interface{}) *gom
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetServices", reflect.TypeOf((*MockStore)(nil).GetServices), ctx, lockStrength)
|
||||
}
|
||||
|
||||
// GetServicesByAccountID mocks base method.
|
||||
func (m *MockStore) GetServicesByAccountID(ctx context.Context, lockStrength LockingStrength, accountID string) ([]*reverseproxy.Service, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetServicesByAccountID", ctx, lockStrength, accountID)
|
||||
ret0, _ := ret[0].([]*reverseproxy.Service)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// GetServicesByAccountID indicates an expected call of GetServicesByAccountID.
|
||||
func (mr *MockStoreMockRecorder) GetServicesByAccountID(ctx, lockStrength, accountID interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetServicesByAccountID", reflect.TypeOf((*MockStore)(nil).GetServicesByAccountID), ctx, lockStrength, accountID)
|
||||
}
|
||||
|
||||
// GetSetupKeyByID mocks base method.
|
||||
func (m *MockStore) GetSetupKeyByID(ctx context.Context, lockStrength LockingStrength, accountID, setupKeyID string) (*types2.SetupKey, error) {
|
||||
m.ctrl.T.Helper()
|
||||
@@ -2231,6 +2231,21 @@ func (mr *MockStoreMockRecorder) ListFreeDomains(ctx, accountID interface{}) *go
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListFreeDomains", reflect.TypeOf((*MockStore)(nil).ListFreeDomains), ctx, accountID)
|
||||
}
|
||||
|
||||
// ListUsers mocks base method.
|
||||
func (m *MockStore) ListUsers(ctx context.Context) ([]*types2.User, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "ListUsers", ctx)
|
||||
ret0, _ := ret[0].([]*types2.User)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// ListUsers indicates an expected call of ListUsers.
|
||||
func (mr *MockStoreMockRecorder) ListUsers(ctx interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListUsers", reflect.TypeOf((*MockStore)(nil).ListUsers), ctx)
|
||||
}
|
||||
|
||||
// MarkAccountPrimary mocks base method.
|
||||
func (m *MockStore) MarkAccountPrimary(ctx context.Context, accountID string) error {
|
||||
m.ctrl.T.Helper()
|
||||
@@ -2776,6 +2791,20 @@ func (mr *MockStoreMockRecorder) UpdateService(ctx, service interface{}) *gomock
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateService", reflect.TypeOf((*MockStore)(nil).UpdateService), ctx, service)
|
||||
}
|
||||
|
||||
// UpdateUserID mocks base method.
|
||||
func (m *MockStore) UpdateUserID(ctx context.Context, accountID, oldUserID, newUserID string) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "UpdateUserID", ctx, accountID, oldUserID, newUserID)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// UpdateUserID indicates an expected call of UpdateUserID.
|
||||
func (mr *MockStoreMockRecorder) UpdateUserID(ctx, accountID, oldUserID, newUserID interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateUserID", reflect.TypeOf((*MockStore)(nil).UpdateUserID), ctx, accountID, oldUserID, newUserID)
|
||||
}
|
||||
|
||||
// UpdateZone mocks base method.
|
||||
func (m *MockStore) UpdateZone(ctx context.Context, zone *zones.Zone) error {
|
||||
m.ctrl.T.Helper()
|
||||
|
||||
Reference in New Issue
Block a user