Repository: influxdata/influxdb
File: authorization/storage.go (Go)

Code before:
	hasherVariant := influxdb2_algo.NewVariant(variantName)
	decoderVariants = append(decoderVariants, hasherVariant)
	for _, variant := range foundVariants {
		// Avoid having 2 hasherVariant decoders.
		if variant != hasherVariant {
			decoderVariants = append(decoderVariants, variant)
		}
	}

	hasher, err := NewAuthorizationHasher(WithHasherVariant(hasherVariant), WithDecoderVariants(decoderVariants))
	if err != nil {
		return nil, fmt.Errorf("error creating authorization hasher for authorization store: %w", err)
	}

	return hasher, nil
}

type MigrationStore interface {
	View(ctx context.Context, fn func(kv.Tx) error) error
	Update(ctx context.Context, fn func(kv.Tx) error) error
}

// HashedTokenMigration migrates any unhashed tokens in the store to hashed tokens. If store
// is not nil, it will be used as the KV store for migration instead of the one passed to
// NewStore.
func (s *Store) HashedTokenMigration(ctx context.Context, store MigrationStore) error {
	if store == nil {
		store = s
	}

	if !s.useHashedTokens || s.readOnly {
		return nil
	}

	// Figure out which authorization records need to be updated.
	var authsNeedingUpdate []*influxdb.Authorization
	err := store.View(ctx, func(tx kv.Tx) error {
		return s.forEachAuthorization(ctx, tx, nil, func(a *influxdb.Authorization) bool {
			if a.IsHashedTokenClear() {
				if a.IsTokenSet() {
					authsNeedingUpdate = append(authsNeedingUpdate, a)
				} else {
					s.log.Warn("during hashed token migration, found authorization without any token set", zap.Uint64("ID", uint64(a.ID)), zap.String("description", a.Description))
				}
			}
			return true
		})
	})
	if err != nil {
		return err
	}

Review comment (suggestion):
The distinction between `s` and `store` is confusing. It's not clear why we have two objects, one of which can substitute for the other.

Code after:

	return hasher, nil
}

// TokenMigrator allows exposing migrateTokens publicly so that token migration and token migration only
// can be performed with different parameters than the authorization Store used for other operations.
type TokenMigrator struct {
	store *Store
}

type TokenMigratorOption func(*Store)

// WithMigratorStore specifies an alternate kvStore to use for token migration. This is useful for
// specifying a writeable kv.Store for migration when a Store's normal kv.Store may be in read-only mode.
func WithMigratorStore(kvStore kv.Store) TokenMigratorOption {
	return func(s *Store) {
		s.kvStore = kvStore
	}
}

// NewTokenMigrator creates a TokenMigrator based on s and any opts passed.
func (s *Store) NewTokenMigrator(opts ...TokenMigratorOption) *TokenMigrator {
	storeCopy := &Store{}
	*storeCopy = *s // shallow copy is sufficient

	for _, o := range opts {
		o(storeCopy)
	}

	return &TokenMigrator{
		store: storeCopy,
	}
}

// MigrateTokens migrates any unhashed tokens in the store to hashed tokens, if token
// hashing is enabled.
func (m *TokenMigrator) MigrateTokens(ctx context.Context) error {
	return m.store.migrateTokens(ctx)
}

// migrateTokens migrates any unhashed tokens in the store to hashed tokens, if token
// hashing is enabled.
func (s *Store) migrateTokens(ctx context.Context) error {
	if !s.useHashedTokens {
		return nil
	}

	// Figure out which authorization records need to be updated.
	var authsNeedingUpdate []*influxdb.Authorization
	err := s.View(ctx, func(tx kv.Tx) error {
		return s.forEachAuthorization(ctx, tx, nil, func(a *influxdb.Authorization) bool {