From 9ad43cd9f96927e2b84ebbbb6ac7c520f5484ede Mon Sep 17 00:00:00 2001 From: David John Date: Tue, 5 Nov 2024 11:55:14 +0530 Subject: [PATCH] feat: add PostgreSQL as storage backend --- cli/storage.go | 3 + go.mod | 1 + go.sum | 2 + storage/psql/psql.go | 249 +++++++++++++++++++++++++++++++ storage/psql/psql_test.go | 25 ++++ storage/psql/query.sql | 106 +++++++++++++ storage/psql/schema.sql | 52 +++++++ storage/psql/sqlc.yaml | 32 ++++ storage/psql/sqlc/db.go | 31 ++++ storage/psql/sqlc/models.go | 28 ++++ storage/psql/sqlc/query.sql.go | 264 +++++++++++++++++++++++++++++++++ 11 files changed, 793 insertions(+) create mode 100644 storage/psql/psql.go create mode 100644 storage/psql/psql_test.go create mode 100644 storage/psql/query.sql create mode 100644 storage/psql/schema.sql create mode 100644 storage/psql/sqlc.yaml create mode 100644 storage/psql/sqlc/db.go create mode 100644 storage/psql/sqlc/models.go create mode 100644 storage/psql/sqlc/query.sql.go diff --git a/cli/storage.go b/cli/storage.go index 886e859..484bc2e 100644 --- a/cli/storage.go +++ b/cli/storage.go @@ -11,6 +11,7 @@ import ( "github.com/micromdm/nanodep/storage/file" "github.com/micromdm/nanodep/storage/inmem" "github.com/micromdm/nanodep/storage/mysql" + "github.com/micromdm/nanodep/storage/psql" ) // Storage parses a storage name and dsn to determine which and return a storage backend. @@ -35,6 +36,8 @@ func Storage(storageName, dsn, options string) (storage.AllStorage, error) { store = inmem.New() case "mysql": store, err = mysql.New(mysql.WithDSN(dsn)) + case "psql": + store, err = psql.New(psql.WithDSN(dsn)) default: return nil, fmt.Errorf("unknown storage: %q", storageName) } diff --git a/go.mod b/go.mod index 973a812..9696532 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.17 require ( github.com/go-sql-driver/mysql v1.8.1 github.com/gomodule/oauth1 v0.2.0 + github.com/lib/pq v1.10.9 github.com/micromdm/nanolib v0.2.0 github.com/peterbourgon/diskv/v3 v3.0.1 github.com/smallstep/pkcs7 v0.0.0-20231107075624-be1870d87d13 diff --git a/go.sum b/go.sum index fc096e3..0241f63 100644 --- a/go.sum +++ b/go.sum @@ -6,6 +6,8 @@ github.com/gomodule/oauth1 v0.2.0 h1:/nNHAD99yipOEspQFbAnNmwGTZ1UNXiD/+JLxwx79fo github.com/gomodule/oauth1 v0.2.0/go.mod h1:4r/a8/3RkhMBxJQWL5qzbOEcaQmNPIkNoI7P8sXeI08= github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= +github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/micromdm/nanolib v0.2.0 h1:g5GHQuUpS82WIAB15LyenjF/0/WSUNJMe5XZfCJSXq4= github.com/micromdm/nanolib v0.2.0/go.mod h1:FwBKCvvphgYvbdUZ+qw5kay7NHJcg6zPi8W7kXNajmE= github.com/peterbourgon/diskv/v3 v3.0.1 h1:x06SQA46+PKIUftmEujdwSEpIx8kR+M9eLYsUxeYveU= diff --git a/storage/psql/psql.go b/storage/psql/psql.go new file mode 100644 index 0000000..0d33b8e --- /dev/null +++ b/storage/psql/psql.go @@ -0,0 +1,249 @@ +package psql + +import ( + "context" + "database/sql" + _ "embed" + "errors" + "fmt" + "time" + + _ "github.com/lib/pq" + "github.com/micromdm/nanodep/client" + "github.com/micromdm/nanodep/storage" + "github.com/micromdm/nanodep/storage/psql/sqlc" +) + +// PSQL implements storage.AllStorage using PSQL. +type PSQLStorage struct { + db *sql.DB + q *sqlc.Queries +} + +type config struct { + driver string + dsn string + db *sql.DB +} + +// Function callback to configure PSQLStorage +type Option func(*config) + +// WithDSN sets the data source name +func WithDSN(dsn string) Option { + return func(c *config) { + c.dsn = dsn + } +} + +// WithDriver sets the driver +func WithDriver(driver string) Option { + return func(c *config) { + c.driver = driver + } +} + +// WithDB sets the db +func WithDB(db *sql.DB) Option { + return func(c *config) { + c.db = db + + } +} + +// Create a new PSQLStorage instance +func New(opts ...Option) (*PSQLStorage, error) { + cfg := &config{driver: "postgres"} + for _, opt := range opts { + opt(cfg) + } + + var err error + if cfg.db == nil { + cfg.db, err = sql.Open(cfg.driver, cfg.dsn) + if err != nil { + return nil, err + } + } + if err = cfg.db.Ping(); err != nil { + return nil, err + } + return &PSQLStorage{db: cfg.db, q: sqlc.New(cfg.db)}, nil + +} + +const timestampFormat = "2006-01-02T15:04:05-07:00" + +// RetrieveAuthTokens reads the DEP OAuth tokens for name (DEP name). +func (s *PSQLStorage) RetrieveAuthTokens(ctx context.Context, name string) (*client.OAuth1Tokens, error) { + tokenRow, err := s.q.GetAuthTokens(ctx, name) + if err != nil { + if errors.Is(err, sql.ErrNoRows) { + return nil, fmt.Errorf("%v: %w", err, storage.ErrNotFound) + } + return nil, err + } + if !tokenRow.ConsumerKey.Valid { // all auth token fields are set together + return nil, fmt.Errorf("consumer key not valid: %w", storage.ErrNotFound) + } + fmt.Println(tokenRow.AccessTokenExpiry.String) + accessTokenExpiryTime, err := time.Parse(timestampFormat, tokenRow.AccessTokenExpiry.String) + if err != nil { + return nil, err + } + return &client.OAuth1Tokens{ + ConsumerKey: tokenRow.ConsumerKey.String, + ConsumerSecret: tokenRow.ConsumerSecret.String, + AccessToken: tokenRow.AccessToken.String, + AccessSecret: tokenRow.AccessSecret.String, + AccessTokenExpiry: accessTokenExpiryTime, + }, nil +} + +// StoreAuthTokens saves the DEP OAuth tokens for the DEP name. +func (s *PSQLStorage) StoreAuthTokens(ctx context.Context, name string, tokens *client.OAuth1Tokens) error { + return s.q.StoreAuthTokens(ctx, sqlc.StoreAuthTokensParams{ + Name: name, + ConsumerKey: sql.NullString{String: tokens.ConsumerKey, Valid: true}, + ConsumerSecret: sql.NullString{String: tokens.ConsumerSecret, Valid: true}, + AccessToken: sql.NullString{String: tokens.AccessToken, Valid: true}, + AccessSecret: sql.NullString{String: tokens.AccessSecret, Valid: true}, + AccessTokenExpiry: sql.NullString{String: tokens.AccessTokenExpiry.Format(timestampFormat), Valid: true}, + }) +} + +// RetrieveConfig reads the JSON DEP config of a DEP name. +// +// Returns (nil, nil) if the DEP name does not exist, or if the config +// for the DEP name does not exist. +func (s *PSQLStorage) RetrieveConfig(ctx context.Context, name string) (*client.Config, error) { + baseURL, err := s.q.GetConfigBaseURL(ctx, name) + if err != nil { + if errors.Is(err, sql.ErrNoRows) { + // If the DEP name does not exist, then the config does not exist. + return nil, nil + } + return nil, err + } + if !baseURL.Valid { + // If the config_base_url is NULL, then config does not exist. + return nil, nil + } + return &client.Config{ + BaseURL: baseURL.String, + }, nil +} + +// StoreConfig saves the DEP config for name (DEP name). +func (s *PSQLStorage) StoreConfig(ctx context.Context, name string, config *client.Config) error { + return s.q.StoreConfig(ctx, sqlc.StoreConfigParams{ + Name: name, + ConfigBaseUrl: sql.NullString{String: config.BaseURL, Valid: true}, + }) +} + +// RetrieveAssignerProfile reads the assigner profile UUID and its timestamp for name (DEP name). +// +// Returns an empty profile UUID if it does not exist. +func (s *PSQLStorage) RetrieveAssignerProfile(ctx context.Context, name string) (profileUUID string, modTime time.Time, err error) { + assignerRow, err := s.q.GetAssignerProfile(ctx, name) + if err != nil { + if errors.Is(err, sql.ErrNoRows) { + // an 'empty' profile UUID is valid, return nil error + return "", time.Time{}, nil + } + return "", time.Time{}, err + } + if assignerRow.AssignerProfileUuid.Valid { + profileUUID = assignerRow.AssignerProfileUuid.String + } + if assignerRow.AssignerProfileUuidAt.Valid { + modTime, err = time.Parse(timestampFormat, assignerRow.AssignerProfileUuidAt.String) + } + return +} + +// StoreAssignerProfile saves the assigner profile UUID for name (DEP name). +func (s *PSQLStorage) StoreAssignerProfile(ctx context.Context, name string, profileUUID string) error { + return s.q.StoreAssignerProfile(ctx, sqlc.StoreAssignerProfileParams{ + Name: name, + AssignerProfileUuid: sql.NullString{String: profileUUID, Valid: true}, + }) +} + +// RetrieveCursor reads the reads the DEP fetch and sync cursor for name (DEP name). +// +// Returns an empty cursor if the cursor does not exist. +func (s *PSQLStorage) RetrieveCursor(ctx context.Context, name string) (string, error) { + cursor, err := s.q.GetSyncerCursor(ctx, name) + if err != nil { + if errors.Is(err, sql.ErrNoRows) { + return "", nil + } + return "", err + } + if !cursor.Valid { + return "", nil + } + return cursor.String, nil +} + +// StoreCursor saves the DEP fetch and sync cursor for name (DEP name). +func (s *PSQLStorage) StoreCursor(ctx context.Context, name, cursor string) error { + return s.q.StoreCursor(ctx, sqlc.StoreCursorParams{ + Name: name, + SyncerCursor: sql.NullString{String: cursor, Valid: true}, + }) + +} + +// StoreTokenPKI stores the staging PEM bytes in pemCert and pemKey for name (DEP name). +func (s *PSQLStorage) StoreTokenPKI(ctx context.Context, name string, pemCert []byte, pemKey []byte) error { + return s.q.StoreTokenPKI(ctx, sqlc.StoreTokenPKIParams{ + Name: name, + TokenpkiStagingCertPem: pemCert, + TokenpkiStagingKeyPem: pemKey, + }) +} + +// UpstageTokenPKI copies the staging PKI certificate and private key to the +// current PKI certificate and private key. +func (s *PSQLStorage) UpstageTokenPKI(ctx context.Context, name string) error { + err := s.q.UpstageKeypair(ctx, name) + if errors.Is(err, sql.ErrNoRows) { + return fmt.Errorf("%v: %w", err, storage.ErrNotFound) + } + return err +} + +// RetrieveStagingTokenPKI returns the PEM bytes for the staged DEP +// token exchange certificate and private key using name (DEP name). +func (s *PSQLStorage) RetrieveStagingTokenPKI(ctx context.Context, name string) ([]byte, []byte, error) { + keypair, err := s.q.GetStagingKeypair(ctx, name) + if err != nil { + if errors.Is(err, sql.ErrNoRows) { + return nil, nil, fmt.Errorf("%v: %w", err, storage.ErrNotFound) + } + return nil, nil, err + } + if keypair.TokenpkiStagingCertPem == nil { // tokenpki_staging_cert_pem and tokenpki_staging_key_pem are set together + return nil, nil, fmt.Errorf("empty certificate: %w", storage.ErrNotFound) + } + return keypair.TokenpkiStagingCertPem, keypair.TokenpkiStagingKeyPem, nil +} + +// RetrieveCurrentTokenPKI returns the PEM bytes for the previously-upstaged DEP +// token exchange certificate and private key using name (DEP name). +func (s *PSQLStorage) RetrieveCurrentTokenPKI(ctx context.Context, name string) (pemCert []byte, pemKey []byte, err error) { + keypair, err := s.q.GetCurrentKeypair(ctx, name) + if err != nil { + if errors.Is(err, sql.ErrNoRows) { + return nil, nil, fmt.Errorf("%v: %w", err, storage.ErrNotFound) + } + return nil, nil, err + } + if keypair.TokenpkiCertPem == nil { // tokenpki_cert_pem and tokenpki_key_pem are set together + return nil, nil, fmt.Errorf("empty certificate: %w", storage.ErrNotFound) + } + return keypair.TokenpkiCertPem, keypair.TokenpkiKeyPem, nil +} diff --git a/storage/psql/psql_test.go b/storage/psql/psql_test.go new file mode 100644 index 0000000..2cefac8 --- /dev/null +++ b/storage/psql/psql_test.go @@ -0,0 +1,25 @@ +package psql + +import ( + "context" + "os" + "testing" + + _ "github.com/lib/pq" + + "github.com/micromdm/nanodep/storage/test" +) + +func TestPSQLStorage(t *testing.T) { + testDSN := os.Getenv("NANODEP_PSQL_STORAGE_TEST_DSN") + if testDSN == "" { + t.Skip("NANODEP_PSQL_STORAGE_TEST_DSN not set") + } + + s, err := New(WithDSN(testDSN)) + if err != nil { + t.Fatal(err) + } + + test.TestWithStorages(t, context.Background(), s) +} diff --git a/storage/psql/query.sql b/storage/psql/query.sql new file mode 100644 index 0000000..d451e70 --- /dev/null +++ b/storage/psql/query.sql @@ -0,0 +1,106 @@ + +-- name: GetConfigBaseURL :one +SELECT config_base_url FROM dep_names WHERE name = $1; + +-- name: GetSyncerCursor :one +SELECT syncer_cursor FROM dep_names WHERE name = $1; + +-- name: GetCurrentKeypair :one +SELECT + tokenpki_cert_pem, + tokenpki_key_pem +FROM + dep_names +WHERE + name = $1; + +-- name: GetStagingKeypair :one +SELECT + tokenpki_staging_cert_pem, + tokenpki_staging_key_pem +FROM + dep_names +WHERE + name = $1; + +-- name: UpstageKeypair :exec +UPDATE + dep_names +SET + tokenpki_cert_pem = tokenpki_staging_cert_pem, + tokenpki_key_pem = tokenpki_staging_key_pem +WHERE + name = $1; + +-- name: GetAuthTokens :one +SELECT + consumer_key, + consumer_secret, + access_token, + access_secret, + access_token_expiry +FROM + dep_names +WHERE + name = $1; + +-- name: GetAssignerProfile :one +SELECT + assigner_profile_uuid, + assigner_profile_uuid_at +FROM + dep_names +WHERE + name = $1; + + +-- name: StoreAuthTokens :exec +INSERT INTO dep_names ( + name, consumer_key, consumer_secret, + access_token, access_secret, + access_token_expiry +) VALUES ( + $1, $2, $3, $4, $5, $6 + ) ON conflict (name) DO UPDATE SET + consumer_key = excluded.consumer_key, + consumer_secret = excluded.consumer_secret, + access_token = excluded.access_token, + access_secret = excluded.access_secret, + access_token_expiry = excluded.access_token_expiry; + + +-- name: StoreConfig :exec +INSERT INTO dep_names ( + name, config_base_url +) VALUES ($1, $2) +ON conflict (name) DO UPDATE SET +config_base_url = excluded.config_base_url; + + +-- name: StoreAssignerProfile :exec +INSERT INTO dep_names ( + name, assigner_profile_uuid, + assigner_profile_uuid_at +) VALUES ( + $1, $2, CURRENT_TIMESTAMP +) ON CONFLICT (name) DO UPDATE SET +assigner_profile_uuid = excluded.assigner_profile_uuid, +assigner_profile_uuid_at = excluded.assigner_profile_uuid_at; + +-- name: StoreCursor :exec +INSERT INTO dep_names ( + name, syncer_cursor +) VALUES ( + $1, $2 +) ON CONFLICT (name) DO UPDATE SET +syncer_cursor = excluded.syncer_cursor; + +-- name: StoreTokenPKI :exec +INSERT INTO dep_names ( + name, tokenpki_staging_cert_pem, + tokenpki_staging_key_pem +) VALUES ( + $1, $2, $3 +) ON CONFLICT (name) DO UPDATE SET +tokenpki_staging_cert_pem = excluded.tokenpki_staging_cert_pem, +tokenpki_staging_key_pem = excluded.tokenpki_staging_key_pem; diff --git a/storage/psql/schema.sql b/storage/psql/schema.sql new file mode 100644 index 0000000..8d64cd4 --- /dev/null +++ b/storage/psql/schema.sql @@ -0,0 +1,52 @@ + +CREATE TABLE dep_names ( + name VARCHAR(255) NOT NULL, + + -- OAuth1 Tokens + consumer_key TEXT NULL, + consumer_secret TEXT NULL, + access_token TEXT NULL, + access_secret TEXT NULL, + access_token_expiry TIMESTAMPTZ NULL, + + -- Config + config_base_url VARCHAR(255) NULL, + + -- Token PKI + tokenpki_cert_pem TEXT NULL, + tokenpki_key_pem TEXT NULL, + tokenpki_staging_cert_pem TEXT NULL, + tokenpki_staging_key_pem TEXT NULL, + + -- Syncer + -- From Apple docs: "The string can be up to 1000 characters". + syncer_cursor VARCHAR(1024) NULL, + + -- Assigner + assigner_profile_uuid TEXT NULL, + assigner_profile_uuid_at TIMESTAMPTZ NULL, + + created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP, + + PRIMARY KEY (name), + + CHECK (tokenpki_cert_pem IS NULL OR SUBSTRING(tokenpki_cert_pem FROM 1 FOR 27) = '-----BEGIN CERTIFICATE-----'), + CHECK (tokenpki_key_pem IS NULL OR SUBSTRING(tokenpki_key_pem FROM 1 FOR 5) = '-----') +); + + +CREATE FUNCTION update_updated_at() +RETURNS TRIGGER AS $$ +BEGIN + NEW.updated_at = now(); + RETURN NEW; +END; +$$ language 'plpgsql'; + +CREATE TRIGGER update_updated_at_on_change + BEFORE UPDATE + ON + dep_names + FOR EACH ROW +EXECUTE PROCEDURE update_updated_at(); diff --git a/storage/psql/sqlc.yaml b/storage/psql/sqlc.yaml new file mode 100644 index 0000000..4dbc2de --- /dev/null +++ b/storage/psql/sqlc.yaml @@ -0,0 +1,32 @@ +version: 2 +sql: + - engine: "postgresql" + queries: "query.sql" + schema: "schema.sql" + gen: + go: + package: "sqlc" + out: "sqlc" + overrides: + - column: "dep_names.tokenpki_cert_pem" + go_type: + type: "byte" + slice: true + - column: "dep_names.tokenpki_key_pem" + go_type: + type: "byte" + slice: true + - column: "dep_names.tokenpki_staging_cert_pem" + go_type: + type: "byte" + slice: true + - column: "dep_names.tokenpki_staging_key_pem" + go_type: + type: "byte" + slice: true + - column: "dep_names.access_token_expiry" + go_type: + type: "sql.NullString" + - column: "dep_names.assigner_profile_uuid_at" + go_type: + type: "sql.NullString" diff --git a/storage/psql/sqlc/db.go b/storage/psql/sqlc/db.go new file mode 100644 index 0000000..2248616 --- /dev/null +++ b/storage/psql/sqlc/db.go @@ -0,0 +1,31 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.27.0 + +package sqlc + +import ( + "context" + "database/sql" +) + +type DBTX interface { + ExecContext(context.Context, string, ...interface{}) (sql.Result, error) + PrepareContext(context.Context, string) (*sql.Stmt, error) + QueryContext(context.Context, string, ...interface{}) (*sql.Rows, error) + QueryRowContext(context.Context, string, ...interface{}) *sql.Row +} + +func New(db DBTX) *Queries { + return &Queries{db: db} +} + +type Queries struct { + db DBTX +} + +func (q *Queries) WithTx(tx *sql.Tx) *Queries { + return &Queries{ + db: tx, + } +} diff --git a/storage/psql/sqlc/models.go b/storage/psql/sqlc/models.go new file mode 100644 index 0000000..a5669e4 --- /dev/null +++ b/storage/psql/sqlc/models.go @@ -0,0 +1,28 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.27.0 + +package sqlc + +import ( + "database/sql" +) + +type DepName struct { + Name string + ConsumerKey sql.NullString + ConsumerSecret sql.NullString + AccessToken sql.NullString + AccessSecret sql.NullString + AccessTokenExpiry sql.NullString + ConfigBaseUrl sql.NullString + TokenpkiCertPem []byte + TokenpkiKeyPem []byte + TokenpkiStagingCertPem []byte + TokenpkiStagingKeyPem []byte + SyncerCursor sql.NullString + AssignerProfileUuid sql.NullString + AssignerProfileUuidAt sql.NullString + CreatedAt sql.NullTime + UpdatedAt sql.NullTime +} diff --git a/storage/psql/sqlc/query.sql.go b/storage/psql/sqlc/query.sql.go new file mode 100644 index 0000000..20d369a --- /dev/null +++ b/storage/psql/sqlc/query.sql.go @@ -0,0 +1,264 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.27.0 +// source: query.sql + +package sqlc + +import ( + "context" + "database/sql" +) + +const getAssignerProfile = `-- name: GetAssignerProfile :one +SELECT + assigner_profile_uuid, + assigner_profile_uuid_at +FROM + dep_names +WHERE + name = $1 +` + +type GetAssignerProfileRow struct { + AssignerProfileUuid sql.NullString + AssignerProfileUuidAt sql.NullString +} + +func (q *Queries) GetAssignerProfile(ctx context.Context, name string) (GetAssignerProfileRow, error) { + row := q.db.QueryRowContext(ctx, getAssignerProfile, name) + var i GetAssignerProfileRow + err := row.Scan(&i.AssignerProfileUuid, &i.AssignerProfileUuidAt) + return i, err +} + +const getAuthTokens = `-- name: GetAuthTokens :one +SELECT + consumer_key, + consumer_secret, + access_token, + access_secret, + access_token_expiry +FROM + dep_names +WHERE + name = $1 +` + +type GetAuthTokensRow struct { + ConsumerKey sql.NullString + ConsumerSecret sql.NullString + AccessToken sql.NullString + AccessSecret sql.NullString + AccessTokenExpiry sql.NullString +} + +func (q *Queries) GetAuthTokens(ctx context.Context, name string) (GetAuthTokensRow, error) { + row := q.db.QueryRowContext(ctx, getAuthTokens, name) + var i GetAuthTokensRow + err := row.Scan( + &i.ConsumerKey, + &i.ConsumerSecret, + &i.AccessToken, + &i.AccessSecret, + &i.AccessTokenExpiry, + ) + return i, err +} + +const getConfigBaseURL = `-- name: GetConfigBaseURL :one +SELECT config_base_url FROM dep_names WHERE name = $1 +` + +func (q *Queries) GetConfigBaseURL(ctx context.Context, name string) (sql.NullString, error) { + row := q.db.QueryRowContext(ctx, getConfigBaseURL, name) + var config_base_url sql.NullString + err := row.Scan(&config_base_url) + return config_base_url, err +} + +const getCurrentKeypair = `-- name: GetCurrentKeypair :one +SELECT + tokenpki_cert_pem, + tokenpki_key_pem +FROM + dep_names +WHERE + name = $1 +` + +type GetCurrentKeypairRow struct { + TokenpkiCertPem []byte + TokenpkiKeyPem []byte +} + +func (q *Queries) GetCurrentKeypair(ctx context.Context, name string) (GetCurrentKeypairRow, error) { + row := q.db.QueryRowContext(ctx, getCurrentKeypair, name) + var i GetCurrentKeypairRow + err := row.Scan(&i.TokenpkiCertPem, &i.TokenpkiKeyPem) + return i, err +} + +const getStagingKeypair = `-- name: GetStagingKeypair :one +SELECT + tokenpki_staging_cert_pem, + tokenpki_staging_key_pem +FROM + dep_names +WHERE + name = $1 +` + +type GetStagingKeypairRow struct { + TokenpkiStagingCertPem []byte + TokenpkiStagingKeyPem []byte +} + +func (q *Queries) GetStagingKeypair(ctx context.Context, name string) (GetStagingKeypairRow, error) { + row := q.db.QueryRowContext(ctx, getStagingKeypair, name) + var i GetStagingKeypairRow + err := row.Scan(&i.TokenpkiStagingCertPem, &i.TokenpkiStagingKeyPem) + return i, err +} + +const getSyncerCursor = `-- name: GetSyncerCursor :one +SELECT syncer_cursor FROM dep_names WHERE name = $1 +` + +func (q *Queries) GetSyncerCursor(ctx context.Context, name string) (sql.NullString, error) { + row := q.db.QueryRowContext(ctx, getSyncerCursor, name) + var syncer_cursor sql.NullString + err := row.Scan(&syncer_cursor) + return syncer_cursor, err +} + +const storeAssignerProfile = `-- name: StoreAssignerProfile :exec +INSERT INTO dep_names ( + name, assigner_profile_uuid, + assigner_profile_uuid_at +) VALUES ( + $1, $2, CURRENT_TIMESTAMP +) ON CONFLICT (name) DO UPDATE SET +assigner_profile_uuid = excluded.assigner_profile_uuid, +assigner_profile_uuid_at = excluded.assigner_profile_uuid_at +` + +type StoreAssignerProfileParams struct { + Name string + AssignerProfileUuid sql.NullString +} + +func (q *Queries) StoreAssignerProfile(ctx context.Context, arg StoreAssignerProfileParams) error { + _, err := q.db.ExecContext(ctx, storeAssignerProfile, arg.Name, arg.AssignerProfileUuid) + return err +} + +const storeAuthTokens = `-- name: StoreAuthTokens :exec +INSERT INTO dep_names ( + name, consumer_key, consumer_secret, + access_token, access_secret, + access_token_expiry +) VALUES ( + $1, $2, $3, $4, $5, $6 + ) ON conflict (name) DO UPDATE SET + consumer_key = excluded.consumer_key, + consumer_secret = excluded.consumer_secret, + access_token = excluded.access_token, + access_secret = excluded.access_secret, + access_token_expiry = excluded.access_token_expiry +` + +type StoreAuthTokensParams struct { + Name string + ConsumerKey sql.NullString + ConsumerSecret sql.NullString + AccessToken sql.NullString + AccessSecret sql.NullString + AccessTokenExpiry sql.NullString +} + +func (q *Queries) StoreAuthTokens(ctx context.Context, arg StoreAuthTokensParams) error { + _, err := q.db.ExecContext(ctx, storeAuthTokens, + arg.Name, + arg.ConsumerKey, + arg.ConsumerSecret, + arg.AccessToken, + arg.AccessSecret, + arg.AccessTokenExpiry, + ) + return err +} + +const storeConfig = `-- name: StoreConfig :exec +INSERT INTO dep_names ( + name, config_base_url +) VALUES ($1, $2) +ON conflict (name) DO UPDATE SET +config_base_url = excluded.config_base_url +` + +type StoreConfigParams struct { + Name string + ConfigBaseUrl sql.NullString +} + +func (q *Queries) StoreConfig(ctx context.Context, arg StoreConfigParams) error { + _, err := q.db.ExecContext(ctx, storeConfig, arg.Name, arg.ConfigBaseUrl) + return err +} + +const storeCursor = `-- name: StoreCursor :exec +INSERT INTO dep_names ( + name, syncer_cursor +) VALUES ( + $1, $2 +) ON CONFLICT (name) DO UPDATE SET +syncer_cursor = excluded.syncer_cursor +` + +type StoreCursorParams struct { + Name string + SyncerCursor sql.NullString +} + +func (q *Queries) StoreCursor(ctx context.Context, arg StoreCursorParams) error { + _, err := q.db.ExecContext(ctx, storeCursor, arg.Name, arg.SyncerCursor) + return err +} + +const storeTokenPKI = `-- name: StoreTokenPKI :exec +INSERT INTO dep_names ( + name, tokenpki_staging_cert_pem, + tokenpki_staging_key_pem +) VALUES ( + $1, $2, $3 +) ON CONFLICT (name) DO UPDATE SET +tokenpki_staging_cert_pem = excluded.tokenpki_staging_cert_pem, +tokenpki_staging_key_pem = excluded.tokenpki_staging_key_pem +` + +type StoreTokenPKIParams struct { + Name string + TokenpkiStagingCertPem []byte + TokenpkiStagingKeyPem []byte +} + +func (q *Queries) StoreTokenPKI(ctx context.Context, arg StoreTokenPKIParams) error { + _, err := q.db.ExecContext(ctx, storeTokenPKI, arg.Name, arg.TokenpkiStagingCertPem, arg.TokenpkiStagingKeyPem) + return err +} + +const upstageKeypair = `-- name: UpstageKeypair :exec +UPDATE + dep_names +SET + tokenpki_cert_pem = tokenpki_staging_cert_pem, + tokenpki_key_pem = tokenpki_staging_key_pem +WHERE + name = $1 +` + +func (q *Queries) UpstageKeypair(ctx context.Context, name string) error { + _, err := q.db.ExecContext(ctx, upstageKeypair, name) + return err +}