Documentation
¶
Overview ¶
Package sqlstore persists administrative principals, token digests and policies in SQL.
Design ¶
SQLite, PostgreSQL and MySQL share the adminauth.Store contract and a separate adminauth_schema_migrations set. Credential digests support lookup, rotation and revocation without storing raw API tokens. Policies survive process restarts and use versions for cache invalidation. Records are not sealed with a keyring, so database authorization and backups remain deployment responsibilities.
References ¶
- Decision record: https://github.com/deploymenttheory/go-apple-dm/blob/main/docs/research/decisions/0034-admin-api-and-authorization.md
- Threat model: https://github.com/deploymenttheory/go-apple-dm/blob/main/docs/security/threat-model.md (admin API)
- Contract suite: adminauth/adminauthtest
- Migration mechanics: server/storage/sqlcommon
Index ¶
- Constants
- Variables
- func Migrate(ctx context.Context, db *sql.DB, d sqlcommon.Dialect) ([]int, error)
- func MigrationSet(d sqlcommon.Dialect) (sqlcommon.MigrationSet, error)
- func Rollback(ctx context.Context, db *sql.DB, d sqlcommon.Dialect, target int) ([]int, error)
- func Version(ctx context.Context, db *sql.DB, d sqlcommon.Dialect) (int, error)
- type Options
- type Store
- func (s *Store) ApplyPrincipal(ctx context.Context, name string, change adminauth.PrincipalChange, ...) (adminauth.Principal, error)
- func (s *Store) CountRoot(ctx context.Context) (int, error)
- func (s *Store) CreatePrincipal(ctx context.Context, p adminauth.Principal, digest string, now time.Time) (adminauth.Principal, error)
- func (s *Store) DB() *sql.DB
- func (s *Store) DeletePolicy(ctx context.Context, name string) error
- func (s *Store) DeletePrincipal(ctx context.Context, name string) error
- func (s *Store) GetPolicy(ctx context.Context, name string) (adminauth.Policy, error)
- func (s *Store) Policies(ctx context.Context) ([]adminauth.Policy, error)
- func (s *Store) PolicyVersion(ctx context.Context) (int64, error)
- func (s *Store) Principal(ctx context.Context, name string) (adminauth.Principal, error)
- func (s *Store) PrincipalByDigest(ctx context.Context, digest string) (adminauth.Principal, error)
- func (s *Store) Principals(ctx context.Context, p adminauth.Page) (adminauth.Result[adminauth.Principal], error)
- func (s *Store) PutPolicy(ctx context.Context, p adminauth.Policy, now time.Time) (adminauth.Policy, error)
- func (s *Store) RevokeToken(ctx context.Context, name string, now time.Time) error
- func (s *Store) SetToken(ctx context.Context, name, digest, tokenID string, expires, now time.Time) (adminauth.Principal, error)
- func (s *Store) UpdatePrincipal(ctx context.Context, name string, roles []string, root bool, now time.Time) (adminauth.Principal, error)
Constants ¶
const MigrationsTable = "adminauth_schema_migrations"
MigrationsTable records the applied versions of the admin schema, separate from the storage, DDM, DEP, and ACME tables so the version sequences never mix.
Variables ¶
var ErrUnsupportedDialect = errors.New("sqlstore: unsupported dialect")
ErrUnsupportedDialect is returned for a dialect without embedded migrations.
Functions ¶
func MigrationSet ¶
func MigrationSet(d sqlcommon.Dialect) (sqlcommon.MigrationSet, error)
MigrationSet returns the admin migrations for the dialect.
Types ¶
type Options ¶
type Options struct {
// SkipMigrate leaves the schema alone; the caller has run Migrate.
SkipMigrate bool
}
Options tune Open.
type Store ¶
type Store struct {
// contains filtered or unexported fields
}
Store implements adminauth.Store over a *sql.DB it does not own: closing the pool is the caller's job.
func Open ¶
Open wraps an opened pool for the dialect and, unless o.SkipMigrate, applies pending migrations.
func (*Store) ApplyPrincipal ¶
func (s *Store) ApplyPrincipal( ctx context.Context, name string, change adminauth.PrincipalChange, now time.Time, ) (adminauth.Principal, error)
ApplyPrincipal uses the existing singleton row to serialize lifecycle changes across connections, before any snapshot reads. It does not bump policy version.
func (*Store) CreatePrincipal ¶
func (s *Store) CreatePrincipal(ctx context.Context, p adminauth.Principal, digest string, now time.Time) (adminauth.Principal, error)
CreatePrincipal implements adminauth.Store.
func (*Store) DeletePolicy ¶
DeletePolicy implements adminauth.Store.
func (*Store) DeletePrincipal ¶
DeletePrincipal implements adminauth.Store.
func (*Store) PolicyVersion ¶
PolicyVersion implements adminauth.Store.
func (*Store) PrincipalByDigest ¶
PrincipalByDigest implements adminauth.Store. This is the authentication path: one indexed lookup on the unique digest index.
func (*Store) Principals ¶
func (s *Store) Principals(ctx context.Context, p adminauth.Page) (adminauth.Result[adminauth.Principal], error)
Principals implements adminauth.Store with a keyset cursor on name.
func (*Store) PutPolicy ¶
func (s *Store) PutPolicy(ctx context.Context, p adminauth.Policy, now time.Time) (adminauth.Policy, error)
PutPolicy implements adminauth.Store. The write and the version bump share one transaction, so a compiled set never sees a version that does not match the policies it would read.
func (*Store) RevokeToken ¶
RevokeToken implements adminauth.Store. The digest becomes NULL rather than an empty string, so the unique index still admits many revoked rows.