Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: change primary key of verifiable and recovery addresses #4138

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 33 additions & 29 deletions driver/registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,45 +7,39 @@
"context"
"io/fs"

"github.com/ory/kratos/selfservice/sessiontokenexchange"
"github.com/ory/x/contextx"
"github.com/ory/x/jsonnetsecure"
"github.com/ory/x/otelx"
prometheus "github.com/ory/x/prometheusx"

"github.com/gorilla/sessions"
"github.com/pkg/errors"

"github.com/ory/nosurf"

"github.com/ory/x/logrusx"

"github.com/ory/kratos/cipher"
"github.com/ory/kratos/continuity"
"github.com/ory/kratos/courier"
"github.com/ory/kratos/driver/config"
"github.com/ory/kratos/hash"
"github.com/ory/kratos/identity"
"github.com/ory/kratos/persistence"
"github.com/ory/kratos/schema"
"github.com/ory/kratos/selfservice/errorx"
"github.com/ory/kratos/selfservice/flow/login"
"github.com/ory/kratos/selfservice/flow/logout"
"github.com/ory/kratos/selfservice/flow/recovery"
"github.com/ory/kratos/selfservice/flow/registration"
"github.com/ory/kratos/selfservice/flow/settings"
"github.com/ory/kratos/selfservice/flow/verification"
"github.com/ory/kratos/selfservice/sessiontokenexchange"
"github.com/ory/kratos/selfservice/strategy/code"
"github.com/ory/kratos/selfservice/strategy/link"

"github.com/ory/x/healthx"

"github.com/ory/kratos/persistence"
"github.com/ory/kratos/selfservice/flow/login"
"github.com/ory/kratos/selfservice/flow/logout"
"github.com/ory/kratos/selfservice/flow/registration"

"github.com/ory/kratos/x"

"github.com/ory/x/dbal"

"github.com/ory/kratos/driver/config"
"github.com/ory/kratos/identity"
"github.com/ory/kratos/selfservice/errorx"
password2 "github.com/ory/kratos/selfservice/strategy/password"
"github.com/ory/kratos/session"
"github.com/ory/kratos/x"
"github.com/ory/nosurf"
"github.com/ory/x/contextx"
"github.com/ory/x/dbal"
"github.com/ory/x/healthx"
"github.com/ory/x/jsonnetsecure"
"github.com/ory/x/logrusx"
"github.com/ory/x/otelx"
"github.com/ory/x/popx"
prometheus "github.com/ory/x/prometheusx"
)

type Registry interface {
Expand Down Expand Up @@ -85,6 +79,8 @@
continuity.ManagementProvider
continuity.PersistenceProvider

cipher.Provider

courier.Provider

persistence.Provider
Expand Down Expand Up @@ -186,10 +182,12 @@
replaceIdentitySchemaProvider func(Registry) schema.IdentitySchemaProvider
inspect func(Registry) error
extraMigrations []fs.FS
replacementStrategies []NewStrategy
extraHooks map[string]func(config.SelfServiceHook) any
disableMigrationLogging bool
jsonnetPool jsonnetsecure.Pool
extraGoMigrations popx.Migrations

replacementStrategies []NewStrategy
extraHooks map[string]func(config.SelfServiceHook) any
disableMigrationLogging bool
jsonnetPool jsonnetsecure.Pool
}

type RegistryOption func(*options)
Expand Down Expand Up @@ -251,6 +249,12 @@
}
}

func WithExtraGoMigrations(m ...popx.Migration) RegistryOption {
return func(o *options) {
o.extraGoMigrations = append(o.extraGoMigrations, m...)

Check warning on line 254 in driver/registry.go

View check run for this annotation

Codecov / codecov/patch

driver/registry.go#L252-L254

Added lines #L252 - L254 were not covered by tests
}
}

func WithDisabledMigrationLogging() RegistryOption {
return func(o *options) {
o.disableMigrationLogging = true
Expand Down
5 changes: 4 additions & 1 deletion driver/registry_default.go
Original file line number Diff line number Diff line change
Expand Up @@ -672,7 +672,10 @@ func (m *RegistryDefault) Init(ctx context.Context, ctxer contextx.Contextualize
m.Logger().WithError(err).Warnf("Unable to open database, retrying.")
return errors.WithStack(err)
}
p, err := sql.NewPersister(ctx, m, c, sql.WithExtraMigrations(o.extraMigrations...), sql.WithDisabledLogging(o.disableMigrationLogging))
p, err := sql.NewPersister(ctx, m, c,
sql.WithExtraMigrations(o.extraMigrations...),
sql.WithExtraGoMigrations(o.extraGoMigrations...),
sql.WithDisabledLogging(o.disableMigrationLogging))
if err != nil {
m.Logger().WithError(err).Warnf("Unable to initialize persister, retrying.")
return err
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ require (
github.com/jmoiron/sqlx v1.4.0
github.com/julienschmidt/httprouter v1.3.0
github.com/knadh/koanf/parsers/json v0.1.0
github.com/laher/mergefs v0.1.2-0.20230223191438-d16611b2f4e7
github.com/laher/mergefs v0.1.2-0.20230223191438-d16611b2f4e7 // indirect
github.com/lestrrat-go/jwx/v2 v2.1.1
github.com/luna-duclos/instrumentedsql v1.1.3
github.com/mailhog/MailHog v1.0.1
Expand Down
1 change: 0 additions & 1 deletion go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -551,7 +551,6 @@ github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/markbates/pkger v0.17.1 h1:/MKEtWqtc0mZvu9OinB9UzVN9iYCwLWuyUv4Bw+PCno=
github.com/markbates/pkger v0.17.1/go.mod h1:0JoVlrol20BSywW79rN3kdFFsE5xYM+rSCQDXbLhiuI=
github.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE=
github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU=
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
Expand Down
29 changes: 14 additions & 15 deletions persistence/sql/migratest/migration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,30 +8,21 @@ import (
"encoding/json"
"os"
"path/filepath"
"slices"
"sync"
"testing"
"time"

"github.com/ory/x/pagination/keysetpagination"
"github.com/ory/x/servicelocatorx"

"github.com/ory/kratos/identity"

"github.com/bradleyjkemp/cupaloy/v2"
"github.com/stretchr/testify/assert"

"github.com/ory/x/dbal"

"github.com/ory/kratos/x/xsql"

"github.com/ory/x/migratest"

"github.com/gobuffalo/pop/v6"
"github.com/sirupsen/logrus"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"github.com/ory/kratos/driver"
"github.com/ory/kratos/driver/config"
"github.com/ory/kratos/identity"
"github.com/ory/kratos/persistence/sql/migrations/gomigrations"
"github.com/ory/kratos/selfservice/flow/login"
"github.com/ory/kratos/selfservice/flow/recovery"
"github.com/ory/kratos/selfservice/flow/registration"
Expand All @@ -41,9 +32,14 @@ import (
"github.com/ory/kratos/selfservice/strategy/link"
"github.com/ory/kratos/session"
"github.com/ory/kratos/x"
"github.com/ory/kratos/x/xsql"
"github.com/ory/x/configx"
"github.com/ory/x/dbal"
"github.com/ory/x/logrusx"
"github.com/ory/x/migratest"
"github.com/ory/x/pagination/keysetpagination"
"github.com/ory/x/popx"
"github.com/ory/x/servicelocatorx"
"github.com/ory/x/sqlcon"
"github.com/ory/x/sqlcon/dockertest"
)
Expand Down Expand Up @@ -87,15 +83,15 @@ func TestMigrations_Postgres(t *testing.T) {
t.Skip("skipping testing in short mode")
}
t.Parallel()
testDatabase(t, "postgres", dockertest.ConnectPop(t, dockertest.RunTestPostgreSQLWithVersion(t, "11.8")))
testDatabase(t, "postgres", dockertest.ConnectPop(t, dockertest.RunTestPostgreSQLWithVersion(t, "14")))
}

func TestMigrations_Mysql(t *testing.T) {
if testing.Short() {
t.Skip("skipping testing in short mode")
}
t.Parallel()
testDatabase(t, "mysql", dockertest.ConnectPop(t, dockertest.RunTestMySQLWithVersion(t, "8.0.34")))
testDatabase(t, "mysql", dockertest.ConnectPop(t, dockertest.RunTestMySQLWithVersion(t, "8.0")))
}

func TestMigrations_Cockroach(t *testing.T) {
Expand Down Expand Up @@ -134,6 +130,9 @@ func testDatabase(t *testing.T, db string, c *pop.Connection) {
os.DirFS("../migrations/sql"),
popx.NewMigrator(c, l, nil, 1*time.Minute),
popx.WithTestdata(t, os.DirFS("./testdata")),
popx.WithGoMigrations(slices.Concat(
gomigrations.ChangeAddressesPK,
)),
)
require.NoError(t, err)
tm.DumpMigrations = true
Expand Down
71 changes: 71 additions & 0 deletions persistence/sql/migrations/gomigrations/identity_pk.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// Copyright © 2024 Ory Corp
// SPDX-License-Identifier: Apache-2.0

package gomigrations

import (
"fmt"
"path/filepath"
"runtime"

"github.com/gobuffalo/pop/v6"
"github.com/pkg/errors"

"github.com/ory/x/popx"
)

func path() string {
_, file, line, _ := runtime.Caller(1)
return fmt.Sprintf("%s:%d", filepath.Base(file), line)
}

var ChangeAddressesPK = []popx.Migration{
{
Version: "20241001000000000000",
Path: path(),
Name: "Change primary key for identity_verifiable_addresses",
Direction: "up",
Type: "go",
DBType: "cockroach",
RunnerNoTx: func(m popx.Migration, c *pop.Connection) error {
_, err := c.Store.Exec("ALTER TABLE identity_verifiable_addresses ALTER PRIMARY KEY USING COLUMNS (identity_id,id)")
aeneasr marked this conversation as resolved.
Show resolved Hide resolved
return errors.WithStack(err)
},
},
{
Version: "20241001000000000000",
Path: path(),
Name: "Revert primary key for identity_verifiable_addresses",
Direction: "down",
Type: "go",
DBType: "cockroach",
RunnerNoTx: func(m popx.Migration, c *pop.Connection) error {
_, err := c.Store.Exec("ALTER TABLE identity_verifiable_addresses ALTER PRIMARY KEY USING COLUMNS (id)")
return errors.WithStack(err)
},
},
{
Version: "20241001000000000001",
Path: path(),
Name: "Change primary key for identity_recovery_addresses",
Direction: "up",
Type: "go",
DBType: "cockroach",
RunnerNoTx: func(m popx.Migration, c *pop.Connection) error {
_, err := c.Store.Exec("ALTER TABLE identity_recovery_addresses ALTER PRIMARY KEY USING COLUMNS (identity_id,id)")
return errors.WithStack(err)
},
},
{
Version: "20241001000000000001",
Path: path(),
Name: "Revert primary key for identity_recovery_addresses",
Direction: "down",
Type: "go",
DBType: "cockroach",
RunnerNoTx: func(m popx.Migration, c *pop.Connection) error {
_, err := c.Store.Exec("ALTER TABLE identity_recovery_addresses ALTER PRIMARY KEY USING COLUMNS (id)")
return errors.WithStack(err)
},
},
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
ALTER TABLE identity_verifiable_addresses
DROP FOREIGN KEY identity_verifiable_addresses_ibfk_1;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess we need this because the primary key won't be deleted if there is still a foreign key using it, correct?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

exactly.


ALTER TABLE identity_verifiable_addresses
DROP PRIMARY KEY,
ADD PRIMARY KEY (id),
ADD CONSTRAINT identity_verifiable_addresses_ibfk_1 FOREIGN KEY (identity_id) REFERENCES identities(id) ON DELETE CASCADE;
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
ALTER TABLE identity_verifiable_addresses
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't this missing a unique index for id?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure, can you explain why we need that unique index? If there is any FK to this table, that should always use the PK and not just the id column.

DROP PRIMARY KEY,
ADD PRIMARY KEY (identity_id, id),
ADD UNIQUE KEY identity_verifiable_addresses_id_uq_idx (id);
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
ALTER TABLE identity_verifiable_addresses
DROP CONSTRAINT identity_verifiable_addresses_pkey,
ADD PRIMARY KEY (id);
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
CREATE UNIQUE INDEX identity_verifiable_addresses_id_uq_idx ON identity_verifiable_addresses (id);

ALTER TABLE identity_verification_codes
DROP CONSTRAINT identity_verification_codes_identity_verifiable_addresses_id_fk;

ALTER TABLE identity_verification_tokens
DROP CONSTRAINT identity_verification_tokens_identity_verifiable_address_i_fkey;
Comment on lines +3 to +7
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do these need to be dropped and recreated?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes:

ERROR: cannot drop constraint identity_recovery_addresses_pkey on table identity_recovery_addresses because other objects depend on it (SQLSTATE 2BP01)


ALTER TABLE identity_verifiable_addresses
DROP CONSTRAINT identity_verifiable_addresses_pkey,
ADD PRIMARY KEY (identity_id, id);

ALTER TABLE identity_verification_codes
ADD CONSTRAINT identity_verification_codes_identity_verifiable_addresses_id_fk FOREIGN KEY (identity_verifiable_address_id) REFERENCES identity_verifiable_addresses(id) ON DELETE CASCADE;

ALTER TABLE identity_verification_tokens
ADD CONSTRAINT identity_verification_tokens_identity_verifiable_address_i_fkey FOREIGN KEY (identity_verifiable_address_id) REFERENCES identity_verifiable_addresses(id) ON DELETE CASCADE;
Comment on lines +13 to +17
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As the PK changed, the foreign key should also change.

Suggested change
ALTER TABLE identity_verification_codes
ADD CONSTRAINT identity_verification_codes_identity_verifiable_addresses_id_fk FOREIGN KEY (identity_verifiable_address_id) REFERENCES identity_verifiable_addresses(id) ON DELETE CASCADE;
ALTER TABLE identity_verification_tokens
ADD CONSTRAINT identity_verification_tokens_identity_verifiable_address_i_fkey FOREIGN KEY (identity_verifiable_address_id) REFERENCES identity_verifiable_addresses(id) ON DELETE CASCADE;
ALTER TABLE identity_verification_codes
ADD CONSTRAINT identity_verification_codes_identity_verifiable_addresses_id_fk FOREIGN KEY (identity_verifiable_address_id) REFERENCES identity_verifiable_addresses(identity_id, id) ON DELETE CASCADE;
ALTER TABLE identity_verification_tokens
ADD CONSTRAINT identity_verification_tokens_identity_verifiable_address_i_fkey FOREIGN KEY (identity_verifiable_address_id) REFERENCES identity_verifiable_addresses(identity_id, id) ON DELETE CASCADE;

Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
CREATE TABLE IF NOT EXISTS "_identity_verifiable_addresses_tmp" (
"id" TEXT PRIMARY KEY,
"status" TEXT NOT NULL,
"via" TEXT NOT NULL,
"verified" bool NOT NULL,
"value" TEXT NOT NULL,
"verified_at" DATETIME,
"identity_id" TEXT NOT NULL,
"created_at" DATETIME NOT NULL,
"updated_at" DATETIME NOT NULL,
"nid" TEXT NOT NULL,
FOREIGN KEY ("identity_id") REFERENCES "identities" ("id") ON UPDATE RESTRICT ON DELETE CASCADE,
FOREIGN KEY ("nid") REFERENCES "networks" ("id") ON UPDATE RESTRICT ON DELETE CASCADE
);

INSERT INTO "_identity_verifiable_addresses_tmp"
("id", "status", "via", "verified", "value", "verified_at", "identity_id", "created_at", "updated_at", "nid")
SELECT
"id", "status", "via", "verified", "value", "verified_at", "identity_id", "created_at", "updated_at", "nid"
FROM "identity_verifiable_addresses";

DROP TABLE "identity_verifiable_addresses";
ALTER TABLE "_identity_verifiable_addresses_tmp" RENAME TO "identity_verifiable_addresses";

CREATE UNIQUE INDEX IF NOT EXISTS "identity_verifiable_addresses_status_via_uq_idx" ON "identity_verifiable_addresses" (nid, via, value);
CREATE INDEX IF NOT EXISTS "identity_verifiable_addresses_status_via_idx" ON "identity_verifiable_addresses" (nid, via, value);
CREATE INDEX IF NOT EXISTS identity_recovery_addresses_nid_id_idx ON identity_recovery_addresses (nid, id);
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
CREATE TABLE IF NOT EXISTS "_identity_verifiable_addresses_tmp" (
"id" TEXT NOT NULL,
"status" TEXT NOT NULL,
"via" TEXT NOT NULL,
"verified" bool NOT NULL,
"value" TEXT NOT NULL,
"verified_at" DATETIME,
"identity_id" TEXT NOT NULL,
"created_at" DATETIME NOT NULL,
"updated_at" DATETIME NOT NULL,
"nid" TEXT NOT NULL,
PRIMARY KEY ("identity_id","id"),
FOREIGN KEY ("identity_id") REFERENCES "identities" ("id") ON UPDATE RESTRICT ON DELETE CASCADE,
FOREIGN KEY ("nid") REFERENCES "networks" ("id") ON UPDATE RESTRICT ON DELETE CASCADE
);

INSERT INTO "_identity_verifiable_addresses_tmp"
("id", "status", "via", "verified", "value", "verified_at", "identity_id", "created_at", "updated_at", "nid")
SELECT
"id", "status", "via", "verified", "value", "verified_at", "identity_id", "created_at", "updated_at", "nid"
FROM "identity_verifiable_addresses";

DROP TABLE "identity_verifiable_addresses";
ALTER TABLE "_identity_verifiable_addresses_tmp" RENAME TO "identity_verifiable_addresses";

CREATE UNIQUE INDEX "identity_verifiable_addresses_status_via_uq_idx" ON "identity_verifiable_addresses" (nid, via, value);
CREATE UNIQUE INDEX "identity_verifiable_addresses_id_uq_idx" ON "identity_verifiable_addresses" (id);
CREATE INDEX "identity_verifiable_addresses_status_via_idx" ON "identity_verifiable_addresses" (nid, via, value);
CREATE INDEX identity_verifiable_addresses_nid_id_idx ON identity_recovery_addresses (nid, id);
CREATE INDEX identity_verifiable_addresses_id_nid_idx ON identity_recovery_addresses (id, nid);
Loading
Loading