Skip to content

Commit

Permalink
feat: map subjects/objects using UUIDv5
Browse files Browse the repository at this point in the history
  • Loading branch information
hperl committed Feb 3, 2022
1 parent 7438c28 commit 4dcdfda
Show file tree
Hide file tree
Showing 15 changed files with 100 additions and 90 deletions.
49 changes: 49 additions & 0 deletions cmd/migrate/migrate_uuid_mapping.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package migrate

import (
"fmt"

"github.com/ory/x/cmdx"
"github.com/ory/x/flagx"
"github.com/pkg/errors"
"github.com/spf13/cobra"

"github.com/ory/keto/internal/driver"
"github.com/ory/keto/internal/persistence"
"github.com/ory/keto/internal/persistence/sql/migrations"
)

func newMigrateUUIDMappingCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "uuid-mapping",
Short: "Migrate the non-UUID subject and object names to UUIDs.",
Long: `Migrate the non-UUID subject and object names to UUIDs.
This step only has to be executed once.
Please ensure that you have a backup in case something goes wrong!"`,
Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, _ []string) error {
reg, err := driver.NewDefaultRegistry(cmd.Context(), cmd.Flags(), false)
if errors.Is(err, persistence.ErrNetworkMigrationsMissing) {
_, _ = fmt.Fprintln(cmd.ErrOrStderr(),
"Migrations were not applied yet, please apply them first using `keto migrate up`.")
return cmdx.FailSilently(cmd)
} else if err != nil {
return err
}

if !flagx.MustGetBool(cmd, FlagYes) &&
!cmdx.AskForConfirmation(
"Are you sure you want to migrate the subject and object names to UUIDs?",
cmd.InOrStdin(), cmd.OutOrStdout()) {
_, _ = fmt.Fprintln(cmd.OutOrStdout(), "OK, aborting.")
return nil
}

migrator := migrations.NewToUUIDMappingMigrator(reg)
return migrator.MigrateUUIDMappings(cmd.Context())
},
}
RegisterYesFlag(cmd.Flags())

return cmd
}
1 change: 1 addition & 0 deletions cmd/migrate/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ func newMigrateCmd() *cobra.Command {
newStatusCmd(),
newUpCmd(),
newDownCmd(),
newMigrateUUIDMappingCmd(),
)
return cmd
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
CREATE TABLE keto_uuid_mappings
(
id UUID NOT NULL,
string_representation VARCHAR(255) NOT NULL CHECK (string_representation <> ''),
id UUID NOT NULL,
string_representation TEXT NOT NULL CHECK (string_representation <> ''),

PRIMARY KEY (id),

-- enforce uniqueness
CONSTRAINT chk_keto_uuid_map_uniq UNIQUE (id, string_representation)
PRIMARY KEY (id)
);
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
CREATE TABLE keto_uuid_mappings
(
id VARCHAR(64) NOT NULL,
string_representation VARCHAR(255) NOT NULL CHECK (string_representation <> ''),
id VARCHAR(64) NOT NULL,
string_representation TEXT NOT NULL CHECK (string_representation <> ''),

PRIMARY KEY (id),

-- enforce uniqueness
CONSTRAINT chk_keto_uuid_map_uniq UNIQUE (id, string_representation)
PRIMARY KEY (id)
);
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
CREATE TABLE keto_uuid_mappings
(
id UUID NOT NULL,
string_representation VARCHAR(255) NOT NULL CHECK (string_representation <> ''),
id UUID NOT NULL,
string_representation TEXT NOT NULL CHECK (string_representation <> ''),

PRIMARY KEY (id),

-- enforce uniqueness
CONSTRAINT chk_keto_uuid_map_uniq UNIQUE (id, string_representation)
PRIMARY KEY (id)
);
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
CREATE TABLE keto_uuid_mappings
(
id UUID NOT NULL,
string_representation VARCHAR(255) NOT NULL CHECK (string_representation <> ''),
id UUID NOT NULL,
string_representation TEXT NOT NULL CHECK (string_representation <> ''),

PRIMARY KEY (id),

-- enforce uniqueness
CONSTRAINT chk_keto_uuid_map_uniq UNIQUE (id, string_representation)
PRIMARY KEY (id)
);
Original file line number Diff line number Diff line change
@@ -1,2 +0,0 @@

CREATE INDEX keto_uuid_mappings_subject_ids_idx ON keto_uuid_mappings (string_representation);
Original file line number Diff line number Diff line change
@@ -1,2 +0,0 @@

CREATE INDEX keto_uuid_mappings_subject_ids_idx ON keto_uuid_mappings (string_representation);
Original file line number Diff line number Diff line change
@@ -1,2 +0,0 @@

CREATE INDEX keto_uuid_mappings_subject_ids_idx ON keto_uuid_mappings (string_representation);
Original file line number Diff line number Diff line change
@@ -1,2 +0,0 @@

CREATE INDEX keto_uuid_mappings_subject_ids_idx ON keto_uuid_mappings (string_representation);
Original file line number Diff line number Diff line change
@@ -1,12 +1,7 @@
CREATE TABLE keto_uuid_mappings
(
id VARCHAR(64) NOT NULL,
string_representation VARCHAR(255) NOT NULL CHECK (string_representation <> ''),
id VARCHAR(64) NOT NULL,
string_representation TEXT NOT NULL CHECK (string_representation <> ''),

PRIMARY KEY (id),

-- enforce uniqueness
CONSTRAINT chk_keto_uuid_map_uniq UNIQUE (id, string_representation)
);

CREATE INDEX keto_uuid_mappings_subject_ids_idx ON keto_uuid_mappings (string_representation);
PRIMARY KEY (id)
);
Original file line number Diff line number Diff line change
@@ -1,12 +1,7 @@
CREATE TABLE keto_uuid_mappings
(
id UUID NOT NULL,
string_representation VARCHAR(255) NOT NULL CHECK (string_representation <> ''),
id UUID NOT NULL,
string_representation TEXT NOT NULL CHECK (string_representation <> ''),

PRIMARY KEY (id),

-- enforce uniqueness
CONSTRAINT chk_keto_uuid_map_uniq UNIQUE (id, string_representation)
);

CREATE INDEX keto_uuid_mappings_subject_ids_idx ON keto_uuid_mappings (string_representation);
PRIMARY KEY (id)
);
3 changes: 1 addition & 2 deletions internal/persistence/sql/migrations/uuid_mapping.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (

"github.com/gobuffalo/pop/v6"
"github.com/gofrs/uuid"

"github.com/ory/x/sqlcon"

"github.com/ory/keto/internal/persistence/sql"
Expand Down Expand Up @@ -96,7 +95,7 @@ func (m *toUUIDMappingMigrator) migrateObject(ctx context.Context, rt *sql.Relat
}

func (m *toUUIDMappingMigrator) addUUIDMapping(ctx context.Context, value string) (id string, err error) {
uid, err := m.d.Persister().MappedUUID(ctx, value)
uid, err := m.d.Persister().ToUUID(ctx, value)
return uid.String(), err
}

Expand Down
34 changes: 14 additions & 20 deletions internal/persistence/sql/uuid_mapping.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,26 @@ func (UUIDMapping) TableName() string {
return "keto_uuid_mappings"
}

func (p *Persister) AddUUIDMapping(ctx context.Context, id uuid.UUID, representation string) error {
func (p *Persister) ToUUID(ctx context.Context, text string) (uuid.UUID, error) {
id := uuid.NewV5(p.NetworkID(ctx), text)
p.d.Logger().Trace("adding UUID mapping")

m := &UUIDMapping{
ID: id,
StringRepresentation: representation,
StringRepresentation: text,
}

err := sqlcon.HandleError(p.Connection(ctx).Create(m))

// Ignore errors if the UUID already exists.
if errors.Is(err, sqlcon.ErrUniqueViolation) {
return id, nil
}
p.d.Logger().Trace("adding UUID mapping")

return sqlcon.HandleError(p.Connection(ctx).Create(m))
return id, err
}

func (p *Persister) LookupUUID(ctx context.Context, id uuid.UUID) (rep string, err error) {
func (p *Persister) FromUUID(ctx context.Context, id uuid.UUID) (rep string, err error) {
p.d.Logger().Trace("looking up UUID")

m := &UUIDMapping{}
Expand All @@ -44,18 +53,3 @@ func (p *Persister) LookupUUID(ctx context.Context, id uuid.UUID) (rep string, e

return m.StringRepresentation, nil
}

func (p *Persister) MappedUUID(ctx context.Context, representation string) (uuid.UUID, error) {
p.d.Logger().Trace("looking up mapped UUID")

m := &UUIDMapping{}
if err := sqlcon.HandleError(p.Connection(ctx).Where("string_representation = ?", representation).First(m)); err != nil {
if errors.Is(err, sqlcon.ErrNoRows) {
id := uuid.Must(uuid.NewV4())
return id, p.AddUUIDMapping(ctx, id, representation)
}
return uuid.Nil, err
}

return m.ID, nil
}
33 changes: 15 additions & 18 deletions internal/uuidmapping/definitions.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,37 +15,34 @@ type (
UUIDMappingManager() Manager
}
Manager interface {
// MappedUUID returns the mapped UUID for the given string
// representation. If the string representation is not mapped, a new
// UUID will be created automatically.
MappedUUID(ctx context.Context, representation string) (uuid.UUID, error)
// ToUUID returns the mapped UUID for the given string representation.
// If the string representation is not mapped, a new UUID will be
// created automatically.
ToUUID(ctx context.Context, representation string) (uuid.UUID, error)

// AddUUIDMapping adds a new mapping between a UUID and a string.
AddUUIDMapping(ctx context.Context, id uuid.UUID, representation string) error

// LookupUUID returns the string representation for the given UUID.
LookupUUID(ctx context.Context, id uuid.UUID) (rep string, err error)
// FromUUID returns the text representation for the given UUID.
FromUUID(ctx context.Context, id uuid.UUID) (text string, err error)
}
)

func ManagerTest(t *testing.T, m Manager) {
ctx := context.Background()

t.Run("case=add_lookup", func(t *testing.T) {
id := uuid.Must(uuid.NewV4())
t.Run("case=ToUUID_FromUUID", func(t *testing.T) {
rep1 := "foo"
require.NoError(t, m.AddUUIDMapping(ctx, id, rep1))
id, err := m.ToUUID(ctx, rep1)
require.NoError(t, err)

rep2, err := m.LookupUUID(ctx, id)
rep2, err := m.FromUUID(ctx, id)
assert.NoError(t, err)
assert.Equal(t, rep1, rep2)
})

t.Run("case=MappedUUID", func(t *testing.T) {
id1, err := m.MappedUUID(ctx, "string")
require.NoError(t, err)
id2, err := m.MappedUUID(ctx, "string")
require.NoError(t, err)
t.Run("case=FromUUID", func(t *testing.T) {
id1, err := m.ToUUID(ctx, "string")
assert.NoError(t, err)
id2, err := m.ToUUID(ctx, "string")
assert.NoError(t, err)
assert.Equal(t, id1, id2)
})
}

0 comments on commit 4dcdfda

Please sign in to comment.