feat: add keys to organization provision daemons (#14627)

This commit is contained in:
Garrett Delfosse
2024-09-16 16:02:08 -04:00
committed by GitHub
parent 4afce19fb7
commit 335eb05223
32 changed files with 728 additions and 72 deletions

View File

@ -533,6 +533,7 @@ func ProvisionerDaemon(dbDaemon database.ProvisionerDaemon) codersdk.Provisioner
Tags: dbDaemon.Tags,
Version: dbDaemon.Version,
APIVersion: dbDaemon.APIVersion,
KeyID: dbDaemon.KeyID,
}
for _, provisionerType := range dbDaemon.Provisioners {
result.Provisioners = append(result.Provisioners, codersdk.ProvisionerType(provisionerType))
@ -540,6 +541,30 @@ func ProvisionerDaemon(dbDaemon database.ProvisionerDaemon) codersdk.Provisioner
return result
}
func RecentProvisionerDaemons(now time.Time, staleInterval time.Duration, daemons []database.ProvisionerDaemon) []codersdk.ProvisionerDaemon {
results := []codersdk.ProvisionerDaemon{}
for _, daemon := range daemons {
// Daemon never connected, skip.
if !daemon.LastSeenAt.Valid {
continue
}
// Daemon has gone away, skip.
if now.Sub(daemon.LastSeenAt.Time) > staleInterval {
continue
}
results = append(results, ProvisionerDaemon(daemon))
}
// Ensure stable order for display and for tests
sort.Slice(results, func(i, j int) bool {
return results[i].Name < results[j].Name
})
return results
}
func SlimRole(role rbac.Role) codersdk.SlimRole {
orgID := ""
if role.Identifier.OrganizationID != uuid.Nil {

View File

@ -3066,6 +3066,10 @@ func (q *querier) ListProvisionerKeysByOrganization(ctx context.Context, organiz
return fetchWithPostFilter(q.auth, policy.ActionRead, q.db.ListProvisionerKeysByOrganization)(ctx, organizationID)
}
func (q *querier) ListProvisionerKeysByOrganizationExcludeReserved(ctx context.Context, organizationID uuid.UUID) ([]database.ProvisionerKey, error) {
return fetchWithPostFilter(q.auth, policy.ActionRead, q.db.ListProvisionerKeysByOrganizationExcludeReserved)(ctx, organizationID)
}
func (q *querier) ListWorkspaceAgentPortShares(ctx context.Context, workspaceID uuid.UUID) ([]database.WorkspaceAgentPortShare, error) {
workspace, err := q.db.GetWorkspaceByID(ctx, workspaceID)
if err != nil {

View File

@ -2020,6 +2020,19 @@ func (s *MethodTestSuite) TestProvisionerKeys() {
}
check.Args(org.ID).Asserts(pk, policy.ActionRead).Returns(pks)
}))
s.Run("ListProvisionerKeysByOrganizationExcludeReserved", s.Subtest(func(db database.Store, check *expects) {
org := dbgen.Organization(s.T(), db, database.Organization{})
pk := dbgen.ProvisionerKey(s.T(), db, database.ProvisionerKey{OrganizationID: org.ID})
pks := []database.ProvisionerKey{
{
ID: pk.ID,
CreatedAt: pk.CreatedAt,
OrganizationID: pk.OrganizationID,
Name: pk.Name,
},
}
check.Args(org.ID).Asserts(pk, policy.ActionRead).Returns(pks)
}))
s.Run("DeleteProvisionerKey", s.Subtest(func(db database.Store, check *expects) {
org := dbgen.Organization(s.T(), db, database.Organization{})
pk := dbgen.ProvisionerKey(s.T(), db, database.ProvisionerKey{OrganizationID: org.ID})

View File

@ -68,6 +68,7 @@ func New() database.Store {
notificationPreferences: make([]database.NotificationPreference, 0),
parameterSchemas: make([]database.ParameterSchema, 0),
provisionerDaemons: make([]database.ProvisionerDaemon, 0),
provisionerKeys: make([]database.ProvisionerKey, 0),
workspaceAgents: make([]database.WorkspaceAgent, 0),
provisionerJobLogs: make([]database.ProvisionerJobLog, 0),
workspaceResources: make([]database.WorkspaceResource, 0),
@ -108,6 +109,41 @@ func New() database.Store {
q.defaultProxyDisplayName = "Default"
q.defaultProxyIconURL = "/emojis/1f3e1.png"
_, err = q.InsertProvisionerKey(context.Background(), database.InsertProvisionerKeyParams{
ID: uuid.MustParse(codersdk.ProvisionerKeyIDBuiltIn),
OrganizationID: defaultOrg.ID,
CreatedAt: dbtime.Now(),
HashedSecret: []byte{},
Name: codersdk.ProvisionerKeyNameBuiltIn,
Tags: map[string]string{},
})
if err != nil {
panic(xerrors.Errorf("failed to create built-in provisioner key: %w", err))
}
_, err = q.InsertProvisionerKey(context.Background(), database.InsertProvisionerKeyParams{
ID: uuid.MustParse(codersdk.ProvisionerKeyIDUserAuth),
OrganizationID: defaultOrg.ID,
CreatedAt: dbtime.Now(),
HashedSecret: []byte{},
Name: codersdk.ProvisionerKeyNameUserAuth,
Tags: map[string]string{},
})
if err != nil {
panic(xerrors.Errorf("failed to create user-auth provisioner key: %w", err))
}
_, err = q.InsertProvisionerKey(context.Background(), database.InsertProvisionerKeyParams{
ID: uuid.MustParse(codersdk.ProvisionerKeyIDPSK),
OrganizationID: defaultOrg.ID,
CreatedAt: dbtime.Now(),
HashedSecret: []byte{},
Name: codersdk.ProvisionerKeyNamePSK,
Tags: map[string]string{},
})
if err != nil {
panic(xerrors.Errorf("failed to create psk provisioner key: %w", err))
}
return q
}
@ -7582,6 +7618,25 @@ func (q *FakeQuerier) ListProvisionerKeysByOrganization(_ context.Context, organ
return keys, nil
}
func (q *FakeQuerier) ListProvisionerKeysByOrganizationExcludeReserved(_ context.Context, organizationID uuid.UUID) ([]database.ProvisionerKey, error) {
q.mutex.RLock()
defer q.mutex.RUnlock()
keys := make([]database.ProvisionerKey, 0)
for _, key := range q.provisionerKeys {
if key.ID.String() == codersdk.ProvisionerKeyIDBuiltIn ||
key.ID.String() == codersdk.ProvisionerKeyIDUserAuth ||
key.ID.String() == codersdk.ProvisionerKeyIDPSK {
continue
}
if key.OrganizationID == organizationID {
keys = append(keys, key)
}
}
return keys, nil
}
func (q *FakeQuerier) ListWorkspaceAgentPortShares(_ context.Context, workspaceID uuid.UUID) ([]database.WorkspaceAgentPortShare, error) {
q.mutex.Lock()
defer q.mutex.Unlock()
@ -9311,6 +9366,7 @@ func (q *FakeQuerier) UpsertProvisionerDaemon(_ context.Context, arg database.Up
Version: arg.Version,
APIVersion: arg.APIVersion,
OrganizationID: arg.OrganizationID,
KeyID: arg.KeyID,
}
q.provisionerDaemons = append(q.provisionerDaemons, d)
return d, nil

View File

@ -1922,6 +1922,13 @@ func (m metricsStore) ListProvisionerKeysByOrganization(ctx context.Context, org
return r0, r1
}
func (m metricsStore) ListProvisionerKeysByOrganizationExcludeReserved(ctx context.Context, organizationID uuid.UUID) ([]database.ProvisionerKey, error) {
start := time.Now()
r0, r1 := m.s.ListProvisionerKeysByOrganizationExcludeReserved(ctx, organizationID)
m.queryLatencies.WithLabelValues("ListProvisionerKeysByOrganizationExcludeReserved").Observe(time.Since(start).Seconds())
return r0, r1
}
func (m metricsStore) ListWorkspaceAgentPortShares(ctx context.Context, workspaceID uuid.UUID) ([]database.WorkspaceAgentPortShare, error) {
start := time.Now()
r0, r1 := m.s.ListWorkspaceAgentPortShares(ctx, workspaceID)

View File

@ -4045,6 +4045,21 @@ func (mr *MockStoreMockRecorder) ListProvisionerKeysByOrganization(arg0, arg1 an
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListProvisionerKeysByOrganization", reflect.TypeOf((*MockStore)(nil).ListProvisionerKeysByOrganization), arg0, arg1)
}
// ListProvisionerKeysByOrganizationExcludeReserved mocks base method.
func (m *MockStore) ListProvisionerKeysByOrganizationExcludeReserved(arg0 context.Context, arg1 uuid.UUID) ([]database.ProvisionerKey, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "ListProvisionerKeysByOrganizationExcludeReserved", arg0, arg1)
ret0, _ := ret[0].([]database.ProvisionerKey)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// ListProvisionerKeysByOrganizationExcludeReserved indicates an expected call of ListProvisionerKeysByOrganizationExcludeReserved.
func (mr *MockStoreMockRecorder) ListProvisionerKeysByOrganizationExcludeReserved(arg0, arg1 any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListProvisionerKeysByOrganizationExcludeReserved", reflect.TypeOf((*MockStore)(nil).ListProvisionerKeysByOrganizationExcludeReserved), arg0, arg1)
}
// ListWorkspaceAgentPortShares mocks base method.
func (m *MockStore) ListWorkspaceAgentPortShares(arg0 context.Context, arg1 uuid.UUID) ([]database.WorkspaceAgentPortShare, error) {
m.ctrl.T.Helper()

View File

@ -26,6 +26,7 @@ import (
"github.com/coder/coder/v2/coderd/database/dbrollup"
"github.com/coder/coder/v2/coderd/database/dbtestutil"
"github.com/coder/coder/v2/coderd/database/dbtime"
"github.com/coder/coder/v2/codersdk"
"github.com/coder/coder/v2/provisionerd/proto"
"github.com/coder/coder/v2/provisionersdk"
"github.com/coder/coder/v2/testutil"
@ -412,6 +413,7 @@ func TestDeleteOldProvisionerDaemons(t *testing.T) {
Version: "1.0.0",
APIVersion: proto.CurrentVersion.String(),
OrganizationID: defaultOrg.ID,
KeyID: uuid.MustParse(codersdk.ProvisionerKeyIDBuiltIn),
})
require.NoError(t, err)
_, err = db.UpsertProvisionerDaemon(ctx, database.UpsertProvisionerDaemonParams{
@ -424,6 +426,7 @@ func TestDeleteOldProvisionerDaemons(t *testing.T) {
Version: "1.0.0",
APIVersion: proto.CurrentVersion.String(),
OrganizationID: defaultOrg.ID,
KeyID: uuid.MustParse(codersdk.ProvisionerKeyIDBuiltIn),
})
require.NoError(t, err)
_, err = db.UpsertProvisionerDaemon(ctx, database.UpsertProvisionerDaemonParams{
@ -438,6 +441,7 @@ func TestDeleteOldProvisionerDaemons(t *testing.T) {
Version: "1.0.0",
APIVersion: proto.CurrentVersion.String(),
OrganizationID: defaultOrg.ID,
KeyID: uuid.MustParse(codersdk.ProvisionerKeyIDBuiltIn),
})
require.NoError(t, err)
_, err = db.UpsertProvisionerDaemon(ctx, database.UpsertProvisionerDaemonParams{
@ -453,6 +457,7 @@ func TestDeleteOldProvisionerDaemons(t *testing.T) {
Version: "1.0.0",
APIVersion: proto.CurrentVersion.String(),
OrganizationID: defaultOrg.ID,
KeyID: uuid.MustParse(codersdk.ProvisionerKeyIDBuiltIn),
})
require.NoError(t, err)

View File

@ -832,7 +832,8 @@ CREATE TABLE provisioner_daemons (
last_seen_at timestamp with time zone,
version text DEFAULT ''::text NOT NULL,
api_version text DEFAULT '1.0'::text NOT NULL,
organization_id uuid NOT NULL
organization_id uuid NOT NULL,
key_id uuid NOT NULL
);
COMMENT ON COLUMN provisioner_daemons.api_version IS 'The API version of the provisioner daemon';
@ -2095,6 +2096,9 @@ ALTER TABLE ONLY organization_members
ALTER TABLE ONLY parameter_schemas
ADD CONSTRAINT parameter_schemas_job_id_fkey FOREIGN KEY (job_id) REFERENCES provisioner_jobs(id) ON DELETE CASCADE;
ALTER TABLE ONLY provisioner_daemons
ADD CONSTRAINT provisioner_daemons_key_id_fkey FOREIGN KEY (key_id) REFERENCES provisioner_keys(id) ON DELETE CASCADE;
ALTER TABLE ONLY provisioner_daemons
ADD CONSTRAINT provisioner_daemons_organization_id_fkey FOREIGN KEY (organization_id) REFERENCES organizations(id) ON DELETE CASCADE;

View File

@ -27,6 +27,7 @@ const (
ForeignKeyOrganizationMembersOrganizationIDUUID ForeignKeyConstraint = "organization_members_organization_id_uuid_fkey" // ALTER TABLE ONLY organization_members ADD CONSTRAINT organization_members_organization_id_uuid_fkey FOREIGN KEY (organization_id) REFERENCES organizations(id) ON DELETE CASCADE;
ForeignKeyOrganizationMembersUserIDUUID ForeignKeyConstraint = "organization_members_user_id_uuid_fkey" // ALTER TABLE ONLY organization_members ADD CONSTRAINT organization_members_user_id_uuid_fkey FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE;
ForeignKeyParameterSchemasJobID ForeignKeyConstraint = "parameter_schemas_job_id_fkey" // ALTER TABLE ONLY parameter_schemas ADD CONSTRAINT parameter_schemas_job_id_fkey FOREIGN KEY (job_id) REFERENCES provisioner_jobs(id) ON DELETE CASCADE;
ForeignKeyProvisionerDaemonsKeyID ForeignKeyConstraint = "provisioner_daemons_key_id_fkey" // ALTER TABLE ONLY provisioner_daemons ADD CONSTRAINT provisioner_daemons_key_id_fkey FOREIGN KEY (key_id) REFERENCES provisioner_keys(id) ON DELETE CASCADE;
ForeignKeyProvisionerDaemonsOrganizationID ForeignKeyConstraint = "provisioner_daemons_organization_id_fkey" // ALTER TABLE ONLY provisioner_daemons ADD CONSTRAINT provisioner_daemons_organization_id_fkey FOREIGN KEY (organization_id) REFERENCES organizations(id) ON DELETE CASCADE;
ForeignKeyProvisionerJobLogsJobID ForeignKeyConstraint = "provisioner_job_logs_job_id_fkey" // ALTER TABLE ONLY provisioner_job_logs ADD CONSTRAINT provisioner_job_logs_job_id_fkey FOREIGN KEY (job_id) REFERENCES provisioner_jobs(id) ON DELETE CASCADE;
ForeignKeyProvisionerJobTimingsJobID ForeignKeyConstraint = "provisioner_job_timings_job_id_fkey" // ALTER TABLE ONLY provisioner_job_timings ADD CONSTRAINT provisioner_job_timings_job_id_fkey FOREIGN KEY (job_id) REFERENCES provisioner_jobs(id) ON DELETE CASCADE;

View File

@ -0,0 +1,5 @@
ALTER TABLE provisioner_daemons DROP COLUMN key_id;
DELETE FROM provisioner_keys WHERE name = 'built-in';
DELETE FROM provisioner_keys WHERE name = 'psk';
DELETE FROM provisioner_keys WHERE name = 'user-auth';

View File

@ -0,0 +1,6 @@
INSERT INTO provisioner_keys (id, created_at, organization_id, name, hashed_secret, tags) VALUES ('00000000-0000-0000-0000-000000000001'::uuid, NOW(), (SELECT id FROM organizations WHERE is_default = true), 'built-in', ''::bytea, '{}');
INSERT INTO provisioner_keys (id, created_at, organization_id, name, hashed_secret, tags) VALUES ('00000000-0000-0000-0000-000000000002'::uuid, NOW(), (SELECT id FROM organizations WHERE is_default = true), 'user-auth', ''::bytea, '{}');
INSERT INTO provisioner_keys (id, created_at, organization_id, name, hashed_secret, tags) VALUES ('00000000-0000-0000-0000-000000000003'::uuid, NOW(), (SELECT id FROM organizations WHERE is_default = true), 'psk', ''::bytea, '{}');
ALTER TABLE provisioner_daemons ADD COLUMN key_id UUID REFERENCES provisioner_keys(id) ON DELETE CASCADE DEFAULT '00000000-0000-0000-0000-000000000001'::uuid NOT NULL;
ALTER TABLE provisioner_daemons ALTER COLUMN key_id DROP DEFAULT;

View File

@ -2311,6 +2311,7 @@ type ProvisionerDaemon struct {
// The API version of the provisioner daemon
APIVersion string `db:"api_version" json:"api_version"`
OrganizationID uuid.UUID `db:"organization_id" json:"organization_id"`
KeyID uuid.UUID `db:"key_id" json:"key_id"`
}
type ProvisionerJob struct {

View File

@ -391,6 +391,7 @@ type sqlcQuerier interface {
InsertWorkspaceResource(ctx context.Context, arg InsertWorkspaceResourceParams) (WorkspaceResource, error)
InsertWorkspaceResourceMetadata(ctx context.Context, arg InsertWorkspaceResourceMetadataParams) ([]WorkspaceResourceMetadatum, error)
ListProvisionerKeysByOrganization(ctx context.Context, organizationID uuid.UUID) ([]ProvisionerKey, error)
ListProvisionerKeysByOrganizationExcludeReserved(ctx context.Context, organizationID uuid.UUID) ([]ProvisionerKey, error)
ListWorkspaceAgentPortShares(ctx context.Context, workspaceID uuid.UUID) ([]WorkspaceAgentPortShare, error)
// Arguments are optional with uuid.Nil to ignore.
// - Use just 'organization_id' to get all members of an org

View File

@ -4971,7 +4971,7 @@ func (q *sqlQuerier) DeleteOldProvisionerDaemons(ctx context.Context) error {
const getProvisionerDaemons = `-- name: GetProvisionerDaemons :many
SELECT
id, created_at, name, provisioners, replica_id, tags, last_seen_at, version, api_version, organization_id
id, created_at, name, provisioners, replica_id, tags, last_seen_at, version, api_version, organization_id, key_id
FROM
provisioner_daemons
`
@ -4996,6 +4996,7 @@ func (q *sqlQuerier) GetProvisionerDaemons(ctx context.Context) ([]ProvisionerDa
&i.Version,
&i.APIVersion,
&i.OrganizationID,
&i.KeyID,
); err != nil {
return nil, err
}
@ -5012,7 +5013,7 @@ func (q *sqlQuerier) GetProvisionerDaemons(ctx context.Context) ([]ProvisionerDa
const getProvisionerDaemonsByOrganization = `-- name: GetProvisionerDaemonsByOrganization :many
SELECT
id, created_at, name, provisioners, replica_id, tags, last_seen_at, version, api_version, organization_id
id, created_at, name, provisioners, replica_id, tags, last_seen_at, version, api_version, organization_id, key_id
FROM
provisioner_daemons
WHERE
@ -5039,6 +5040,7 @@ func (q *sqlQuerier) GetProvisionerDaemonsByOrganization(ctx context.Context, or
&i.Version,
&i.APIVersion,
&i.OrganizationID,
&i.KeyID,
); err != nil {
return nil, err
}
@ -5084,7 +5086,8 @@ INSERT INTO
last_seen_at,
"version",
organization_id,
api_version
api_version,
key_id
)
VALUES (
gen_random_uuid(),
@ -5095,15 +5098,17 @@ VALUES (
$5,
$6,
$7,
$8
$8,
$9
) ON CONFLICT("organization_id", "name", LOWER(COALESCE(tags ->> 'owner'::text, ''::text))) DO UPDATE SET
provisioners = $3,
tags = $4,
last_seen_at = $5,
"version" = $6,
api_version = $8,
organization_id = $7
RETURNING id, created_at, name, provisioners, replica_id, tags, last_seen_at, version, api_version, organization_id
organization_id = $7,
key_id = $9
RETURNING id, created_at, name, provisioners, replica_id, tags, last_seen_at, version, api_version, organization_id, key_id
`
type UpsertProvisionerDaemonParams struct {
@ -5115,6 +5120,7 @@ type UpsertProvisionerDaemonParams struct {
Version string `db:"version" json:"version"`
OrganizationID uuid.UUID `db:"organization_id" json:"organization_id"`
APIVersion string `db:"api_version" json:"api_version"`
KeyID uuid.UUID `db:"key_id" json:"key_id"`
}
func (q *sqlQuerier) UpsertProvisionerDaemon(ctx context.Context, arg UpsertProvisionerDaemonParams) (ProvisionerDaemon, error) {
@ -5127,6 +5133,7 @@ func (q *sqlQuerier) UpsertProvisionerDaemon(ctx context.Context, arg UpsertProv
arg.Version,
arg.OrganizationID,
arg.APIVersion,
arg.KeyID,
)
var i ProvisionerDaemon
err := row.Scan(
@ -5140,6 +5147,7 @@ func (q *sqlQuerier) UpsertProvisionerDaemon(ctx context.Context, arg UpsertProv
&i.Version,
&i.APIVersion,
&i.OrganizationID,
&i.KeyID,
)
return i, err
}
@ -6021,6 +6029,54 @@ func (q *sqlQuerier) ListProvisionerKeysByOrganization(ctx context.Context, orga
return items, nil
}
const listProvisionerKeysByOrganizationExcludeReserved = `-- name: ListProvisionerKeysByOrganizationExcludeReserved :many
SELECT
id, created_at, organization_id, name, hashed_secret, tags
FROM
provisioner_keys
WHERE
organization_id = $1
AND
-- exclude reserved built-in key
id != '00000000-0000-0000-0000-000000000001'::uuid
AND
-- exclude reserved user-auth key
id != '00000000-0000-0000-0000-000000000002'::uuid
AND
-- exclude reserved psk key
id != '00000000-0000-0000-0000-000000000003'::uuid
`
func (q *sqlQuerier) ListProvisionerKeysByOrganizationExcludeReserved(ctx context.Context, organizationID uuid.UUID) ([]ProvisionerKey, error) {
rows, err := q.db.QueryContext(ctx, listProvisionerKeysByOrganizationExcludeReserved, organizationID)
if err != nil {
return nil, err
}
defer rows.Close()
var items []ProvisionerKey
for rows.Next() {
var i ProvisionerKey
if err := rows.Scan(
&i.ID,
&i.CreatedAt,
&i.OrganizationID,
&i.Name,
&i.HashedSecret,
&i.Tags,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const getWorkspaceProxies = `-- name: GetWorkspaceProxies :many
SELECT
id, name, display_name, icon, url, wildcard_hostname, created_at, updated_at, deleted, token_hashed_secret, region_id, derp_enabled, derp_only, version

View File

@ -33,7 +33,8 @@ INSERT INTO
last_seen_at,
"version",
organization_id,
api_version
api_version,
key_id
)
VALUES (
gen_random_uuid(),
@ -44,14 +45,16 @@ VALUES (
@last_seen_at,
@version,
@organization_id,
@api_version
@api_version,
@key_id
) ON CONFLICT("organization_id", "name", LOWER(COALESCE(tags ->> 'owner'::text, ''::text))) DO UPDATE SET
provisioners = @provisioners,
tags = @tags,
last_seen_at = @last_seen_at,
"version" = @version,
api_version = @api_version,
organization_id = @organization_id
organization_id = @organization_id,
key_id = @key_id
RETURNING *;
-- name: UpdateProvisionerDaemonLastSeenAt :exec

View File

@ -37,6 +37,23 @@ WHERE
AND
lower(name) = lower(@name);
-- name: ListProvisionerKeysByOrganizationExcludeReserved :many
SELECT
*
FROM
provisioner_keys
WHERE
organization_id = $1
AND
-- exclude reserved built-in key
id != '00000000-0000-0000-0000-000000000001'::uuid
AND
-- exclude reserved user-auth key
id != '00000000-0000-0000-0000-000000000002'::uuid
AND
-- exclude reserved psk key
id != '00000000-0000-0000-0000-000000000003'::uuid;
-- name: ListProvisionerKeysByOrganization :many
SELECT
*