Documentation
¶
Overview ¶
Package sqlstore persists device enrollment service accounts, devices, profiles and assignment state in SQL.
Design ¶
SQLite, PostgreSQL and MySQL use a separate dep_schema_migrations set with shared dialect helpers. OAuth secrets, sessions and private keys are sealed through storage/crypt when a keyring is supplied. Device/profile bytes are stored beside indexed lookup fields, timestamps use UTC and lists use keyset pagination. Device pages, fetch-generation membership and cursors commit together; account locks and persisted assignment leases fence worker overlap. Stable name locks also serialize first token imports and token keypair staging, and survive account deletion. Assignment retry deadlines survive process restart. appleplatformservices/dep/deptest supplies the shared contract tests.
References ¶
- Decision record 0026: https://github.com/deploymenttheory/go-apple-dm/blob/main/docs/research/decisions/0026-dep-client-sync-and-assignment.md
- Decision record 0012: https://github.com/deploymenttheory/go-apple-dm/blob/main/docs/research/decisions/0012-sql-storage-backends.md (dialects and migrations)
- Decision record 0013: https://github.com/deploymenttheory/go-apple-dm/blob/main/docs/research/decisions/0013-secrets-at-rest.md (sealed columns)
- Apple: https://developer.apple.com/documentation/devicemanagement/device
- Apple: https://developer.apple.com/documentation/devicemanagement/profile
- Apple: https://developer.apple.com/documentation/devicemanagement/sync-devices (cursor lifetime)
- Migrations: https://github.com/deploymenttheory/go-apple-dm/tree/main/server/depstore/sqlstore/migrations
- Worker-state migration: 0002_sync_state.sql in each dialect directory
- Account-name lock migration: 0003_account_locks.sql in each dialect directory
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) AssignmentState(ctx context.Context, account string) (dep.AssignmentState, error)
- func (s *Store) Cursor(ctx context.Context, name string) (dep.Cursor, error)
- func (s *Store) DB() *sql.DB
- func (s *Store) DeleteAccount(ctx context.Context, name string) error
- func (s *Store) DeleteProfile(ctx context.Context, account, uuid string) error
- func (s *Store) GetAccount(ctx context.Context, name string) (*dep.Account, error)
- func (s *Store) GetAssignment(ctx context.Context, account, serial string) (*dep.Assignment, error)
- func (s *Store) GetDevice(ctx context.Context, account, serial string) (*dep.StoredDevice, error)
- func (s *Store) GetProfile(ctx context.Context, account, uuid string) (*dep.Profile, error)
- func (s *Store) Keypair(ctx context.Context, name string, stage dep.Stage) (*dep.Keypair, error)
- func (s *Store) ListAccounts(ctx context.Context, p paging.Page) (paging.Result[dep.Account], error)
- func (s *Store) ListAssignments(ctx context.Context, account string, q dep.AssignmentQuery, p paging.Page) (paging.Result[dep.Assignment], error)
- func (s *Store) ListDevices(ctx context.Context, account string, q dep.DeviceQuery, p paging.Page) (paging.Result[dep.StoredDevice], error)
- func (s *Store) ListProfiles(ctx context.Context, account string, p paging.Page) (paging.Result[dep.Profile], error)
- func (s *Store) MarkFetched(ctx context.Context, account, generation string, serials []string) error
- func (s *Store) PutAccount(ctx context.Context, a *dep.Account) error
- func (s *Store) PutAssignment(ctx context.Context, a *dep.Assignment) error
- func (s *Store) PutAssignmentState(ctx context.Context, account string, state dep.AssignmentState) error
- func (s *Store) PutDevices(ctx context.Context, account string, devs []dep.Device, at time.Time) error
- func (s *Store) PutKeypair(ctx context.Context, name string, stage dep.Stage, kp *dep.Keypair) error
- func (s *Store) PutProfile(ctx context.Context, account string, p *dep.Profile) error
- func (s *Store) RawSecrets(ctx context.Context, name string) (map[string][]byte, error)
- func (s *Store) Session(ctx context.Context, name string) (string, error)
- func (s *Store) SetAccountState(ctx context.Context, name string, st dep.AccountState) error
- func (s *Store) SetCursor(ctx context.Context, name string, c dep.Cursor) error
- func (s *Store) SetSession(ctx context.Context, name, token string) error
- func (s *Store) Update(ctx context.Context, fn func(dep.Tx) error) error
- func (s *Store) UpstageKeypair(ctx context.Context, name string) error
Constants ¶
const ( PurposeConsumerSecret = "dep_accounts.consumer_secret" // #nosec G101 -- a column name, not a credential PurposeAccessToken = "dep_accounts.access_token" // #nosec G101 -- a column name, not a credential PurposeAccessSecret = "dep_accounts.access_secret" // #nosec G101 -- a column name, not a credential PurposeSession = "dep_sessions.token" // #nosec G101 -- a column name, not a credential PurposeKeyPEM = "dep_keypairs.key_pem" // #nosec G101 -- a column name, not a credential )
Purposes name the sealed columns; each is the AAD prefix binding a ciphertext to its column (decision record 0013).
const MigrationsTable = "dep_schema_migrations"
MigrationsTable records the applied versions of the DEP schema, separate from the storage and DDM tables so the version sequences never mix.
Variables ¶
var ErrUnsupportedDialect = errors.New("sqlstore: unsupported dialect")
ErrUnsupportedDialect is returned for a dialect without embedded migrations (anything but sqlite, postgres, and mysql).
Functions ¶
func MigrationSet ¶
func MigrationSet(d sqlcommon.Dialect) (sqlcommon.MigrationSet, error)
MigrationSet returns the DEP migrations for the dialect.
Types ¶
type Options ¶
type Options struct {
// SkipMigrate leaves the schema alone; the caller has run Migrate.
SkipMigrate bool
// Keyring seals the OAuth secrets, session tokens, and private keys
// (decision record 0013); nil keeps them in plaintext.
Keyring *crypt.Keyring
}
Options tune Open.
type Store ¶
type Store struct {
// contains filtered or unexported fields
}
Store implements dep.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) AssignmentState ¶ added in v0.10.1
AssignmentState returns the account's persisted assignment lease and retry schedule, or zero state when none is stored.
func (*Store) DeleteAccount ¶
DeleteAccount implements dep.AccountStore.
func (*Store) DeleteProfile ¶
DeleteProfile implements dep.ProfileStore.
func (*Store) GetAccount ¶
GetAccount implements dep.AccountStore.
func (*Store) GetAssignment ¶
GetAssignment implements dep.AssignmentStore.
func (*Store) GetProfile ¶
GetProfile implements dep.ProfileStore.
func (*Store) ListAccounts ¶
func (s *Store) ListAccounts(ctx context.Context, p paging.Page) (paging.Result[dep.Account], error)
ListAccounts implements dep.AccountStore.
func (*Store) ListAssignments ¶
func (s *Store) ListAssignments(ctx context.Context, account string, q dep.AssignmentQuery, p paging.Page) (paging.Result[dep.Assignment], error)
ListAssignments implements dep.AssignmentStore.
func (*Store) ListDevices ¶
func (s *Store) ListDevices(ctx context.Context, account string, q dep.DeviceQuery, p paging.Page) (paging.Result[dep.StoredDevice], error)
ListDevices implements dep.DeviceStore.
func (*Store) ListProfiles ¶
func (s *Store) ListProfiles(ctx context.Context, account string, p paging.Page) (paging.Result[dep.Profile], error)
ListProfiles implements dep.ProfileStore.
func (*Store) MarkFetched ¶ added in v0.10.1
func (s *Store) MarkFetched(ctx context.Context, account, generation string, serials []string) error
MarkFetched records which serials were observed in an account's full-fetch generation. Syncers call the transaction form together with device and cursor writes so partial fetches cannot trigger reconciliation.
func (*Store) PutAccount ¶
PutAccount implements dep.AccountStore.
func (*Store) PutAssignment ¶
PutAssignment implements dep.AssignmentStore.
func (*Store) PutAssignmentState ¶ added in v0.10.1
func (s *Store) PutAssignmentState(ctx context.Context, account string, state dep.AssignmentState) error
PutAssignmentState replaces an account's assignment lease and retry schedule. Worker transitions call the transaction form after LockAccount to fence overlapping runs.
func (*Store) PutDevices ¶
func (s *Store) PutDevices(ctx context.Context, account string, devs []dep.Device, at time.Time) error
PutDevices implements dep.DeviceStore.
func (*Store) PutKeypair ¶
func (s *Store) PutKeypair(ctx context.Context, name string, stage dep.Stage, kp *dep.Keypair) error
PutKeypair implements dep.AccountStore.
func (*Store) PutProfile ¶
PutProfile implements dep.ProfileStore.
func (*Store) RawSecrets ¶
RawSecrets returns the stored bytes of every secret column of the account as they rest in the database, keyed consumer_secret, access_token, access_secret, session, and key_pem:<stage>. It lets the contract suite prove sealing.
func (*Store) SetAccountState ¶
SetAccountState implements dep.AccountStore.
func (*Store) SetSession ¶
SetSession implements dep.SessionStore.