Skip to content

Commit

Permalink
feat: Add mapping for encoding subject into UUIDs
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
hperl committed Jan 10, 2022
1 parent 7ee65b5 commit c3ad030
Show file tree
Hide file tree
Showing 26 changed files with 201 additions and 0 deletions.
2 changes: 2 additions & 0 deletions internal/driver/registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down Expand Up @@ -34,6 +35,7 @@ type (
x.WriterProvider

relationtuple.ManagerProvider
uuidmapping.ManagerProvider
expand.EngineProvider
check.EngineProvider
persistence.Migrator
Expand Down
8 changes: 8 additions & 0 deletions internal/driver/registry_default.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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")
Expand Down
2 changes: 2 additions & 0 deletions internal/persistence/definitions.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
10 changes: 10 additions & 0 deletions internal/persistence/sql/migrations/single_table_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"strconv"
"testing"

"github.com/gofrs/uuid"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

Expand Down Expand Up @@ -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)

Expand Down
Original file line number Diff line number Diff line change
@@ -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)
);
Original file line number Diff line number Diff line change
@@ -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)
);
Original file line number Diff line number Diff line change
@@ -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)
);
Original file line number Diff line number Diff line change
@@ -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)
);
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
DROP TABLE keto_uuid_mappings;
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
DROP TABLE keto_uuid_mappings;
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
DROP TABLE keto_uuid_mappings;
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
DROP TABLE keto_uuid_mappings;
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
DROP TABLE keto_uuid_mappings;
Original file line number Diff line number Diff line change
@@ -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)
);
Original file line number Diff line number Diff line change
@@ -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)
);
45 changes: 45 additions & 0 deletions internal/persistence/sql/uuid_mapping.go
Original file line number Diff line number Diff line change
@@ -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
}
52 changes: 52 additions & 0 deletions internal/persistence/sql/uuid_mapping_test.go
Original file line number Diff line number Diff line change
@@ -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)
}
})
}
})
}
}
17 changes: 17 additions & 0 deletions internal/uuidmapping/definitions.go
Original file line number Diff line number Diff line change
@@ -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)
}
)

0 comments on commit c3ad030

Please sign in to comment.