From e85b4b83084233067bd9aec460d31dede70309fc Mon Sep 17 00:00:00 2001 From: Ryan Yanulites Date: Tue, 6 Aug 2024 10:31:52 -0600 Subject: [PATCH 1/7] initial migration script --- ...0806142109_add_resource_mapping_groups.sql | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 service/policy/db/migrations/20240806142109_add_resource_mapping_groups.sql diff --git a/service/policy/db/migrations/20240806142109_add_resource_mapping_groups.sql b/service/policy/db/migrations/20240806142109_add_resource_mapping_groups.sql new file mode 100644 index 000000000..23099e348 --- /dev/null +++ b/service/policy/db/migrations/20240806142109_add_resource_mapping_groups.sql @@ -0,0 +1,22 @@ +-- +goose Up +-- +goose StatementBegin + +CREATE TABLE IF NOT EXISTS resource_mapping_groups ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + namespace_id UUID NOT NULL REFERENCES attribute_namespaces(id), + name VARCHAR NOT NULL, + UNIQUE(namespace_id, name) +); + +ALTER TABLE resource_mappings ADD COLUMN group_id UUID REFERENCES resource_mapping_groups(id); + +-- +goose StatementEnd + +-- +goose Down +-- +goose StatementBegin + +ALTER TABLE resource_mappings DROP COLUMN group_id; + +DROP TABLE IF EXISTS resource_mapping_groups; + +-- +goose StatementEnd From ead67837e2b44b549269545c4318eca38e77b914 Mon Sep 17 00:00:00 2001 From: Ryan Yanulites Date: Tue, 6 Aug 2024 16:03:22 -0600 Subject: [PATCH 2/7] add delete action on FK and sqlc crud --- ...0806142109_add_resource_mapping_groups.sql | 2 +- service/policy/db/models.go | 7 ++ service/policy/db/query.sql | 21 ++++++ service/policy/db/query.sql.go | 72 +++++++++++++++++++ 4 files changed, 101 insertions(+), 1 deletion(-) diff --git a/service/policy/db/migrations/20240806142109_add_resource_mapping_groups.sql b/service/policy/db/migrations/20240806142109_add_resource_mapping_groups.sql index 23099e348..4ef74dcda 100644 --- a/service/policy/db/migrations/20240806142109_add_resource_mapping_groups.sql +++ b/service/policy/db/migrations/20240806142109_add_resource_mapping_groups.sql @@ -8,7 +8,7 @@ CREATE TABLE IF NOT EXISTS resource_mapping_groups ( UNIQUE(namespace_id, name) ); -ALTER TABLE resource_mappings ADD COLUMN group_id UUID REFERENCES resource_mapping_groups(id); +ALTER TABLE resource_mappings ADD COLUMN group_id UUID REFERENCES resource_mapping_groups(id) ON DELETE SET NULL; -- +goose StatementEnd diff --git a/service/policy/db/models.go b/service/policy/db/models.go index c90edd85e..77e2f52f3 100644 --- a/service/policy/db/models.go +++ b/service/policy/db/models.go @@ -139,6 +139,13 @@ type ResourceMapping struct { Metadata []byte `json:"metadata"` CreatedAt pgtype.Timestamptz `json:"created_at"` UpdatedAt pgtype.Timestamptz `json:"updated_at"` + GroupID pgtype.UUID `json:"group_id"` +} + +type ResourceMappingGroup struct { + ID string `json:"id"` + NamespaceID string `json:"namespace_id"` + Name string `json:"name"` } type SubjectConditionSet struct { diff --git a/service/policy/db/query.sql b/service/policy/db/query.sql index 1af91bef7..8583e03af 100644 --- a/service/policy/db/query.sql +++ b/service/policy/db/query.sql @@ -1,4 +1,6 @@ +---------------------------------------------------------------- -- KEY ACCESS SERVERS +---------------------------------------------------------------- -- name: ListKeyAccessServers :many SELECT id, uri, public_key, @@ -27,3 +29,22 @@ RETURNING id; -- name: DeleteKeyAccessServer :execrows DELETE FROM key_access_servers WHERE id = $1; +---------------------------------------------------------------- +-- RESOURCE MAPPING GROUPS +---------------------------------------------------------------- + +-- name: CreateResourceMappingGroup :one +INSERT INTO resource_mapping_groups (namespace_id, name) +VALUES ($1, $2) +RETURNING id; + +-- name: UpdateResourceMappingGroup :one +UPDATE resource_mapping_groups +SET + -- assuming name is the only modifiable field + name = coalesce(sqlc.narg('name'), name) +WHERE id = $1 +RETURNING id; + +-- name: DeleteResourceMappingGroup :execrows +DELETE FROM resource_mapping_groups WHERE id = $1; diff --git a/service/policy/db/query.sql.go b/service/policy/db/query.sql.go index 2bcdbab10..7565a889f 100644 --- a/service/policy/db/query.sql.go +++ b/service/policy/db/query.sql.go @@ -35,6 +35,32 @@ func (q *Queries) CreateKeyAccessServer(ctx context.Context, arg CreateKeyAccess return id, err } +const createResourceMappingGroup = `-- name: CreateResourceMappingGroup :one + +INSERT INTO resource_mapping_groups (namespace_id, name) +VALUES ($1, $2) +RETURNING id +` + +type CreateResourceMappingGroupParams struct { + NamespaceID string `json:"namespace_id"` + Name string `json:"name"` +} + +// -------------------------------------------------------------- +// RESOURCE MAPPING GROUPS +// -------------------------------------------------------------- +// +// INSERT INTO resource_mapping_groups (namespace_id, name) +// VALUES ($1, $2) +// RETURNING id +func (q *Queries) CreateResourceMappingGroup(ctx context.Context, arg CreateResourceMappingGroupParams) (string, error) { + row := q.db.QueryRow(ctx, createResourceMappingGroup, arg.NamespaceID, arg.Name) + var id string + err := row.Scan(&id) + return id, err +} + const deleteKeyAccessServer = `-- name: DeleteKeyAccessServer :execrows DELETE FROM key_access_servers WHERE id = $1 ` @@ -50,6 +76,21 @@ func (q *Queries) DeleteKeyAccessServer(ctx context.Context, id string) (int64, return result.RowsAffected(), nil } +const deleteResourceMappingGroup = `-- name: DeleteResourceMappingGroup :execrows +DELETE FROM resource_mapping_groups WHERE id = $1 +` + +// DeleteResourceMappingGroup +// +// DELETE FROM resource_mapping_groups WHERE id = $1 +func (q *Queries) DeleteResourceMappingGroup(ctx context.Context, id string) (int64, error) { + result, err := q.db.Exec(ctx, deleteResourceMappingGroup, id) + if err != nil { + return 0, err + } + return result.RowsAffected(), nil +} + const getKeyAccessServer = `-- name: GetKeyAccessServer :one SELECT id, uri, public_key, JSON_STRIP_NULLS(JSON_BUILD_OBJECT('labels', metadata -> 'labels', 'created_at', created_at, 'updated_at', updated_at)) as metadata @@ -94,7 +135,9 @@ type ListKeyAccessServersRow struct { Metadata []byte `json:"metadata"` } +// -------------------------------------------------------------- // KEY ACCESS SERVERS +// -------------------------------------------------------------- // // SELECT id, uri, public_key, // JSON_STRIP_NULLS(JSON_BUILD_OBJECT('labels', metadata -> 'labels', 'created_at', created_at, 'updated_at', updated_at)) as metadata @@ -161,3 +204,32 @@ func (q *Queries) UpdateKeyAccessServer(ctx context.Context, arg UpdateKeyAccess err := row.Scan(&id) return id, err } + +const updateResourceMappingGroup = `-- name: UpdateResourceMappingGroup :one +UPDATE resource_mapping_groups +SET + -- assuming name is the only modifiable field + name = coalesce($2, name) +WHERE id = $1 +RETURNING id +` + +type UpdateResourceMappingGroupParams struct { + ID string `json:"id"` + Name pgtype.Text `json:"name"` +} + +// UpdateResourceMappingGroup +// +// UPDATE resource_mapping_groups +// SET +// -- assuming name is the only modifiable field +// name = coalesce($2, name) +// WHERE id = $1 +// RETURNING id +func (q *Queries) UpdateResourceMappingGroup(ctx context.Context, arg UpdateResourceMappingGroupParams) (string, error) { + row := q.db.QueryRow(ctx, updateResourceMappingGroup, arg.ID, arg.Name) + var id string + err := row.Scan(&id) + return id, err +} From c19cd5b2e8b8699599614073c482f9722198ed98 Mon Sep 17 00:00:00 2001 From: Ryan Yanulites Date: Tue, 6 Aug 2024 16:16:09 -0600 Subject: [PATCH 3/7] add table comments per most recent migration PR --- .../20240806142109_add_resource_mapping_groups.sql | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/service/policy/db/migrations/20240806142109_add_resource_mapping_groups.sql b/service/policy/db/migrations/20240806142109_add_resource_mapping_groups.sql index 4ef74dcda..cbd02dfd6 100644 --- a/service/policy/db/migrations/20240806142109_add_resource_mapping_groups.sql +++ b/service/policy/db/migrations/20240806142109_add_resource_mapping_groups.sql @@ -8,8 +8,15 @@ CREATE TABLE IF NOT EXISTS resource_mapping_groups ( UNIQUE(namespace_id, name) ); +COMMENT ON TABLE resource_mapping_groups IS 'Table to store the groups of resource mappings by unique namespace and group name combinations'; +COMMENT ON COLUMN resource_mapping_groups.id IS 'Primary key for the table'; +COMMENT ON COLUMN resource_mapping_groups.namespace_id IS 'Foreign key to the namespace of the attribute'; +COMMENT ON COLUMN resource_mapping_groups.name IS 'Name for the group of resource mappings'; + ALTER TABLE resource_mappings ADD COLUMN group_id UUID REFERENCES resource_mapping_groups(id) ON DELETE SET NULL; +COMMENT ON COLUMN resource_mappings.group_id IS 'Foreign key to the parent group of the resource mapping'; + -- +goose StatementEnd -- +goose Down From 5ca9a948f09f7b6ede26738ff45e3c5817dec7a7 Mon Sep 17 00:00:00 2001 From: Ryan Yanulites Date: Tue, 6 Aug 2024 16:36:57 -0600 Subject: [PATCH 4/7] add erd --- ...40806142109_add_resource_mapping_groups.md | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 service/policy/db/migrations/20240806142109_add_resource_mapping_groups.md diff --git a/service/policy/db/migrations/20240806142109_add_resource_mapping_groups.md b/service/policy/db/migrations/20240806142109_add_resource_mapping_groups.md new file mode 100644 index 000000000..b76d2a0e4 --- /dev/null +++ b/service/policy/db/migrations/20240806142109_add_resource_mapping_groups.md @@ -0,0 +1,32 @@ +# Diagram for 20240806142109_add_resource_mapping_groups.sql + +## Background + +This schema reflects the addition of a `resource_mapping_groups` table, allowing existing Resource Mappings to be grouped by namespace and a common name. The migration also updates the `resource_mappings` table to include a `group_id` column, which will be used to optionally associate a Resource Mapping with a group. + +# ERD + +```mermaid +--- +title: Database Schema Mermaid Diagram +--- + +erDiagram + + ResourceMappingGroup ||--|{ ResourceMapping : has + + ResourceMappingGroup { + uuid id PK + uuid namespace_id + varchar name + compIdx comp_key UK "namespace_id + name" + } + + ResourceMapping { + uuid id PK + uuid attribute_value_id FK + varchar[] terms + jsonb metadata + uuid group_id FK + } +``` \ No newline at end of file From 3262cbc58c899b63c138adec382dee3801cb3bf3 Mon Sep 17 00:00:00 2001 From: Ryan Yanulites Date: Wed, 7 Aug 2024 07:06:28 -0600 Subject: [PATCH 5/7] add namespace_id to updateable query for unsafe actions --- service/policy/db/models.go | 11 ++++++++--- service/policy/db/query.sql | 2 +- service/policy/db/query.sql.go | 15 ++++++++------- 3 files changed, 17 insertions(+), 11 deletions(-) diff --git a/service/policy/db/models.go b/service/policy/db/models.go index ad69acf6e..372cb5188 100644 --- a/service/policy/db/models.go +++ b/service/policy/db/models.go @@ -168,13 +168,18 @@ type ResourceMapping struct { Metadata []byte `json:"metadata"` CreatedAt pgtype.Timestamptz `json:"created_at"` UpdatedAt pgtype.Timestamptz `json:"updated_at"` - GroupID pgtype.UUID `json:"group_id"` + // Foreign key to the parent group of the resource mapping + GroupID pgtype.UUID `json:"group_id"` } +// Table to store the groups of resource mappings by unique namespace and group name combinations type ResourceMappingGroup struct { - ID string `json:"id"` + // Primary key for the table + ID string `json:"id"` + // Foreign key to the namespace of the attribute NamespaceID string `json:"namespace_id"` - Name string `json:"name"` + // Name for the group of resource mappings + Name string `json:"name"` } // Table to store sets of conditions that logically entitle subject entity representations to attribute values via a subject mapping diff --git a/service/policy/db/query.sql b/service/policy/db/query.sql index 8583e03af..0179a33ea 100644 --- a/service/policy/db/query.sql +++ b/service/policy/db/query.sql @@ -41,7 +41,7 @@ RETURNING id; -- name: UpdateResourceMappingGroup :one UPDATE resource_mapping_groups SET - -- assuming name is the only modifiable field + namespace_id = coalesce(sqlc.narg('namespace_id'), namespace_id), name = coalesce(sqlc.narg('name'), name) WHERE id = $1 RETURNING id; diff --git a/service/policy/db/query.sql.go b/service/policy/db/query.sql.go index 7565a889f..0efbbfa63 100644 --- a/service/policy/db/query.sql.go +++ b/service/policy/db/query.sql.go @@ -208,27 +208,28 @@ func (q *Queries) UpdateKeyAccessServer(ctx context.Context, arg UpdateKeyAccess const updateResourceMappingGroup = `-- name: UpdateResourceMappingGroup :one UPDATE resource_mapping_groups SET - -- assuming name is the only modifiable field - name = coalesce($2, name) + namespace_id = coalesce($2, namespace_id), + name = coalesce($3, name) WHERE id = $1 RETURNING id ` type UpdateResourceMappingGroupParams struct { - ID string `json:"id"` - Name pgtype.Text `json:"name"` + ID string `json:"id"` + NamespaceID pgtype.UUID `json:"namespace_id"` + Name pgtype.Text `json:"name"` } // UpdateResourceMappingGroup // // UPDATE resource_mapping_groups // SET -// -- assuming name is the only modifiable field -// name = coalesce($2, name) +// namespace_id = coalesce($2, namespace_id), +// name = coalesce($3, name) // WHERE id = $1 // RETURNING id func (q *Queries) UpdateResourceMappingGroup(ctx context.Context, arg UpdateResourceMappingGroupParams) (string, error) { - row := q.db.QueryRow(ctx, updateResourceMappingGroup, arg.ID, arg.Name) + row := q.db.QueryRow(ctx, updateResourceMappingGroup, arg.ID, arg.NamespaceID, arg.Name) var id string err := row.Scan(&id) return id, err From b8822e5e04899790253924f5086a05fd946e3902 Mon Sep 17 00:00:00 2001 From: Ryan Yanulites Date: Wed, 7 Aug 2024 07:20:51 -0600 Subject: [PATCH 6/7] add read CRUD query --- service/policy/db/query.sql | 5 +++++ service/policy/db/query.sql.go | 26 ++++++++++++++++++++++---- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/service/policy/db/query.sql b/service/policy/db/query.sql index 0179a33ea..6b3347434 100644 --- a/service/policy/db/query.sql +++ b/service/policy/db/query.sql @@ -33,6 +33,11 @@ DELETE FROM key_access_servers WHERE id = $1; -- RESOURCE MAPPING GROUPS ---------------------------------------------------------------- +-- name: GetResourceMappingGroup :one +SELECT id, namespace_id, name +FROM resource_mapping_groups +WHERE id = $1; + -- name: CreateResourceMappingGroup :one INSERT INTO resource_mapping_groups (namespace_id, name) VALUES ($1, $2) diff --git a/service/policy/db/query.sql.go b/service/policy/db/query.sql.go index 0efbbfa63..3334ba80c 100644 --- a/service/policy/db/query.sql.go +++ b/service/policy/db/query.sql.go @@ -36,7 +36,6 @@ func (q *Queries) CreateKeyAccessServer(ctx context.Context, arg CreateKeyAccess } const createResourceMappingGroup = `-- name: CreateResourceMappingGroup :one - INSERT INTO resource_mapping_groups (namespace_id, name) VALUES ($1, $2) RETURNING id @@ -47,9 +46,7 @@ type CreateResourceMappingGroupParams struct { Name string `json:"name"` } -// -------------------------------------------------------------- -// RESOURCE MAPPING GROUPS -// -------------------------------------------------------------- +// CreateResourceMappingGroup // // INSERT INTO resource_mapping_groups (namespace_id, name) // VALUES ($1, $2) @@ -121,6 +118,27 @@ func (q *Queries) GetKeyAccessServer(ctx context.Context, id string) (GetKeyAcce return i, err } +const getResourceMappingGroup = `-- name: GetResourceMappingGroup :one + +SELECT id, namespace_id, name +FROM resource_mapping_groups +WHERE id = $1 +` + +// -------------------------------------------------------------- +// RESOURCE MAPPING GROUPS +// -------------------------------------------------------------- +// +// SELECT id, namespace_id, name +// FROM resource_mapping_groups +// WHERE id = $1 +func (q *Queries) GetResourceMappingGroup(ctx context.Context, id string) (ResourceMappingGroup, error) { + row := q.db.QueryRow(ctx, getResourceMappingGroup, id) + var i ResourceMappingGroup + err := row.Scan(&i.ID, &i.NamespaceID, &i.Name) + return i, err +} + const listKeyAccessServers = `-- name: ListKeyAccessServers :many SELECT id, uri, public_key, From 1b103dc6e3b17b99d2d2f8e02fafe3544a9ddd9c Mon Sep 17 00:00:00 2001 From: Ryan Yanulites Date: Wed, 7 Aug 2024 08:49:48 -0600 Subject: [PATCH 7/7] address PR feedback --- ...0806142109_add_resource_mapping_groups.sql | 4 +- service/policy/db/models.go | 2 +- service/policy/db/query.sql | 4 ++ service/policy/db/query.sql.go | 37 +++++++++++++++++-- 4 files changed, 40 insertions(+), 7 deletions(-) diff --git a/service/policy/db/migrations/20240806142109_add_resource_mapping_groups.sql b/service/policy/db/migrations/20240806142109_add_resource_mapping_groups.sql index cbd02dfd6..1cef3156c 100644 --- a/service/policy/db/migrations/20240806142109_add_resource_mapping_groups.sql +++ b/service/policy/db/migrations/20240806142109_add_resource_mapping_groups.sql @@ -3,7 +3,7 @@ CREATE TABLE IF NOT EXISTS resource_mapping_groups ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - namespace_id UUID NOT NULL REFERENCES attribute_namespaces(id), + namespace_id UUID NOT NULL REFERENCES attribute_namespaces(id) ON DELETE CASCADE, name VARCHAR NOT NULL, UNIQUE(namespace_id, name) ); @@ -15,7 +15,7 @@ COMMENT ON COLUMN resource_mapping_groups.name IS 'Name for the group of resourc ALTER TABLE resource_mappings ADD COLUMN group_id UUID REFERENCES resource_mapping_groups(id) ON DELETE SET NULL; -COMMENT ON COLUMN resource_mappings.group_id IS 'Foreign key to the parent group of the resource mapping'; +COMMENT ON COLUMN resource_mappings.group_id IS 'Foreign key to the parent group of the resource mapping (optional, a resource mapping may not be in a group)'; -- +goose StatementEnd diff --git a/service/policy/db/models.go b/service/policy/db/models.go index 372cb5188..33d67905f 100644 --- a/service/policy/db/models.go +++ b/service/policy/db/models.go @@ -168,7 +168,7 @@ type ResourceMapping struct { Metadata []byte `json:"metadata"` CreatedAt pgtype.Timestamptz `json:"created_at"` UpdatedAt pgtype.Timestamptz `json:"updated_at"` - // Foreign key to the parent group of the resource mapping + // Foreign key to the parent group of the resource mapping (optional, a resource mapping may not be in a group) GroupID pgtype.UUID `json:"group_id"` } diff --git a/service/policy/db/query.sql b/service/policy/db/query.sql index 6b3347434..eb21a0d63 100644 --- a/service/policy/db/query.sql +++ b/service/policy/db/query.sql @@ -33,6 +33,10 @@ DELETE FROM key_access_servers WHERE id = $1; -- RESOURCE MAPPING GROUPS ---------------------------------------------------------------- +-- name: ListResourceMappingGroups :many +SELECT id, namespace_id, name +FROM resource_mapping_groups; + -- name: GetResourceMappingGroup :one SELECT id, namespace_id, name FROM resource_mapping_groups diff --git a/service/policy/db/query.sql.go b/service/policy/db/query.sql.go index 3334ba80c..7b767d916 100644 --- a/service/policy/db/query.sql.go +++ b/service/policy/db/query.sql.go @@ -119,15 +119,12 @@ func (q *Queries) GetKeyAccessServer(ctx context.Context, id string) (GetKeyAcce } const getResourceMappingGroup = `-- name: GetResourceMappingGroup :one - SELECT id, namespace_id, name FROM resource_mapping_groups WHERE id = $1 ` -// -------------------------------------------------------------- -// RESOURCE MAPPING GROUPS -// -------------------------------------------------------------- +// GetResourceMappingGroup // // SELECT id, namespace_id, name // FROM resource_mapping_groups @@ -185,6 +182,38 @@ func (q *Queries) ListKeyAccessServers(ctx context.Context) ([]ListKeyAccessServ return items, nil } +const listResourceMappingGroups = `-- name: ListResourceMappingGroups :many + +SELECT id, namespace_id, name +FROM resource_mapping_groups +` + +// -------------------------------------------------------------- +// RESOURCE MAPPING GROUPS +// -------------------------------------------------------------- +// +// SELECT id, namespace_id, name +// FROM resource_mapping_groups +func (q *Queries) ListResourceMappingGroups(ctx context.Context) ([]ResourceMappingGroup, error) { + rows, err := q.db.Query(ctx, listResourceMappingGroups) + if err != nil { + return nil, err + } + defer rows.Close() + var items []ResourceMappingGroup + for rows.Next() { + var i ResourceMappingGroup + if err := rows.Scan(&i.ID, &i.NamespaceID, &i.Name); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + const updateKeyAccessServer = `-- name: UpdateKeyAccessServer :one UPDATE key_access_servers SET