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 commit with their cursors in one transaction. 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: dep/sqlstore/migrations/{sqlite,postgres,mysql}/0001_init.sql
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) 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) PutAccount(ctx context.Context, a *dep.Account) error
- func (s *Store) PutAssignment(ctx context.Context, a *dep.Assignment) 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) 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) PutAccount ¶
PutAccount implements dep.AccountStore.
func (*Store) PutAssignment ¶
PutAssignment implements dep.AssignmentStore.
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.