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) BootstrapPrincipal(ctx context.Context, p adminauth.Principal, digest string, now time.Time) (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) DeleteRole(ctx context.Context, name string) error
- func (s *Store) GetPolicy(ctx context.Context, name string) (adminauth.Policy, error)
- func (s *Store) Initialized(ctx context.Context) (bool, 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) PutRole(ctx context.Context, role adminauth.Role, now time.Time) (adminauth.Role, error)
- func (s *Store) RevokeToken(ctx context.Context, name string, now time.Time) error
- func (s *Store) Role(ctx context.Context, name string) (adminauth.Role, error)
- func (s *Store) Roles(ctx context.Context, p adminauth.Page) (adminauth.Result[adminauth.Role], 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) BootstrapPrincipal ¶ added in v0.10.0
func (s *Store) BootstrapPrincipal(ctx context.Context, p adminauth.Principal, digest string, now time.Time) (adminauth.Principal, error)
BootstrapPrincipal atomically installs the first active root credential and closes bootstrap. It requires an empty, uninitialized principal store and receives only the credential digest.
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) DeleteRole ¶ added in v0.10.0
DeleteRole removes an unreferenced role. Principal membership or a policy reference returns ErrConflict; an absent role returns ErrNotFound.
func (*Store) Initialized ¶ added in v0.10.0
Initialized reports whether principal initialization has permanently consumed the bootstrap opportunity.
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) PutRole ¶ added in v0.10.0
func (s *Store) PutRole(ctx context.Context, role adminauth.Role, now time.Time) (adminauth.Role, error)
PutRole creates or updates a named role, preserving its creation time and advancing the policy version. Role membership alone grants no operational permission.
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.
func (*Store) Role ¶ added in v0.10.0
Role returns the named managed role, or ErrNotFound when no such role exists.
func (*Store) Roles ¶ added in v0.10.0
func (s *Store) Roles(ctx context.Context, p adminauth.Page) (adminauth.Result[adminauth.Role], error)
Roles returns a page of managed roles in name order using an exclusive cursor.