Documentation
¶
Overview ¶
Package sqlstore is the SQL dep.Store: one implementation over database/sql for the SQLite, PostgreSQL, and MySQL dialects.
Why ¶
The DEP client needs durable accounts with their OAuth tokens, the shared session, the sync cursor with the time it was received, the token PKI keypairs, every device Apple reported, the profiles defined, and per-serial assignment outcomes, with the same semantics on every backend the MDM storage already supports. Phase 6 of the plan of record delivers it with the client (decision record 0026). This package reuses storage/sqlcommon's dialects and migration runner but owns its own migration set, recorded in dep_schema_migrations, so it can share a database with the MDM and DDM schemas or live apart. The OAuth secrets, session tokens, and private keys are sealed through storage/crypt under column-bound purposes when a keyring is given (record 0013); device and profile records are stored as the exact bytes dep.Marshal produces (no engine JSON types) beside indexed copies of the keys the assigner filters on; every timestamp is written and read in UTC; every list is keyset paginated; and a page of devices commits with its cursor in one transaction. The contract suite in dep/deptest runs against all three dialects.
References ¶
- Decision record 0026: docs/research/decisions/0026-dep-client-sync-and-assignment.md
- Decision record 0012: docs/research/decisions/0012-sql-storage-backends.md (dialects and migrations)
- Decision record 0013: docs/research/decisions/0013-secrets-at-rest.md (sealed columns)
- Plan of record: docs/research/implementation_plan.md (section 5, DEP / ABM; phase 6)
- 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 storage.Page) (storage.Result[dep.Account], error)
- func (s *Store) ListAssignments(ctx context.Context, account string, q dep.AssignmentQuery, p storage.Page) (storage.Result[dep.Assignment], error)
- func (s *Store) ListDevices(ctx context.Context, account string, q dep.DeviceQuery, p storage.Page) (storage.Result[dep.StoredDevice], error)
- func (s *Store) ListProfiles(ctx context.Context, account string, p storage.Page) (storage.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 storage.Page) (storage.Result[dep.Account], error)
ListAccounts implements dep.AccountStore.
func (*Store) ListAssignments ¶
func (s *Store) ListAssignments(ctx context.Context, account string, q dep.AssignmentQuery, p storage.Page) (storage.Result[dep.Assignment], error)
ListAssignments implements dep.AssignmentStore.
func (*Store) ListDevices ¶
func (s *Store) ListDevices(ctx context.Context, account string, q dep.DeviceQuery, p storage.Page) (storage.Result[dep.StoredDevice], error)
ListDevices implements dep.DeviceStore.
func (*Store) ListProfiles ¶
func (s *Store) ListProfiles(ctx context.Context, account string, p storage.Page) (storage.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.