From c3ad030b374c002d26d46dc409bbeac4402f90f2 Mon Sep 17 00:00:00 2001 From: Henning Perl Date: Sun, 9 Jan 2022 11:24:32 +0100 Subject: [PATCH] feat: Add mapping for encoding subject into UUIDs A new table was added that stores a mapping between an arbitraty string and a UUIDv5 of that string, namespaced by a namespace UUID that the user can set in the namespace configuration. --- internal/driver/registry.go | 2 + internal/driver/registry_default.go | 8 +++ internal/persistence/definitions.go | 2 + .../sql/migrations/single_table_test.go | 10 ++++ ...eate-uuid-mapping-table.cockroach.down.sql | 0 ...create-uuid-mapping-table.cockroach.up.sql | 10 ++++ ...0_create-uuid-mapping-table.mysql.down.sql | 0 ...000_create-uuid-mapping-table.mysql.up.sql | 10 ++++ ...reate-uuid-mapping-table.postgres.down.sql | 0 ..._create-uuid-mapping-table.postgres.up.sql | 10 ++++ ...create-uuid-mapping-table.sqlite3.down.sql | 0 ...0_create-uuid-mapping-table.sqlite3.up.sql | 10 ++++ ...eate-uuid-mapping-table.cockroach.down.sql | 1 + ...create-uuid-mapping-table.cockroach.up.sql | 0 ...1_create-uuid-mapping-table.mysql.down.sql | 1 + ...001_create-uuid-mapping-table.mysql.up.sql | 0 ...reate-uuid-mapping-table.postgres.down.sql | 1 + ..._create-uuid-mapping-table.postgres.up.sql | 0 ...create-uuid-mapping-table.sqlite3.down.sql | 1 + ...1_create-uuid-mapping-table.sqlite3.up.sql | 0 ...0200400_create-uuid-mapping-table.down.sql | 1 + ...400_create-uuid-mapping-table.mysql.up.sql | 10 ++++ ...110200400_create-uuid-mapping-table.up.sql | 10 ++++ internal/persistence/sql/uuid_mapping.go | 45 ++++++++++++++++ internal/persistence/sql/uuid_mapping_test.go | 52 +++++++++++++++++++ internal/uuidmapping/definitions.go | 17 ++++++ 26 files changed, 201 insertions(+) create mode 100644 internal/persistence/sql/migrations/sql/20220110200400000000_create-uuid-mapping-table.cockroach.down.sql create mode 100644 internal/persistence/sql/migrations/sql/20220110200400000000_create-uuid-mapping-table.cockroach.up.sql create mode 100644 internal/persistence/sql/migrations/sql/20220110200400000000_create-uuid-mapping-table.mysql.down.sql create mode 100644 internal/persistence/sql/migrations/sql/20220110200400000000_create-uuid-mapping-table.mysql.up.sql create mode 100644 internal/persistence/sql/migrations/sql/20220110200400000000_create-uuid-mapping-table.postgres.down.sql create mode 100644 internal/persistence/sql/migrations/sql/20220110200400000000_create-uuid-mapping-table.postgres.up.sql create mode 100644 internal/persistence/sql/migrations/sql/20220110200400000000_create-uuid-mapping-table.sqlite3.down.sql create mode 100644 internal/persistence/sql/migrations/sql/20220110200400000000_create-uuid-mapping-table.sqlite3.up.sql create mode 100644 internal/persistence/sql/migrations/sql/20220110200400000001_create-uuid-mapping-table.cockroach.down.sql create mode 100644 internal/persistence/sql/migrations/sql/20220110200400000001_create-uuid-mapping-table.cockroach.up.sql create mode 100644 internal/persistence/sql/migrations/sql/20220110200400000001_create-uuid-mapping-table.mysql.down.sql create mode 100644 internal/persistence/sql/migrations/sql/20220110200400000001_create-uuid-mapping-table.mysql.up.sql create mode 100644 internal/persistence/sql/migrations/sql/20220110200400000001_create-uuid-mapping-table.postgres.down.sql create mode 100644 internal/persistence/sql/migrations/sql/20220110200400000001_create-uuid-mapping-table.postgres.up.sql create mode 100644 internal/persistence/sql/migrations/sql/20220110200400000001_create-uuid-mapping-table.sqlite3.down.sql create mode 100644 internal/persistence/sql/migrations/sql/20220110200400000001_create-uuid-mapping-table.sqlite3.up.sql create mode 100644 internal/persistence/sql/migrations/templates/20220110200400_create-uuid-mapping-table.down.sql create mode 100644 internal/persistence/sql/migrations/templates/20220110200400_create-uuid-mapping-table.mysql.up.sql create mode 100644 internal/persistence/sql/migrations/templates/20220110200400_create-uuid-mapping-table.up.sql create mode 100644 internal/persistence/sql/uuid_mapping.go create mode 100644 internal/persistence/sql/uuid_mapping_test.go create mode 100644 internal/uuidmapping/definitions.go diff --git a/internal/driver/registry.go b/internal/driver/registry.go index 481c895d3..e5a207052 100644 --- a/internal/driver/registry.go +++ b/internal/driver/registry.go @@ -7,6 +7,7 @@ import ( "github.com/gobuffalo/pop/v6" "github.com/ory/keto/internal/driver/config" + "github.com/ory/keto/internal/uuidmapping" "github.com/spf13/cobra" @@ -34,6 +35,7 @@ type ( x.WriterProvider relationtuple.ManagerProvider + uuidmapping.ManagerProvider expand.EngineProvider check.EngineProvider persistence.Migrator diff --git a/internal/driver/registry_default.go b/internal/driver/registry_default.go index f18cb2d39..0a0675194 100644 --- a/internal/driver/registry_default.go +++ b/internal/driver/registry_default.go @@ -31,6 +31,7 @@ import ( grpcHealthV1 "google.golang.org/grpc/health/grpc_health_v1" "github.com/ory/keto/internal/driver/config" + "github.com/ory/keto/internal/uuidmapping" "github.com/ory/herodot" "github.com/ory/x/healthx" @@ -149,6 +150,13 @@ func (r *RegistryDefault) RelationTupleManager() relationtuple.Manager { return r.p } +func (r *RegistryDefault) UUIDMappingManager() uuidmapping.Manager { + if r.p == nil { + panic("no relation tuple manager, but expected to have one") + } + return r.p +} + func (r *RegistryDefault) Persister() persistence.Persister { if r.p == nil { panic("no persister, but expected to have one") diff --git a/internal/persistence/definitions.go b/internal/persistence/definitions.go index b2c9c1536..320568f13 100644 --- a/internal/persistence/definitions.go +++ b/internal/persistence/definitions.go @@ -9,11 +9,13 @@ import ( "github.com/gobuffalo/pop/v6" "github.com/ory/keto/internal/relationtuple" + "github.com/ory/keto/internal/uuidmapping" ) type ( Persister interface { relationtuple.Manager + uuidmapping.Manager Connection(ctx context.Context) *pop.Connection } diff --git a/internal/persistence/sql/migrations/single_table_test.go b/internal/persistence/sql/migrations/single_table_test.go index 13e3f8cf2..defb4187c 100644 --- a/internal/persistence/sql/migrations/single_table_test.go +++ b/internal/persistence/sql/migrations/single_table_test.go @@ -5,6 +5,7 @@ import ( "strconv" "testing" + "github.com/gofrs/uuid" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -96,6 +97,15 @@ func TestToSingleTableMigrator(t *testing.T) { assert.Equal(t, sSet, rts[1]) }) + t.Run("case=uuid mapping", func(t *testing.T) { + id := uuid.Must(uuid.NewV4()) + rep1 := "foo" + r.UUIDMappingManager().AddUUIDMapping(ctx, id, rep1) + rep2, err := r.UUIDMappingManager().LookupUUID(ctx, id) + assert.NoError(t, err) + assert.Equal(t, rep1, rep2) + }) + t.Run("case=paginates", func(t *testing.T) { n := setup(t) diff --git a/internal/persistence/sql/migrations/sql/20220110200400000000_create-uuid-mapping-table.cockroach.down.sql b/internal/persistence/sql/migrations/sql/20220110200400000000_create-uuid-mapping-table.cockroach.down.sql new file mode 100644 index 000000000..e69de29bb diff --git a/internal/persistence/sql/migrations/sql/20220110200400000000_create-uuid-mapping-table.cockroach.up.sql b/internal/persistence/sql/migrations/sql/20220110200400000000_create-uuid-mapping-table.cockroach.up.sql new file mode 100644 index 000000000..48641a7a7 --- /dev/null +++ b/internal/persistence/sql/migrations/sql/20220110200400000000_create-uuid-mapping-table.cockroach.up.sql @@ -0,0 +1,10 @@ +CREATE TABLE keto_uuid_mappings +( + id UUID NOT NULL, + string_representation VARCHAR(64) NOT NULL CHECK (string_representation <> ''), + + PRIMARY KEY (id), + + -- enforce uniqueness + CONSTRAINT chk_keto_uuid_map_uniq UNIQUE (id, string_representation) +); \ No newline at end of file diff --git a/internal/persistence/sql/migrations/sql/20220110200400000000_create-uuid-mapping-table.mysql.down.sql b/internal/persistence/sql/migrations/sql/20220110200400000000_create-uuid-mapping-table.mysql.down.sql new file mode 100644 index 000000000..e69de29bb diff --git a/internal/persistence/sql/migrations/sql/20220110200400000000_create-uuid-mapping-table.mysql.up.sql b/internal/persistence/sql/migrations/sql/20220110200400000000_create-uuid-mapping-table.mysql.up.sql new file mode 100644 index 000000000..0d707ec15 --- /dev/null +++ b/internal/persistence/sql/migrations/sql/20220110200400000000_create-uuid-mapping-table.mysql.up.sql @@ -0,0 +1,10 @@ +CREATE TABLE keto_uuid_mappings +( + id VARCHAR(64) NOT NULL, + string_representation VARCHAR(64) NOT NULL CHECK (string_representation <> ''), + + PRIMARY KEY (id), + + -- enforce uniqueness + CONSTRAINT chk_keto_uuid_map_uniq UNIQUE (id, string_representation) +); \ No newline at end of file diff --git a/internal/persistence/sql/migrations/sql/20220110200400000000_create-uuid-mapping-table.postgres.down.sql b/internal/persistence/sql/migrations/sql/20220110200400000000_create-uuid-mapping-table.postgres.down.sql new file mode 100644 index 000000000..e69de29bb diff --git a/internal/persistence/sql/migrations/sql/20220110200400000000_create-uuid-mapping-table.postgres.up.sql b/internal/persistence/sql/migrations/sql/20220110200400000000_create-uuid-mapping-table.postgres.up.sql new file mode 100644 index 000000000..48641a7a7 --- /dev/null +++ b/internal/persistence/sql/migrations/sql/20220110200400000000_create-uuid-mapping-table.postgres.up.sql @@ -0,0 +1,10 @@ +CREATE TABLE keto_uuid_mappings +( + id UUID NOT NULL, + string_representation VARCHAR(64) NOT NULL CHECK (string_representation <> ''), + + PRIMARY KEY (id), + + -- enforce uniqueness + CONSTRAINT chk_keto_uuid_map_uniq UNIQUE (id, string_representation) +); \ No newline at end of file diff --git a/internal/persistence/sql/migrations/sql/20220110200400000000_create-uuid-mapping-table.sqlite3.down.sql b/internal/persistence/sql/migrations/sql/20220110200400000000_create-uuid-mapping-table.sqlite3.down.sql new file mode 100644 index 000000000..e69de29bb diff --git a/internal/persistence/sql/migrations/sql/20220110200400000000_create-uuid-mapping-table.sqlite3.up.sql b/internal/persistence/sql/migrations/sql/20220110200400000000_create-uuid-mapping-table.sqlite3.up.sql new file mode 100644 index 000000000..48641a7a7 --- /dev/null +++ b/internal/persistence/sql/migrations/sql/20220110200400000000_create-uuid-mapping-table.sqlite3.up.sql @@ -0,0 +1,10 @@ +CREATE TABLE keto_uuid_mappings +( + id UUID NOT NULL, + string_representation VARCHAR(64) NOT NULL CHECK (string_representation <> ''), + + PRIMARY KEY (id), + + -- enforce uniqueness + CONSTRAINT chk_keto_uuid_map_uniq UNIQUE (id, string_representation) +); \ No newline at end of file diff --git a/internal/persistence/sql/migrations/sql/20220110200400000001_create-uuid-mapping-table.cockroach.down.sql b/internal/persistence/sql/migrations/sql/20220110200400000001_create-uuid-mapping-table.cockroach.down.sql new file mode 100644 index 000000000..1a0e9eb51 --- /dev/null +++ b/internal/persistence/sql/migrations/sql/20220110200400000001_create-uuid-mapping-table.cockroach.down.sql @@ -0,0 +1 @@ +DROP TABLE keto_uuid_mappings; \ No newline at end of file diff --git a/internal/persistence/sql/migrations/sql/20220110200400000001_create-uuid-mapping-table.cockroach.up.sql b/internal/persistence/sql/migrations/sql/20220110200400000001_create-uuid-mapping-table.cockroach.up.sql new file mode 100644 index 000000000..e69de29bb diff --git a/internal/persistence/sql/migrations/sql/20220110200400000001_create-uuid-mapping-table.mysql.down.sql b/internal/persistence/sql/migrations/sql/20220110200400000001_create-uuid-mapping-table.mysql.down.sql new file mode 100644 index 000000000..1a0e9eb51 --- /dev/null +++ b/internal/persistence/sql/migrations/sql/20220110200400000001_create-uuid-mapping-table.mysql.down.sql @@ -0,0 +1 @@ +DROP TABLE keto_uuid_mappings; \ No newline at end of file diff --git a/internal/persistence/sql/migrations/sql/20220110200400000001_create-uuid-mapping-table.mysql.up.sql b/internal/persistence/sql/migrations/sql/20220110200400000001_create-uuid-mapping-table.mysql.up.sql new file mode 100644 index 000000000..e69de29bb diff --git a/internal/persistence/sql/migrations/sql/20220110200400000001_create-uuid-mapping-table.postgres.down.sql b/internal/persistence/sql/migrations/sql/20220110200400000001_create-uuid-mapping-table.postgres.down.sql new file mode 100644 index 000000000..1a0e9eb51 --- /dev/null +++ b/internal/persistence/sql/migrations/sql/20220110200400000001_create-uuid-mapping-table.postgres.down.sql @@ -0,0 +1 @@ +DROP TABLE keto_uuid_mappings; \ No newline at end of file diff --git a/internal/persistence/sql/migrations/sql/20220110200400000001_create-uuid-mapping-table.postgres.up.sql b/internal/persistence/sql/migrations/sql/20220110200400000001_create-uuid-mapping-table.postgres.up.sql new file mode 100644 index 000000000..e69de29bb diff --git a/internal/persistence/sql/migrations/sql/20220110200400000001_create-uuid-mapping-table.sqlite3.down.sql b/internal/persistence/sql/migrations/sql/20220110200400000001_create-uuid-mapping-table.sqlite3.down.sql new file mode 100644 index 000000000..1a0e9eb51 --- /dev/null +++ b/internal/persistence/sql/migrations/sql/20220110200400000001_create-uuid-mapping-table.sqlite3.down.sql @@ -0,0 +1 @@ +DROP TABLE keto_uuid_mappings; \ No newline at end of file diff --git a/internal/persistence/sql/migrations/sql/20220110200400000001_create-uuid-mapping-table.sqlite3.up.sql b/internal/persistence/sql/migrations/sql/20220110200400000001_create-uuid-mapping-table.sqlite3.up.sql new file mode 100644 index 000000000..e69de29bb diff --git a/internal/persistence/sql/migrations/templates/20220110200400_create-uuid-mapping-table.down.sql b/internal/persistence/sql/migrations/templates/20220110200400_create-uuid-mapping-table.down.sql new file mode 100644 index 000000000..09ae2c1f8 --- /dev/null +++ b/internal/persistence/sql/migrations/templates/20220110200400_create-uuid-mapping-table.down.sql @@ -0,0 +1 @@ +DROP TABLE keto_uuid_mappings; diff --git a/internal/persistence/sql/migrations/templates/20220110200400_create-uuid-mapping-table.mysql.up.sql b/internal/persistence/sql/migrations/templates/20220110200400_create-uuid-mapping-table.mysql.up.sql new file mode 100644 index 000000000..0d707ec15 --- /dev/null +++ b/internal/persistence/sql/migrations/templates/20220110200400_create-uuid-mapping-table.mysql.up.sql @@ -0,0 +1,10 @@ +CREATE TABLE keto_uuid_mappings +( + id VARCHAR(64) NOT NULL, + string_representation VARCHAR(64) NOT NULL CHECK (string_representation <> ''), + + PRIMARY KEY (id), + + -- enforce uniqueness + CONSTRAINT chk_keto_uuid_map_uniq UNIQUE (id, string_representation) +); \ No newline at end of file diff --git a/internal/persistence/sql/migrations/templates/20220110200400_create-uuid-mapping-table.up.sql b/internal/persistence/sql/migrations/templates/20220110200400_create-uuid-mapping-table.up.sql new file mode 100644 index 000000000..48641a7a7 --- /dev/null +++ b/internal/persistence/sql/migrations/templates/20220110200400_create-uuid-mapping-table.up.sql @@ -0,0 +1,10 @@ +CREATE TABLE keto_uuid_mappings +( + id UUID NOT NULL, + string_representation VARCHAR(64) NOT NULL CHECK (string_representation <> ''), + + PRIMARY KEY (id), + + -- enforce uniqueness + CONSTRAINT chk_keto_uuid_map_uniq UNIQUE (id, string_representation) +); \ No newline at end of file diff --git a/internal/persistence/sql/uuid_mapping.go b/internal/persistence/sql/uuid_mapping.go new file mode 100644 index 000000000..069ea3f7b --- /dev/null +++ b/internal/persistence/sql/uuid_mapping.go @@ -0,0 +1,45 @@ +package sql + +import ( + "context" + + "github.com/gofrs/uuid" + "github.com/ory/x/sqlcon" +) + +type ( + UUIDMapping struct { + ID uuid.UUID `db:"id"` + StringRepresentation string `db:"string_representation"` + } + UUIDMappings []*UUIDMapping +) + +func (UUIDMappings) TableName() string { + return "keto_uuid_mappings" +} + +func (UUIDMapping) TableName() string { + return "keto_uuid_mappings" +} + +func (p *Persister) AddUUIDMapping(ctx context.Context, id uuid.UUID, representation string) (error) { + m := &UUIDMapping{ + ID: id, + StringRepresentation: representation, + } + p.d.Logger().Trace("adding UUID mapping") + + return sqlcon.HandleError(p.Connection(ctx).Create(m)) +} + +func (p *Persister) LookupUUID(ctx context.Context, id uuid.UUID) (rep string, err error) { + p.d.Logger().Trace("looking up UUID") + + m := &UUIDMapping{} + if err := sqlcon.HandleError(p.Connection(ctx).Find(m, id)); err != nil { + return "", err + } + + return m.StringRepresentation, nil +} diff --git a/internal/persistence/sql/uuid_mapping_test.go b/internal/persistence/sql/uuid_mapping_test.go new file mode 100644 index 000000000..4019225df --- /dev/null +++ b/internal/persistence/sql/uuid_mapping_test.go @@ -0,0 +1,52 @@ +package sql_test + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/ory/keto/internal/driver" + "github.com/ory/keto/internal/persistence/sql" + "github.com/ory/keto/internal/x/dbx" +) + +func TestUUIDMapping(t *testing.T) { + for _, dsn := range dbx.GetDSNs(t, false) { + t.Run("dsn="+dsn.Name, func(t *testing.T) { + reg := driver.NewTestRegistry(t, dsn) + c, err := reg.PopConnection() + require.NoError(t, err) + + for _, tc := range []struct { + desc string + mappings interface{} + shouldErr bool + }{{ + desc: "empty should fail on constraint", + mappings: &sql.UUIDMapping{}, + shouldErr: true, + }, { + desc: "single with string rep should succeed", + mappings: &sql.UUIDMapping{StringRepresentation: "foo"}, + shouldErr: false, + }, { + desc: "two with same rep should fail on constraint", + mappings: sql.UUIDMappings{ + &sql.UUIDMapping{StringRepresentation: "bar"}, + &sql.UUIDMapping{StringRepresentation: "bar"}, + }, + shouldErr: true, + }} { + t.Run("case="+tc.desc, func(t *testing.T) { + err = c.Create(tc.mappings) + if tc.shouldErr { + assert.Error(t, err) + } else { + assert.NoError(t, err) + } + }) + } + }) + } +} diff --git a/internal/uuidmapping/definitions.go b/internal/uuidmapping/definitions.go new file mode 100644 index 000000000..d82356b53 --- /dev/null +++ b/internal/uuidmapping/definitions.go @@ -0,0 +1,17 @@ +package uuidmapping + +import ( + "context" + + "github.com/gofrs/uuid" +) + +type ( + ManagerProvider interface { + UUIDMappingManager() Manager + } + Manager interface { + AddUUIDMapping(ctx context.Context, id uuid.UUID, representation string) (error) + LookupUUID(ctx context.Context, id uuid.UUID) (rep string, err error) + } +)