diff --git a/Makefile b/Makefile index 61e4284d3994..453c33c87103 100644 --- a/Makefile +++ b/Makefile @@ -164,7 +164,7 @@ authors: # updates the AUTHORS file # Formats the code .PHONY: format format: .bin/goimports .bin/ory node_modules - .bin/ory dev headers copyright --exclude=internal/httpclient --exclude=internal/client-go --exclude test/e2e/proxy/node_modules --exclude test/e2e/node_modules --exclude node_modules + .bin/ory dev headers copyright --exclude=internal/httpclient --exclude=internal/client-go --exclude test/e2e/proxy/node_modules --exclude test/e2e/node_modules --exclude node_modules --exclude x/slices goimports -w -local github.com/ory . npm exec -- prettier --write 'test/e2e/**/*{.ts,.js}' npm exec -- prettier --write '.github' diff --git a/driver/registry.go b/driver/registry.go index e87fdd076078..446fa8336cc0 100644 --- a/driver/registry.go +++ b/driver/registry.go @@ -7,45 +7,38 @@ import ( "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/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 { @@ -185,6 +178,7 @@ type options struct { replaceTracer func(*otelx.Tracer) *otelx.Tracer inspect func(Registry) error extraMigrations []fs.FS + extraGoMigrations popx.Migrations replacementStrategies []NewStrategy extraHooks map[string]func(config.SelfServiceHook) any disableMigrationLogging bool @@ -244,6 +238,12 @@ func WithExtraMigrations(m ...fs.FS) RegistryOption { } } +func WithExtraGoMigrations(m ...popx.Migration) RegistryOption { + return func(o *options) { + o.extraGoMigrations = append(o.extraGoMigrations, m...) + } +} + func WithDisabledMigrationLogging() RegistryOption { return func(o *options) { o.disableMigrationLogging = true diff --git a/driver/registry_default.go b/driver/registry_default.go index 1ab63c0af561..a1fe7abacf29 100644 --- a/driver/registry_default.go +++ b/driver/registry_default.go @@ -662,7 +662,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 diff --git a/go.mod b/go.mod index 04a6ea917423..df7b028ac2b3 100644 --- a/go.mod +++ b/go.mod @@ -59,7 +59,7 @@ require ( github.com/jteeuwen/go-bindata v3.0.7+incompatible 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 v1.2.29 // indirect github.com/luna-duclos/instrumentedsql v1.1.3 github.com/mailhog/MailHog v1.0.1 @@ -103,7 +103,7 @@ require ( golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa golang.org/x/net v0.21.0 golang.org/x/oauth2 v0.16.0 - golang.org/x/sync v0.5.0 + golang.org/x/sync v0.6.0 golang.org/x/text v0.14.0 golang.org/x/tools/cmd/cover v0.1.0-deprecated google.golang.org/grpc v1.59.0 @@ -310,7 +310,7 @@ require ( golang.org/x/mod v0.14.0 // indirect golang.org/x/sys v0.18.0 // indirect golang.org/x/term v0.18.0 // indirect - golang.org/x/tools v0.15.0 // indirect + golang.org/x/tools v0.17.0 // indirect golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect google.golang.org/appengine v1.6.8 // indirect google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17 // indirect diff --git a/go.sum b/go.sum index 7db827552f74..02307eb58a11 100644 --- a/go.sum +++ b/go.sum @@ -714,7 +714,6 @@ github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsI github.com/markbates/pkger v0.17.1 h1:/MKEtWqtc0mZvu9OinB9UzVN9iYCwLWuyUv4Bw+PCno= github.com/markbates/pkger v0.17.1/go.mod h1:0JoVlrol20BSywW79rN3kdFFsE5xYM+rSCQDXbLhiuI= github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0= -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.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= @@ -1238,8 +1237,8 @@ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE= -golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= +golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1426,8 +1425,8 @@ golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.15.0 h1:zdAyfUGbYmuVokhzVmghFl2ZJh5QhcfebBgmVPFYA+8= -golang.org/x/tools v0.15.0/go.mod h1:hpksKq4dtpQWS1uQ61JkdqWM3LscIS6Slf+VVkm+wQk= +golang.org/x/tools v0.17.0 h1:FvmRgNOcs3kOa+T20R1uhfP9F6HgG2mfxDv1vrx1Htc= +golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps= golang.org/x/tools/cmd/cover v0.1.0-deprecated h1:Rwy+mWYz6loAF+LnG1jHG/JWMHRMMC2/1XX3Ejkx9lA= golang.org/x/tools/cmd/cover v0.1.0-deprecated/go.mod h1:hMDiIvlpN1NoVgmjLjUJE9tMHyxHjFX7RuQ+rW12mSA= golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/identity/credentials.go b/identity/credentials.go index 3cc910c5a74e..89d8c65188d7 100644 --- a/identity/credentials.go +++ b/identity/credentials.go @@ -7,11 +7,16 @@ import ( "context" "database/sql" "reflect" + "sync" "time" + "github.com/gobuffalo/pop/v6" "github.com/gofrs/uuid" + "github.com/pkg/errors" + "go.opentelemetry.io/otel/trace" "github.com/ory/kratos/ui/node" + "github.com/ory/x/otelx" "github.com/ory/x/sqlxx" ) @@ -190,11 +195,54 @@ func (c Credentials) GetID() uuid.UUID { return c.ID } +func (c Credentials) GetNID() uuid.UUID { + return c.NID +} + +var ( + typeTable map[uuid.UUID]CredentialsType + typeErr error + typeOnce sync.Once +) + +func (c *Credentials) AfterFind(con *pop.Connection) error { + typeOnce.Do(func() { + span := trace.SpanFromContext(con.Context()) + ctx, span := span.TracerProvider().Tracer("").Start(con.Context(), "identity.Credentials.AfterFind") + con = con.WithContext(ctx) + defer otelx.End(span, &typeErr) + + var table []CredentialsTypeTable + if typeErr = con.All(&table); typeErr != nil { + return + } + typeTable = make(map[uuid.UUID]CredentialsType, len(table)) + for _, t := range table { + typeTable[t.ID] = t.Name + } + }) + if typeErr != nil { + return typeErr + } + + var ok bool + c.Type, ok = typeTable[c.IdentityCredentialTypeID] + if !ok { + return errors.New("could not find credentials type") + } + + return nil +} + +var _ pop.AfterFindable = (*Credentials)(nil) + type ( // swagger:ignore CredentialIdentifier struct { ID uuid.UUID `db:"id"` Identifier string `db:"identifier"` + // Identity is a helper struct field for gobuffalo.pop. + IdentityID uuid.UUID `json:"-" db:"identity_id"` // IdentityCredentialsID is a helper struct field for gobuffalo.pop. IdentityCredentialsID uuid.UUID `json:"-" db:"identity_credential_id"` // IdentityCredentialsTypeID is a helper struct field for gobuffalo.pop. diff --git a/identity/test/pool.go b/identity/test/pool.go index 450b5c1ea881..bbf5782e4404 100644 --- a/identity/test/pool.go +++ b/identity/test/pool.go @@ -1322,8 +1322,8 @@ func TestPool(ctx context.Context, conf *config.Config, p persistence.Persister, require.NoError(t, p.GetConnection(ctx).RawQuery("INSERT INTO identity_credentials (id, identity_id, nid, identity_credential_type_id, created_at, updated_at, config) VALUES (?, ?, ?, ?, ?, ?, '{}')", cid2, iid, nid2, m[0].ID, time.Now(), time.Now()).Exec()) ici1, ici2 := x.NewUUID(), x.NewUUID() - require.NoError(t, p.GetConnection(ctx).RawQuery("INSERT INTO identity_credential_identifiers (id, identity_credential_id, nid, identifier, created_at, updated_at, identity_credential_type_id) VALUES (?, ?, ?, ?, ?, ?, ?)", ici1, cid1, nid1, "nid1", time.Now(), time.Now(), m[0].ID).Exec()) - require.NoError(t, p.GetConnection(ctx).RawQuery("INSERT INTO identity_credential_identifiers (id, identity_credential_id, nid, identifier, created_at, updated_at, identity_credential_type_id) VALUES (?, ?, ?, ?, ?, ?, ?)", ici2, cid2, nid2, "nid2", time.Now(), time.Now(), m[0].ID).Exec()) + require.NoError(t, p.GetConnection(ctx).RawQuery("INSERT INTO identity_credential_identifiers (id, identity_id, identity_credential_id, nid, identifier, created_at, updated_at, identity_credential_type_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?)", ici1, iid, cid1, nid1, "nid1", time.Now(), time.Now(), m[0].ID).Exec()) + require.NoError(t, p.GetConnection(ctx).RawQuery("INSERT INTO identity_credential_identifiers (id, identity_id, identity_credential_id, nid, identifier, created_at, updated_at, identity_credential_type_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?)", ici2, iid, cid2, nid2, "nid2", time.Now(), time.Now(), m[0].ID).Exec()) _, err := p.GetIdentity(ctx, nid1, identity.ExpandNothing) require.ErrorIs(t, err, sqlcon.ErrNoRows) diff --git a/persistence/sql/identity/persister_identity.go b/persistence/sql/identity/persister_identity.go index cdbd46427d38..183be7e0bb4a 100644 --- a/persistence/sql/identity/persister_identity.go +++ b/persistence/sql/identity/persister_identity.go @@ -370,6 +370,7 @@ func (p *IdentityPersister) createIdentityCredentials(ctx context.Context, conn identifiers = append(identifiers, &identity.CredentialIdentifier{ Identifier: identifier, + IdentityID: cred.IdentityID, IdentityCredentialsID: cred.ID, IdentityCredentialsTypeID: ct.ID, NID: p.NetworkID(ctx), @@ -695,7 +696,7 @@ func QueryForCredentials(con *pop.Connection, where ...Where) (map[uuid.UUID](ma ici := "identity_credential_identifiers" switch con.Dialect.Name() { case "cockroach": - ici += "@identity_credential_identifiers_nid_identity_credential_id_idx" + ici += "@primary" case "sqlite3": ici += " INDEXED BY identity_credential_identifiers_nid_identity_credential_id_idx" case "mysql": @@ -719,7 +720,9 @@ func QueryForCredentials(con *pop.Connection, where ...Where) (map[uuid.UUID](ma "(identity_credentials.identity_credential_type_id = ict.id)", ).LeftJoin( ici, - "identity_credential_identifiers.identity_credential_id = identity_credentials.id AND identity_credential_identifiers.nid = identity_credentials.nid", + `identity_credential_identifiers.identity_id = identity_credentials.identity_id + AND identity_credential_identifiers.identity_credential_id = identity_credentials.id + AND identity_credential_identifiers.nid = identity_credentials.nid`, ) for _, w := range where { q = q.Where("("+w.Condition+")", w.Args...) diff --git a/persistence/sql/migratest/migration_test.go b/persistence/sql/migratest/migration_test.go index bf727683248b..dd2f8023aa76 100644 --- a/persistence/sql/migratest/migration_test.go +++ b/persistence/sql/migratest/migration_test.go @@ -16,12 +16,14 @@ import ( "github.com/ory/x/servicelocatorx" "github.com/ory/kratos/identity" + "github.com/ory/kratos/persistence/sql/migrations/gomigrations" "github.com/bradleyjkemp/cupaloy/v2" "github.com/stretchr/testify/assert" "github.com/ory/x/dbal" + "github.com/ory/kratos/x/slices" "github.com/ory/kratos/x/xsql" "github.com/ory/x/migratest" @@ -134,6 +136,10 @@ 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.IdentityPrimaryKeysStep1, + gomigrations.IdentityPrimaryKeysStep2, + )), ) require.NoError(t, err) tm.DumpMigrations = true diff --git a/persistence/sql/migrations/gomigrations/identity_pk.go b/persistence/sql/migrations/gomigrations/identity_pk.go new file mode 100644 index 000000000000..449bf361020e --- /dev/null +++ b/persistence/sql/migrations/gomigrations/identity_pk.go @@ -0,0 +1,290 @@ +// 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 IdentityPrimaryKeysStep1 = []popx.Migration{ + { + Version: "20240208000000000000", + 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)") + return errors.WithStack(err) + }, + }, + { + Version: "20240208000000000000", + 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: "20240208000000000001", + 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: "20240208000000000001", + 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) + }, + }, + { + Version: "20240208000000000002", + Path: path(), + Name: "Change primary key for identity_credentials", + Direction: "up", + Type: "go", + DBType: "cockroach", + RunnerNoTx: func(m popx.Migration, c *pop.Connection) error { + _, err := c.Store.Exec("ALTER TABLE identity_credentials ALTER PRIMARY KEY USING COLUMNS (identity_id,id)") + return errors.WithStack(err) + }, + }, + { + Version: "20240208000000000002", + Path: path(), + Name: "Revert primary key for identity_credentials", + Direction: "down", + Type: "go", + DBType: "cockroach", + RunnerNoTx: func(m popx.Migration, c *pop.Connection) error { + _, err := c.Store.Exec("ALTER TABLE identity_credentials ALTER PRIMARY KEY USING COLUMNS (id)") + return errors.WithStack(err) + }, + }, + { + Version: "20240208000000000003", + Path: path(), + Name: "Add column identity_id to identity_credential_identifiers and session_devices", + Direction: "up", + Type: "go", + DBType: "cockroach", + RunnerNoTx: func(m popx.Migration, c *pop.Connection) error { + _, err := c.Store.Exec("ALTER TABLE identity_credential_identifiers ADD COLUMN identity_id UUID NULL REFERENCES identities(id) ON DELETE CASCADE") + if err != nil { + return errors.WithStack(err) + } + _, err = c.Store.Exec("ALTER TABLE session_devices ADD COLUMN identity_id UUID NULL REFERENCES identities(id) ON DELETE CASCADE") + return errors.WithStack(err) + }, + }, + { + Version: "20240208000000000003", + Path: path(), + Name: "Drop column identity_id to identity_credential_identifiers and session_devices", + Direction: "down", + Type: "go", + DBType: "cockroach", + RunnerNoTx: func(m popx.Migration, c *pop.Connection) error { + _, err := c.Store.Exec("ALTER TABLE identity_credential_identifiers DROP COLUMN identity_id") + if err != nil { + return errors.WithStack(err) + } + _, err = c.Store.Exec("ALTER TABLE session_devices DROP COLUMN identity_id") + return errors.WithStack(err) + }, + }, +} + +var IdentityPrimaryKeysStep2 = []popx.Migration{ + { + Version: "20240208000000000004", + Path: path(), + Name: "Backfill column identity_id in identity_credential_identifiers", + Direction: "up", + Type: "go", + DBType: "cockroach", + RunnerNoTx: func(m popx.Migration, c *pop.Connection) error { + for { + res, err := c.Store.Exec(` + UPDATE + identity_credential_identifiers ici + SET + identity_id = ic.identity_id + FROM + identity_credentials ic + WHERE + ici.identity_credential_id = ic.id + AND ici.nid = ic.nid + AND ici.identity_id IS NULL + LIMIT 100`) + if err != nil { + return errors.WithStack(err) + } + n, err := res.RowsAffected() + if err != nil { + return errors.WithStack(err) + } + if n == 0 { + break + } + fmt.Printf("Backfilled %d rows in identity_credential_identifiers\n", n) + } + return nil + }, + }, + { + Version: "20240208000000000004", + Path: path(), + Name: "Revert backfill column identity_id in identity_credential_identifiers (noop)", + Direction: "down", + Type: "go", + DBType: "cockroach", + RunnerNoTx: func(m popx.Migration, c *pop.Connection) error { + // nothing + return nil + }, + }, + { + Version: "20240208000000000005", + Path: path(), + Name: "Change primary key of identity_credential_identifiers", + Direction: "up", + Type: "go", + DBType: "cockroach", + RunnerNoTx: func(m popx.Migration, c *pop.Connection) error { + _, err := c.Store.Exec("ALTER TABLE identity_credential_identifiers ALTER identity_id SET NOT NULL") + if err != nil { + return errors.WithStack(err) + } + _, err = c.Store.Exec("ALTER TABLE identity_credential_identifiers ALTER PRIMARY KEY USING COLUMNS (identity_id, identity_credential_id, id)") + return errors.WithStack(err) + }, + }, + { + Version: "20240208000000000005", + Path: path(), + Name: "Revert primary key of identity_credential_identifiers", + Direction: "down", + Type: "go", + DBType: "cockroach", + RunnerNoTx: func(m popx.Migration, c *pop.Connection) error { + _, err := c.Store.Exec("ALTER TABLE identity_credential_identifiers ALTER PRIMARY KEY USING COLUMNS (id)") + if err != nil { + return errors.WithStack(err) + } + _, err = c.Store.Exec("ALTER TABLE identity_credential_identifiers ALTER identity_id DROP NOT NULL") + return errors.WithStack(err) + }, + }, + { + Version: "20240208000000000006", + Path: path(), + Name: "Backfill column identity_id in session_devices", + Direction: "up", + Type: "go", + DBType: "cockroach", + RunnerNoTx: func(m popx.Migration, c *pop.Connection) error { + for { + res, err := c.Store.Exec(` + UPDATE + session_devices sd + SET + identity_id = s.identity_id + FROM + sessions s + WHERE + sd.session_id = s.id + AND sd.nid = s.nid + AND sd.identity_id IS NULL + LIMIT 100`) + if err != nil { + return errors.WithStack(err) + } + n, err := res.RowsAffected() + if err != nil { + return errors.WithStack(err) + } + if n == 0 { + break + } + fmt.Printf("Backfilled %d rows in session_devices\n", n) + } + return nil + }, + }, + { + Version: "20240208000000000006", + Path: path(), + Name: "Revert backfill column identity_id in Backfill column identity_id in session_devices (noop)", + Direction: "down", + Type: "go", + DBType: "cockroach", + RunnerNoTx: func(m popx.Migration, c *pop.Connection) error { + // nothing + return nil + }, + }, + { + Version: "20240208000000000007", + Path: path(), + Name: "Change primary key of session_devices", + Direction: "up", + Type: "go", + DBType: "cockroach", + RunnerNoTx: func(m popx.Migration, c *pop.Connection) error { + _, err := c.Store.Exec("ALTER TABLE session_devices ALTER identity_id SET NOT NULL") + if err != nil { + return errors.WithStack(err) + } + _, err = c.Store.Exec("ALTER TABLE session_devices ALTER PRIMARY KEY USING COLUMNS (session_id, identity_id, id)") + return errors.WithStack(err) + }, + }, + { + Version: "20240208000000000007", + Path: path(), + Name: "Revert primary key of session_devices", + Direction: "down", + Type: "go", + DBType: "cockroach", + RunnerNoTx: func(m popx.Migration, c *pop.Connection) error { + _, err := c.Store.Exec("ALTER TABLE session_devices ALTER PRIMARY KEY USING COLUMNS (id)") + if err != nil { + return errors.WithStack(err) + } + _, err = c.Store.Exec("ALTER TABLE session_devices ALTER identity_id DROP NOT NULL") + return errors.WithStack(err) + }, + }, +} diff --git a/persistence/sql/migrations/sql/20210817181232000000_unique_credentials.sqlite3.down.sql b/persistence/sql/migrations/sql/20210817181232000000_unique_credentials.sqlite3.down.sql index ebb42cf99e00..7d1f91589640 100644 --- a/persistence/sql/migrations/sql/20210817181232000000_unique_credentials.sqlite3.down.sql +++ b/persistence/sql/migrations/sql/20210817181232000000_unique_credentials.sqlite3.down.sql @@ -1 +1,2 @@ -CREATE UNIQUE INDEX "identity_credential_identifiers_identifier_nid_uq_idx" ON "identity_credential_identifiers" (nid, identifier); \ No newline at end of file +DELETE FROM "identity_credential_identifiers"; -- This migration is destructive. +CREATE UNIQUE INDEX "identity_credential_identifiers_identifier_nid_uq_idx" ON "identity_credential_identifiers" (nid, identifier); diff --git a/persistence/sql/migrations/sql/20240208000000000000_identity_verifiable_addresses_pk.mysql.down.sql b/persistence/sql/migrations/sql/20240208000000000000_identity_verifiable_addresses_pk.mysql.down.sql new file mode 100644 index 000000000000..91863660ac61 --- /dev/null +++ b/persistence/sql/migrations/sql/20240208000000000000_identity_verifiable_addresses_pk.mysql.down.sql @@ -0,0 +1,7 @@ +ALTER TABLE identity_verifiable_addresses + DROP FOREIGN KEY identity_verifiable_addresses_ibfk_1; + +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; diff --git a/persistence/sql/migrations/sql/20240208000000000000_identity_verifiable_addresses_pk.mysql.up.sql b/persistence/sql/migrations/sql/20240208000000000000_identity_verifiable_addresses_pk.mysql.up.sql new file mode 100644 index 000000000000..4ad5ad9aa98d --- /dev/null +++ b/persistence/sql/migrations/sql/20240208000000000000_identity_verifiable_addresses_pk.mysql.up.sql @@ -0,0 +1,3 @@ +ALTER TABLE identity_verifiable_addresses + DROP PRIMARY KEY, + ADD PRIMARY KEY (identity_id, id); diff --git a/persistence/sql/migrations/sql/20240208000000000000_identity_verifiable_addresses_pk.postgres.down.sql b/persistence/sql/migrations/sql/20240208000000000000_identity_verifiable_addresses_pk.postgres.down.sql new file mode 100644 index 000000000000..ed3fe5674e96 --- /dev/null +++ b/persistence/sql/migrations/sql/20240208000000000000_identity_verifiable_addresses_pk.postgres.down.sql @@ -0,0 +1,3 @@ +ALTER TABLE identity_verifiable_addresses + DROP CONSTRAINT identity_verifiable_addresses_pkey, + ADD PRIMARY KEY (id); diff --git a/persistence/sql/migrations/sql/20240208000000000000_identity_verifiable_addresses_pk.postgres.up.sql b/persistence/sql/migrations/sql/20240208000000000000_identity_verifiable_addresses_pk.postgres.up.sql new file mode 100644 index 000000000000..67cdda56393c --- /dev/null +++ b/persistence/sql/migrations/sql/20240208000000000000_identity_verifiable_addresses_pk.postgres.up.sql @@ -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; + +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; diff --git a/persistence/sql/migrations/sql/20240208000000000000_identity_verifiable_addresses_pk.sqlite.down.sql b/persistence/sql/migrations/sql/20240208000000000000_identity_verifiable_addresses_pk.sqlite.down.sql new file mode 100644 index 000000000000..461a5e8fa392 --- /dev/null +++ b/persistence/sql/migrations/sql/20240208000000000000_identity_verifiable_addresses_pk.sqlite.down.sql @@ -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); diff --git a/persistence/sql/migrations/sql/20240208000000000000_identity_verifiable_addresses_pk.sqlite.up.sql b/persistence/sql/migrations/sql/20240208000000000000_identity_verifiable_addresses_pk.sqlite.up.sql new file mode 100644 index 000000000000..dae62489b9e6 --- /dev/null +++ b/persistence/sql/migrations/sql/20240208000000000000_identity_verifiable_addresses_pk.sqlite.up.sql @@ -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); diff --git a/persistence/sql/migrations/sql/20240208000000000001_identity_recovery_addresses_pk.mysql.down.sql b/persistence/sql/migrations/sql/20240208000000000001_identity_recovery_addresses_pk.mysql.down.sql new file mode 100644 index 000000000000..132924783396 --- /dev/null +++ b/persistence/sql/migrations/sql/20240208000000000001_identity_recovery_addresses_pk.mysql.down.sql @@ -0,0 +1,24 @@ +ALTER TABLE identity_recovery_codes + DROP FOREIGN KEY identity_recovery_codes_identity_recovery_addresses_id_fk, + DROP FOREIGN KEY identity_recovery_codes_identity_id_fk; + +ALTER TABLE identity_recovery_tokens + DROP FOREIGN KEY identity_recovery_tokens_ibfk_1, + DROP FOREIGN KEY identity_recovery_tokens_identity_id_fk_idx; + +ALTER TABLE identity_recovery_addresses + DROP FOREIGN KEY identity_recovery_addresses_ibfk_1; + +ALTER TABLE identity_recovery_addresses + DROP PRIMARY KEY, + ADD PRIMARY KEY (id), + ADD CONSTRAINT identity_recovery_addresses_ibfk_1 FOREIGN KEY (identity_id) REFERENCES identities(id) ON DELETE CASCADE; + +ALTER TABLE identity_recovery_codes + ADD CONSTRAINT identity_recovery_codes_identity_recovery_addresses_id_fk FOREIGN KEY (identity_recovery_address_id) REFERENCES identity_recovery_addresses (id) ON DELETE CASCADE, + ADD CONSTRAINT identity_recovery_tokens_identity_id_fk FOREIGN KEY (identity_id) REFERENCES identities (id) ON DELETE CASCADE; -- this foreign key constraint was previously misnamed and this down-migration restores the incorrect name + + +ALTER TABLE identity_recovery_tokens + ADD CONSTRAINT identity_recovery_tokens_ibfk_1 FOREIGN KEY (identity_recovery_address_id) REFERENCES identity_recovery_addresses(id) ON DELETE CASCADE, + ADD CONSTRAINT identity_recovery_tokens_identity_id_fk_idx FOREIGN KEY (identity_id) REFERENCES identities(id) ON DELETE CASCADE; diff --git a/persistence/sql/migrations/sql/20240208000000000001_identity_recovery_addresses_pk.mysql.up.sql b/persistence/sql/migrations/sql/20240208000000000001_identity_recovery_addresses_pk.mysql.up.sql new file mode 100644 index 000000000000..34015cca5a41 --- /dev/null +++ b/persistence/sql/migrations/sql/20240208000000000001_identity_recovery_addresses_pk.mysql.up.sql @@ -0,0 +1,19 @@ +ALTER TABLE identity_recovery_codes + DROP FOREIGN KEY identity_recovery_codes_identity_recovery_addresses_id_fk, + DROP FOREIGN KEY identity_recovery_tokens_identity_id_fk; -- this foreign key constraint was previously misnamed + +ALTER TABLE identity_recovery_tokens + DROP FOREIGN KEY identity_recovery_tokens_ibfk_1, + DROP FOREIGN KEY identity_recovery_tokens_identity_id_fk_idx; + +ALTER TABLE identity_recovery_addresses + DROP PRIMARY KEY, + ADD PRIMARY KEY (identity_id, id); + +ALTER TABLE identity_recovery_codes + ADD CONSTRAINT identity_recovery_codes_identity_recovery_addresses_id_fk FOREIGN KEY (identity_recovery_address_id) REFERENCES identity_recovery_addresses (id) ON DELETE CASCADE, + ADD CONSTRAINT identity_recovery_codes_identity_id_fk FOREIGN KEY (identity_id) REFERENCES identities (id) ON DELETE CASCADE; -- this foreign key constraint was previously misnamed, this is the correct name + +ALTER TABLE identity_recovery_tokens + ADD CONSTRAINT identity_recovery_tokens_ibfk_1 FOREIGN KEY (identity_recovery_address_id) REFERENCES identity_recovery_addresses(id) ON DELETE CASCADE, + ADD CONSTRAINT identity_recovery_tokens_identity_id_fk_idx FOREIGN KEY (identity_id) REFERENCES identities(id) ON DELETE CASCADE; diff --git a/persistence/sql/migrations/sql/20240208000000000001_identity_recovery_addresses_pk.postgres.down.sql b/persistence/sql/migrations/sql/20240208000000000001_identity_recovery_addresses_pk.postgres.down.sql new file mode 100644 index 000000000000..4d0b140857a4 --- /dev/null +++ b/persistence/sql/migrations/sql/20240208000000000001_identity_recovery_addresses_pk.postgres.down.sql @@ -0,0 +1,3 @@ +ALTER TABLE identity_recovery_addresses + DROP CONSTRAINT identity_recovery_addresses_pkey, + ADD PRIMARY KEY (id); diff --git a/persistence/sql/migrations/sql/20240208000000000001_identity_recovery_addresses_pk.postgres.up.sql b/persistence/sql/migrations/sql/20240208000000000001_identity_recovery_addresses_pk.postgres.up.sql new file mode 100644 index 000000000000..859f0f1bd2c2 --- /dev/null +++ b/persistence/sql/migrations/sql/20240208000000000001_identity_recovery_addresses_pk.postgres.up.sql @@ -0,0 +1,17 @@ +CREATE UNIQUE INDEX identity_recovery_addresses_id_uq_idx ON identity_recovery_addresses (id); + +ALTER TABLE identity_recovery_codes + DROP CONSTRAINT identity_recovery_codes_identity_recovery_addresses_id_fk; + +ALTER TABLE identity_recovery_tokens + DROP CONSTRAINT identity_recovery_tokens_identity_recovery_address_id_fkey; + +ALTER TABLE identity_recovery_addresses + DROP CONSTRAINT identity_recovery_addresses_pkey, + ADD PRIMARY KEY (identity_id, id); + +ALTER TABLE identity_recovery_codes + ADD CONSTRAINT identity_recovery_codes_identity_recovery_addresses_id_fk FOREIGN KEY (identity_recovery_address_id) REFERENCES identity_recovery_addresses(id) ON DELETE CASCADE; + +ALTER TABLE identity_recovery_tokens + ADD CONSTRAINT identity_recovery_tokens_identity_recovery_address_id_fkey FOREIGN KEY (identity_recovery_address_id) REFERENCES identity_recovery_addresses(id) ON DELETE CASCADE; diff --git a/persistence/sql/migrations/sql/20240208000000000001_identity_recovery_addresses_pk.sqlite.down.sql b/persistence/sql/migrations/sql/20240208000000000001_identity_recovery_addresses_pk.sqlite.down.sql new file mode 100644 index 000000000000..923af86dca4b --- /dev/null +++ b/persistence/sql/migrations/sql/20240208000000000001_identity_recovery_addresses_pk.sqlite.down.sql @@ -0,0 +1,25 @@ +CREATE TABLE "_identity_recovery_addresses_tmp" ( +"id" TEXT PRIMARY KEY, +"via" TEXT NOT NULL, +"value" TEXT NOT NULL, +"identity_id" char(36) NOT NULL, +"created_at" DATETIME NOT NULL, +"updated_at" DATETIME NOT NULL, +"nid" char(36), +FOREIGN KEY (identity_id) REFERENCES identities (id) ON UPDATE NO ACTION ON DELETE CASCADE +); + +INSERT INTO "_identity_recovery_addresses_tmp" + ("id", "via", "value", "identity_id", "created_at", "updated_at", "nid") +SELECT + "id", "via", "value", "identity_id", "created_at", "updated_at", "nid" +FROM "identity_recovery_addresses"; + +DROP TABLE "identity_recovery_addresses"; +ALTER TABLE "_identity_recovery_addresses_tmp" RENAME TO "identity_recovery_addresses"; + +CREATE INDEX identity_recovery_addresses_nid_id_idx ON identity_recovery_addresses (nid, id); +CREATE INDEX identity_recovery_addresses_id_nid_idx ON identity_recovery_addresses (id, nid); +CREATE UNIQUE INDEX "identity_recovery_addresses_id_uq_idx" ON "identity_recovery_addresses" (id); +CREATE UNIQUE INDEX "identity_recovery_addresses_status_via_uq_idx" ON "identity_recovery_addresses" (nid, via, value); +CREATE INDEX "identity_recovery_addresses_status_via_idx" ON "identity_recovery_addresses" (nid, via, value); diff --git a/persistence/sql/migrations/sql/20240208000000000001_identity_recovery_addresses_pk.sqlite.up.sql b/persistence/sql/migrations/sql/20240208000000000001_identity_recovery_addresses_pk.sqlite.up.sql new file mode 100644 index 000000000000..154e25167585 --- /dev/null +++ b/persistence/sql/migrations/sql/20240208000000000001_identity_recovery_addresses_pk.sqlite.up.sql @@ -0,0 +1,26 @@ +CREATE TABLE "_identity_recovery_addresses_tmp" ( +"id" TEXT NOT NULL, +"via" TEXT NOT NULL, +"value" TEXT NOT NULL, +"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_recovery_addresses_tmp" + ("id", "via", "value", "identity_id", "created_at", "updated_at", "nid") +SELECT + "id", "via", "value", "identity_id", "created_at", "updated_at", "nid" +FROM "identity_recovery_addresses"; + +DROP TABLE "identity_recovery_addresses"; +ALTER TABLE "_identity_recovery_addresses_tmp" RENAME TO "identity_recovery_addresses"; + +CREATE INDEX identity_recovery_addresses_nid_id_idx ON identity_recovery_addresses (nid, id); +CREATE UNIQUE INDEX "identity_recovery_addresses_id_uq_idx" ON "identity_recovery_addresses" (id); +CREATE UNIQUE INDEX "identity_recovery_addresses_status_via_uq_idx" ON "identity_recovery_addresses" (nid, via, value); +CREATE INDEX "identity_recovery_addresses_status_via_idx" ON "identity_recovery_addresses" (nid, via, value); diff --git a/persistence/sql/migrations/sql/20240208000000000002_identity_credentials_pk.mysql.down.sql b/persistence/sql/migrations/sql/20240208000000000002_identity_credentials_pk.mysql.down.sql new file mode 100644 index 000000000000..e6322379fe13 --- /dev/null +++ b/persistence/sql/migrations/sql/20240208000000000002_identity_credentials_pk.mysql.down.sql @@ -0,0 +1,3 @@ +ALTER TABLE identity_credentials + DROP PRIMARY KEY, + ADD PRIMARY KEY (id(36)); diff --git a/persistence/sql/migrations/sql/20240208000000000002_identity_credentials_pk.mysql.up.sql b/persistence/sql/migrations/sql/20240208000000000002_identity_credentials_pk.mysql.up.sql new file mode 100644 index 000000000000..e41634aab4ee --- /dev/null +++ b/persistence/sql/migrations/sql/20240208000000000002_identity_credentials_pk.mysql.up.sql @@ -0,0 +1,3 @@ +ALTER TABLE identity_credentials + DROP PRIMARY KEY, + ADD PRIMARY KEY (identity_id(36), id(36)); diff --git a/persistence/sql/migrations/sql/20240208000000000002_identity_credentials_pk.postgres.down.sql b/persistence/sql/migrations/sql/20240208000000000002_identity_credentials_pk.postgres.down.sql new file mode 100644 index 000000000000..726a86b5bed1 --- /dev/null +++ b/persistence/sql/migrations/sql/20240208000000000002_identity_credentials_pk.postgres.down.sql @@ -0,0 +1,3 @@ +ALTER TABLE identity_credentials + DROP CONSTRAINT identity_credentials_pkey, + ADD PRIMARY KEY (id); diff --git a/persistence/sql/migrations/sql/20240208000000000002_identity_credentials_pk.postgres.up.sql b/persistence/sql/migrations/sql/20240208000000000002_identity_credentials_pk.postgres.up.sql new file mode 100644 index 000000000000..02849e53a59c --- /dev/null +++ b/persistence/sql/migrations/sql/20240208000000000002_identity_credentials_pk.postgres.up.sql @@ -0,0 +1,11 @@ +CREATE UNIQUE INDEX identity_credentials_id_uq_idx ON identity_credentials(id); + +ALTER TABLE identity_credential_identifiers + DROP CONSTRAINT identity_credential_identifiers_identity_credential_id_fkey; + +ALTER TABLE identity_credentials + DROP CONSTRAINT identity_credentials_pkey, + ADD PRIMARY KEY (identity_id, id); + +ALTER TABLE identity_credential_identifiers + ADD CONSTRAINT identity_credential_identifiers_identity_credential_id_fkey FOREIGN KEY (identity_credential_id) REFERENCES identity_credentials(id) ON DELETE CASCADE; diff --git a/persistence/sql/migrations/sql/20240208000000000002_identity_credentials_pk.sqlite.down.sql b/persistence/sql/migrations/sql/20240208000000000002_identity_credentials_pk.sqlite.down.sql new file mode 100644 index 000000000000..080f3725be55 --- /dev/null +++ b/persistence/sql/migrations/sql/20240208000000000002_identity_credentials_pk.sqlite.down.sql @@ -0,0 +1,24 @@ +CREATE TABLE "_identity_credentials_tmp" ( +"id" TEXT PRIMARY KEY, +"config" TEXT NOT NULL, +"identity_credential_type_id" char(36) NOT NULL, +"identity_id" char(36) NOT NULL, +"created_at" DATETIME NOT NULL, +"updated_at" DATETIME NOT NULL, +"nid" char(36), version INT NOT NULL DEFAULT '0', +FOREIGN KEY (identity_credential_type_id) REFERENCES identity_credential_types (id) ON UPDATE NO ACTION ON DELETE CASCADE, +FOREIGN KEY (identity_id) REFERENCES identities (id) ON UPDATE NO ACTION ON DELETE CASCADE +); + +INSERT INTO "_identity_credentials_tmp" + ("id", "config", "identity_credential_type_id", "identity_id", "created_at", "updated_at", "nid", "version") +SELECT + "id", "config", "identity_credential_type_id", "identity_id", "created_at", "updated_at", "nid", "version" +FROM "identity_credentials"; + +DROP TABLE "identity_credentials"; +ALTER TABLE "_identity_credentials_tmp" RENAME TO "identity_credentials"; + +CREATE INDEX identity_credentials_nid_id_idx ON identity_credentials (nid, id); +CREATE INDEX identity_credentials_id_nid_idx ON identity_credentials (id, nid); +CREATE UNIQUE INDEX identity_credentials_id_uq_idx ON identity_credentials (id); diff --git a/persistence/sql/migrations/sql/20240208000000000002_identity_credentials_pk.sqlite.up.sql b/persistence/sql/migrations/sql/20240208000000000002_identity_credentials_pk.sqlite.up.sql new file mode 100644 index 000000000000..d35be1e3d9ab --- /dev/null +++ b/persistence/sql/migrations/sql/20240208000000000002_identity_credentials_pk.sqlite.up.sql @@ -0,0 +1,25 @@ +CREATE TABLE "_identity_credentials_tmp" ( +"id" TEXT NOT NULL, +"config" TEXT NOT NULL, +"identity_credential_type_id" TEXT NOT NULL, +"identity_id" TEXT NOT NULL, +"created_at" DATETIME NOT NULL, +"updated_at" DATETIME NOT NULL, +"nid" TEXT NOT NULL, +"version" INT NOT NULL DEFAULT '0', +PRIMARY KEY (identity_id,id), +FOREIGN KEY (identity_credential_type_id) REFERENCES identity_credential_types (id) ON UPDATE NO ACTION ON DELETE CASCADE, +FOREIGN KEY (identity_id) REFERENCES identities (id) ON UPDATE NO ACTION ON DELETE CASCADE +); + +INSERT INTO "_identity_credentials_tmp" + ("id", "config", "identity_credential_type_id", "identity_id", "created_at", "updated_at", "nid", "version") +SELECT + "id", "config", "identity_credential_type_id", "identity_id", "created_at", "updated_at", "nid", "version" +FROM "identity_credentials"; + +DROP TABLE "identity_credentials"; +ALTER TABLE "_identity_credentials_tmp" RENAME TO "identity_credentials"; + +CREATE INDEX identity_credentials_nid_id_idx ON identity_credentials (nid, id); +CREATE UNIQUE INDEX identity_credentials_id_uq_idx ON identity_credentials (id); diff --git a/persistence/sql/migrations/sql/20240208000000000003_identity_credential_identifiers_identity_id.mysql.down.sql b/persistence/sql/migrations/sql/20240208000000000003_identity_credential_identifiers_identity_id.mysql.down.sql new file mode 100644 index 000000000000..568a99a1aadd --- /dev/null +++ b/persistence/sql/migrations/sql/20240208000000000003_identity_credential_identifiers_identity_id.mysql.down.sql @@ -0,0 +1,3 @@ +ALTER TABLE identity_credential_identifiers + DROP PRIMARY KEY, + ADD PRIMARY KEY(id); diff --git a/persistence/sql/migrations/sql/20240208000000000003_identity_credential_identifiers_identity_id.mysql.up.sql b/persistence/sql/migrations/sql/20240208000000000003_identity_credential_identifiers_identity_id.mysql.up.sql new file mode 100644 index 000000000000..d2ece3d0c6b3 --- /dev/null +++ b/persistence/sql/migrations/sql/20240208000000000003_identity_credential_identifiers_identity_id.mysql.up.sql @@ -0,0 +1,17 @@ +ALTER TABLE identity_credential_identifiers ADD COLUMN identity_id char(36) NULL; + +UPDATE + identity_credential_identifiers ici +JOIN + identity_credentials ic + ON ici.identity_credential_id = ic.id + AND ici.nid = ic.nid +SET + ici.identity_id = ic.identity_id +WHERE + ici.identity_id IS NULL; + +ALTER TABLE identity_credential_identifiers + MODIFY identity_id char(36) NOT NULL, + DROP PRIMARY KEY, + ADD PRIMARY KEY(identity_id, identity_credential_id, id); diff --git a/persistence/sql/migrations/sql/20240208000000000003_identity_credential_identifiers_identity_id.postgres.down.sql b/persistence/sql/migrations/sql/20240208000000000003_identity_credential_identifiers_identity_id.postgres.down.sql new file mode 100644 index 000000000000..84108c191ab1 --- /dev/null +++ b/persistence/sql/migrations/sql/20240208000000000003_identity_credential_identifiers_identity_id.postgres.down.sql @@ -0,0 +1,4 @@ +ALTER TABLE identity_credential_identifiers + DROP CONSTRAINT identity_credential_identifiers_pkey, + ADD PRIMARY KEY (id), + DROP COLUMN identity_id; diff --git a/persistence/sql/migrations/sql/20240208000000000003_identity_credential_identifiers_identity_id.postgres.up.sql b/persistence/sql/migrations/sql/20240208000000000003_identity_credential_identifiers_identity_id.postgres.up.sql new file mode 100644 index 000000000000..32278a8cb66e --- /dev/null +++ b/persistence/sql/migrations/sql/20240208000000000003_identity_credential_identifiers_identity_id.postgres.up.sql @@ -0,0 +1,19 @@ +ALTER TABLE identity_credential_identifiers ADD COLUMN identity_id UUID NULL; + +CREATE UNIQUE INDEX identity_credential_identifiers_id_uq_idx ON identity_credential_identifiers(id); + +UPDATE + identity_credential_identifiers ici +SET + identity_id = ic.identity_id +FROM + identity_credentials ic +WHERE + ici.identity_credential_id = ic.id + AND ici.nid = ic.nid + AND ici.identity_id IS NULL; + +ALTER TABLE identity_credential_identifiers + ALTER identity_id SET NOT NULL, + DROP CONSTRAINT identity_credential_identifiers_pkey, + ADD PRIMARY KEY(identity_id, identity_credential_id, id); diff --git a/persistence/sql/migrations/sql/20240208000000000003_identity_credential_identifiers_identity_id.sqlite.down.sql b/persistence/sql/migrations/sql/20240208000000000003_identity_credential_identifiers_identity_id.sqlite.down.sql new file mode 100644 index 000000000000..9686890480b4 --- /dev/null +++ b/persistence/sql/migrations/sql/20240208000000000003_identity_credential_identifiers_identity_id.sqlite.down.sql @@ -0,0 +1,23 @@ +CREATE TABLE IF NOT EXISTS "_identity_credential_identifiers_tmp" ( +"id" TEXT PRIMARY KEY, +"identifier" TEXT NOT NULL, +"identity_credential_id" char(36) NOT NULL, +"created_at" DATETIME NOT NULL, +"updated_at" DATETIME NOT NULL, +"nid" char(36), +"identity_credential_type_id" char(36) NOT NULL, +FOREIGN KEY (identity_credential_id) REFERENCES identity_credentials (id) ON UPDATE NO ACTION ON DELETE CASCADE +); + +INSERT INTO _identity_credential_identifiers_tmp (id, identifier, identity_credential_id, created_at, updated_at, nid, identity_credential_type_id) + SELECT id, identifier, identity_credential_id, created_at, updated_at, nid, identity_credential_type_id + FROM identity_credential_identifiers; + +DROP TABLE identity_credential_identifiers; +ALTER TABLE "_identity_credential_identifiers_tmp" RENAME TO "identity_credential_identifiers"; + +CREATE UNIQUE INDEX "identity_credential_identifiers_identifier_nid_type_uq_idx" ON "identity_credential_identifiers" (nid, identity_credential_type_id, identifier); +CREATE INDEX "identity_credential_identifiers_nid_identity_credential_id_idx" ON "identity_credential_identifiers" (identity_credential_id, nid); +CREATE INDEX identity_credential_identifiers_nid_id_idx ON identity_credential_identifiers (nid, id); +CREATE INDEX identity_credential_identifiers_id_nid_idx ON identity_credential_identifiers (id, nid); +CREATE INDEX identity_credential_identifiers_nid_i_ici_idx ON identity_credential_identifiers (nid, identifier, identity_credential_id); diff --git a/persistence/sql/migrations/sql/20240208000000000003_identity_credential_identifiers_identity_id.sqlite.up.sql b/persistence/sql/migrations/sql/20240208000000000003_identity_credential_identifiers_identity_id.sqlite.up.sql new file mode 100644 index 000000000000..5414ebd14166 --- /dev/null +++ b/persistence/sql/migrations/sql/20240208000000000003_identity_credential_identifiers_identity_id.sqlite.up.sql @@ -0,0 +1,30 @@ +CREATE TABLE "_identity_credential_identifiers_tmp" ( +"id" TEXT NOT NULL, +"identifier" TEXT NOT NULL, +"identity_credential_id" TEXT NOT NULL, +"identity_id" TEXT NOT NULL, +"created_at" DATETIME NOT NULL, +"updated_at" DATETIME NOT NULL, +"nid" TEXT NOT NULL, +"identity_credential_type_id" TEXT NOT NULL, +PRIMARY KEY (identity_id, identity_credential_id, id), +FOREIGN KEY (identity_id) REFERENCES identities (id) ON UPDATE RESTRICT ON DELETE CASCADE, +FOREIGN KEY (identity_credential_id) REFERENCES identity_credentials (id) ON UPDATE RESTRICT ON DELETE CASCADE, +FOREIGN KEY (nid) REFERENCES networks (id) ON UPDATE RESTRICT ON DELETE CASCADE +); + + +INSERT INTO _identity_credential_identifiers_tmp (id, identifier, identity_credential_id, created_at, updated_at, nid, identity_credential_type_id, identity_id) + SELECT ici.id, ici.identifier, ici.identity_credential_id, ici.created_at, ici.updated_at, ici.nid, ici.identity_credential_type_id, ic.identity_id + FROM identity_credential_identifiers ici + INNER JOIN identity_credentials ic ON ici.identity_credential_id = ic.id AND ici.nid = ic.nid; + +DROP TABLE identity_credential_identifiers; +ALTER TABLE "_identity_credential_identifiers_tmp" RENAME TO "identity_credential_identifiers"; + +CREATE UNIQUE INDEX "identity_credential_identifiers_identifier_nid_type_uq_idx" ON "identity_credential_identifiers" (nid, identity_credential_type_id, identifier); +CREATE INDEX "identity_credential_identifiers_nid_identity_credential_id_idx" ON "identity_credential_identifiers" (identity_credential_id, nid); +CREATE UNIQUE INDEX "identity_credential_identifiers_id_uq_idx" ON "identity_credential_identifiers" (id); +CREATE INDEX identity_credential_identifiers_nid_id_idx ON identity_credential_identifiers (nid, id); +CREATE INDEX identity_credential_identifiers_id_nid_idx ON identity_credential_identifiers (id, nid); +CREATE INDEX identity_credential_identifiers_nid_i_ici_idx ON identity_credential_identifiers (nid, identifier, identity_credential_id); diff --git a/persistence/sql/migrations/sql/20240208000000000004_session_devices_pk.mysql.down.sql b/persistence/sql/migrations/sql/20240208000000000004_session_devices_pk.mysql.down.sql new file mode 100644 index 000000000000..2e2f4ac81658 --- /dev/null +++ b/persistence/sql/migrations/sql/20240208000000000004_session_devices_pk.mysql.down.sql @@ -0,0 +1,7 @@ +ALTER TABLE session_devices + DROP FOREIGN KEY session_devices_ibfk_1, + DROP PRIMARY KEY, + ADD PRIMARY KEY(id), + DROP COLUMN identity_id; +ALTER TABLE session_devices + ADD CONSTRAINT session_devices_ibfk_1 FOREIGN KEY (session_id) REFERENCES sessions(id) ON DELETE CASCADE; diff --git a/persistence/sql/migrations/sql/20240208000000000004_session_devices_pk.mysql.up.sql b/persistence/sql/migrations/sql/20240208000000000004_session_devices_pk.mysql.up.sql new file mode 100644 index 000000000000..5e40603830fc --- /dev/null +++ b/persistence/sql/migrations/sql/20240208000000000004_session_devices_pk.mysql.up.sql @@ -0,0 +1,18 @@ +ALTER TABLE session_devices ADD COLUMN identity_id char(36) NULL; + +UPDATE + session_devices sd +JOIN + sessions s ON sd.session_id = s.id +SET + sd.identity_id = s.identity_id +WHERE + sd.identity_id IS NULL; + +ALTER TABLE session_devices + MODIFY identity_id char(36) NOT NULL, + DROP FOREIGN KEY session_devices_ibfk_1, + DROP PRIMARY KEY, + ADD PRIMARY KEY(session_id(36), identity_id(36), id(36)); +ALTER TABLE session_devices + ADD CONSTRAINT session_devices_ibfk_1 FOREIGN KEY (nid,identity_id,session_id) REFERENCES sessions(nid,identity_id,id) ON DELETE CASCADE; diff --git a/persistence/sql/migrations/sql/20240208000000000004_session_devices_pk.postgres.down.sql b/persistence/sql/migrations/sql/20240208000000000004_session_devices_pk.postgres.down.sql new file mode 100644 index 000000000000..68601d6a52b8 --- /dev/null +++ b/persistence/sql/migrations/sql/20240208000000000004_session_devices_pk.postgres.down.sql @@ -0,0 +1,4 @@ +ALTER TABLE session_devices + DROP CONSTRAINT session_devices_pkey, + ADD PRIMARY KEY (id), + DROP COLUMN identity_id; diff --git a/persistence/sql/migrations/sql/20240208000000000004_session_devices_pk.postgres.up.sql b/persistence/sql/migrations/sql/20240208000000000004_session_devices_pk.postgres.up.sql new file mode 100644 index 000000000000..26aa7eb513b3 --- /dev/null +++ b/persistence/sql/migrations/sql/20240208000000000004_session_devices_pk.postgres.up.sql @@ -0,0 +1,19 @@ +ALTER TABLE session_devices ADD COLUMN identity_id uuid NULL; + +CREATE UNIQUE INDEX session_devices_id_uq_idx ON session_devices(id); + +UPDATE + session_devices sd +SET + identity_id = s.identity_id +FROM + sessions s +WHERE + sd.session_id = s.id + AND sd.nid = s.nid + AND sd.identity_id IS NULL; + +ALTER TABLE session_devices + ALTER COLUMN identity_id SET NOT NULL, + DROP CONSTRAINT session_devices_pkey, + ADD PRIMARY KEY (session_id, identity_id, id); diff --git a/persistence/sql/migrations/sql/20240208000000000004_session_devices_pk.sqlite.down.sql b/persistence/sql/migrations/sql/20240208000000000004_session_devices_pk.sqlite.down.sql new file mode 100644 index 000000000000..0f84fa6f2baa --- /dev/null +++ b/persistence/sql/migrations/sql/20240208000000000004_session_devices_pk.sqlite.down.sql @@ -0,0 +1,26 @@ +CREATE TABLE IF NOT EXISTS "_session_devices_tmp" +( + "id" UUID PRIMARY KEY NOT NULL, + "ip_address" VARCHAR(50) DEFAULT '', + "user_agent" VARCHAR(512) DEFAULT '', + "location" VARCHAR(512) DEFAULT '', + "nid" UUID NOT NULL, + "session_id" UUID NOT NULL, + "created_at" timestamp NOT NULL, + "updated_at" timestamp NOT NULL, + CONSTRAINT "session_metadata_sessions_id_fk" FOREIGN KEY ("session_id") REFERENCES "sessions" ("id") ON DELETE cascade, + CONSTRAINT "session_metadata_nid_fk" FOREIGN KEY ("nid") REFERENCES "networks" ("id") ON DELETE cascade, + CONSTRAINT unique_session_device UNIQUE (nid, session_id, ip_address, user_agent) +); + +INSERT INTO "_session_devices_tmp" + ("id", "ip_address", "user_agent", "location", "nid", "session_id", "created_at", "updated_at") +SELECT + "id", "ip_address", "user_agent", "location", "nid", "session_id", "created_at", "updated_at" +FROM "session_devices"; + +DROP TABLE "session_devices"; +ALTER TABLE "_session_devices_tmp" RENAME TO "session_devices"; + +CREATE INDEX "session_devices_id_nid_idx" ON "session_devices" (id, nid); +CREATE INDEX "session_devices_session_id_nid_idx" ON "session_devices" (session_id, nid); diff --git a/persistence/sql/migrations/sql/20240208000000000004_session_devices_pk.sqlite.up.sql b/persistence/sql/migrations/sql/20240208000000000004_session_devices_pk.sqlite.up.sql new file mode 100644 index 000000000000..7500baef0f7d --- /dev/null +++ b/persistence/sql/migrations/sql/20240208000000000004_session_devices_pk.sqlite.up.sql @@ -0,0 +1,29 @@ +CREATE TABLE IF NOT EXISTS "_session_devices_tmp" +( + "session_id" TEXT NOT NULL, + "identity_id" TEXT NOT NULL, + "id" TEXT NOT NULL, + "ip_address" VARCHAR(50) DEFAULT '', + "user_agent" VARCHAR(512) DEFAULT '', + "location" VARCHAR(512) DEFAULT '', + "nid" TEXT NOT NULL, + "created_at" timestamp NOT NULL, + "updated_at" timestamp NOT NULL, + PRIMARY KEY (session_id, identity_id, id), + CONSTRAINT "session_metadata_session_id_fk" FOREIGN KEY ("session_id") REFERENCES "sessions" ("id") ON DELETE cascade, + CONSTRAINT "session_metadata_identity_id_fk" FOREIGN KEY ("identity_id") REFERENCES "identities" ("id") ON DELETE cascade, + CONSTRAINT "session_metadata_nid_fk" FOREIGN KEY ("nid") REFERENCES "networks" ("id") ON DELETE cascade, + CONSTRAINT unique_session_device UNIQUE (nid, session_id, ip_address, user_agent), + CONSTRAINT "unique_session_device_id" UNIQUE (id) +); + +INSERT INTO _session_devices_tmp + (id, identity_id, session_id, ip_address, user_agent, "location", nid, created_at, updated_at) +SELECT + sd.id, s.identity_id, sd.session_id, sd.ip_address, sd.user_agent, sd.location, sd.nid, sd.created_at, sd.updated_at +FROM session_devices sd + JOIN sessions s + ON s.id = sd.session_id AND s.nid = sd.nid; + +DROP TABLE "session_devices"; +ALTER TABLE "_session_devices_tmp" RENAME TO "session_devices"; diff --git a/persistence/sql/persister.go b/persistence/sql/persister.go index 99990b7ace91..8e393ca11078 100644 --- a/persistence/sql/persister.go +++ b/persistence/sql/persister.go @@ -11,7 +11,6 @@ import ( "github.com/gobuffalo/pop/v6" "github.com/gofrs/uuid" - "github.com/laher/mergefs" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -20,10 +19,13 @@ import ( "github.com/ory/kratos/persistence" "github.com/ory/kratos/persistence/sql/devices" idpersistence "github.com/ory/kratos/persistence/sql/identity" + "github.com/ory/kratos/persistence/sql/migrations/gomigrations" "github.com/ory/kratos/schema" "github.com/ory/kratos/session" "github.com/ory/kratos/x" + "github.com/ory/kratos/x/slices" "github.com/ory/x/contextx" + "github.com/ory/x/fsx" "github.com/ory/x/networkx" "github.com/ory/x/otelx" "github.com/ory/x/popx" @@ -57,8 +59,9 @@ type ( ) type persisterOptions struct { - extraMigrations []fs.FS - disableLogging bool + extraMigrations []fs.FS + extraGoMigrations popx.Migrations + disableLogging bool } type persisterOption func(o *persisterOptions) @@ -69,6 +72,12 @@ func WithExtraMigrations(fss ...fs.FS) persisterOption { } } +func WithExtraGoMigrations(ms ...popx.Migration) persisterOption { + return func(o *persisterOptions) { + o.extraGoMigrations = ms + } +} + func WithDisabledLogging(v bool) persisterOption { return func(o *persisterOptions) { o.disableLogging = v @@ -85,15 +94,13 @@ func NewPersister(ctx context.Context, r persisterDependencies, c *pop.Connectio logger.Logrus().SetLevel(logrus.WarnLevel) } m, err := popx.NewMigrationBox( - mergefs.Merge( - append( - []fs.FS{ - migrations, networkx.Migrations, - }, - o.extraMigrations..., - )..., - ), + fsx.Merge(append([]fs.FS{migrations, networkx.Migrations}, o.extraMigrations...)...), popx.NewMigrator(c, logger, r.Tracer(ctx), 0), + popx.WithGoMigrations(slices.Concat( + gomigrations.IdentityPrimaryKeysStep1, + gomigrations.IdentityPrimaryKeysStep2, + o.extraGoMigrations, + )), ) if err != nil { return nil, err diff --git a/persistence/sql/persister_session.go b/persistence/sql/persister_session.go index 7cbd968f50a2..6803dda4163c 100644 --- a/persistence/sql/persister_session.go +++ b/persistence/sql/persister_session.go @@ -183,7 +183,11 @@ func (p *Persister) UpsertSession(ctx context.Context, s *session.Session) (err ctx, span := p.r.Tracer(ctx).Tracer().Start(ctx, "persistence.sql.UpsertSession") defer otelx.End(span, &err) + s.IdentityID = s.Identity.ID s.NID = p.NetworkID(ctx) + if s.NID != s.Identity.NID { + return errors.New("nid mismatch") + } return errors.WithStack(p.Transaction(ctx, func(ctx context.Context, tx *pop.Connection) error { exists := false @@ -213,6 +217,7 @@ func (p *Persister) UpsertSession(ctx context.Context, s *session.Session) (err for i := range s.Devices { device := &(s.Devices[i]) device.SessionID = s.ID + device.IdentityID = s.IdentityID device.NID = s.NID if device.Location != nil { diff --git a/session/session.go b/session/session.go index 84f64ceec0d6..42a2a238f1c7 100644 --- a/session/session.go +++ b/session/session.go @@ -49,6 +49,9 @@ type Device struct { // SessionID is a helper struct field for gobuffalo.pop. SessionID uuid.UUID `json:"-" faker:"-" db:"session_id"` + // IdentityID is a helper struct field for gobuffalo.pop. + IdentityID uuid.UUID `json:"-" faker:"-" db:"identity_id"` + // IPAddress of the client IPAddress *string `json:"ip_address" faker:"ptr_ipv4" db:"ip_address"` @@ -281,8 +284,9 @@ func (s *Session) Activate(r *http.Request, i *identity.Identity, c lifespanProv func (s *Session) SetSessionDeviceInformation(r *http.Request) { device := Device{ - SessionID: s.ID, - IPAddress: stringsx.GetPointer(httpx.ClientIP(r)), + SessionID: s.ID, + IdentityID: s.IdentityID, + IPAddress: stringsx.GetPointer(httpx.ClientIP(r)), } agent := r.Header["User-Agent"] diff --git a/session/session_test.go b/session/session_test.go index 4e1efe3b647a..6f9123749e25 100644 --- a/session/session_test.go +++ b/session/session_test.go @@ -103,7 +103,8 @@ func TestSession(t *testing.T) { assert.Equal(t, identity.NoAuthenticatorAssuranceLevel, s.AuthenticatorAssuranceLevel) assert.Equal(t, authAt, s.AuthenticatedAt) assert.Equal(t, 1, len(s.Devices)) - assert.Equal(t, s.ID.String(), s.Devices[0].SessionID.String()) + assert.Equal(t, s.ID, s.Devices[0].SessionID) + assert.Equal(t, s.IdentityID, s.Devices[0].IdentityID) assert.Equal(t, tc.expected, *s.Devices[0].IPAddress) assert.Equal(t, "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36", *s.Devices[0].UserAgent) assert.Equal(t, "", *s.Devices[0].Location) @@ -123,9 +124,10 @@ func TestSession(t *testing.T) { assert.Equal(t, identity.NoAuthenticatorAssuranceLevel, s.AuthenticatorAssuranceLevel) assert.Equal(t, authAt, s.AuthenticatedAt) assert.Equal(t, 1, len(s.Devices)) - assert.Equal(t, s.ID.String(), s.Devices[0].SessionID.String()) - assert.NotNil(t, s.Devices[0].UpdatedAt) - assert.NotNil(t, s.Devices[0].CreatedAt) + assert.Equal(t, s.ID, s.Devices[0].SessionID) + assert.Equal(t, s.IdentityID, s.Devices[0].IdentityID) + assert.NotZero(t, s.Devices[0].UpdatedAt) + assert.NotZero(t, s.Devices[0].CreatedAt) assert.Equal(t, "54.155.246.155", *s.Devices[0].IPAddress) assert.Equal(t, "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36", *s.Devices[0].UserAgent) assert.Equal(t, "", *s.Devices[0].Location) @@ -143,9 +145,10 @@ func TestSession(t *testing.T) { assert.Equal(t, identity.NoAuthenticatorAssuranceLevel, s.AuthenticatorAssuranceLevel) assert.Equal(t, authAt, s.AuthenticatedAt) assert.Equal(t, 1, len(s.Devices)) - assert.Equal(t, s.ID.String(), s.Devices[0].SessionID.String()) - assert.NotNil(t, s.Devices[0].UpdatedAt) - assert.NotNil(t, s.Devices[0].CreatedAt) + assert.Equal(t, s.ID, s.Devices[0].SessionID) + assert.Equal(t, s.IdentityID, s.Devices[0].IdentityID) + assert.NotZero(t, s.Devices[0].UpdatedAt) + assert.NotZero(t, s.Devices[0].CreatedAt) assert.Equal(t, "54.155.246.155", *s.Devices[0].IPAddress) assert.Equal(t, "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36", *s.Devices[0].UserAgent) assert.Equal(t, "", *s.Devices[0].Location) @@ -164,7 +167,8 @@ func TestSession(t *testing.T) { assert.Equal(t, identity.NoAuthenticatorAssuranceLevel, s.AuthenticatorAssuranceLevel) assert.Equal(t, authAt, s.AuthenticatedAt) assert.Equal(t, 1, len(s.Devices)) - assert.Equal(t, s.ID.String(), s.Devices[0].SessionID.String()) + assert.Equal(t, s.ID, s.Devices[0].SessionID) + assert.Equal(t, s.IdentityID, s.Devices[0].IdentityID) assert.Equal(t, "54.155.246.232", *s.Devices[0].IPAddress) assert.Equal(t, "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36", *s.Devices[0].UserAgent) assert.Equal(t, "Munich, Germany", *s.Devices[0].Location) diff --git a/session/test/persistence.go b/session/test/persistence.go index 727cc744bdce..2f0e06155080 100644 --- a/session/test/persistence.go +++ b/session/test/persistence.go @@ -85,6 +85,7 @@ func TestPersister(ctx context.Context, conf *config.Config, p interface { for i, d := range actual { assert.Equal(t, expected.Devices[i].SessionID, d.SessionID) + assert.Equal(t, expected.Devices[i].IdentityID, d.IdentityID) assert.Equal(t, expected.Devices[i].NID, d.NID) assert.Equal(t, *expected.Devices[i].IPAddress, *d.IPAddress) assert.Equal(t, expected.Devices[i].UserAgent, d.UserAgent) @@ -144,11 +145,15 @@ func TestPersister(ctx context.Context, conf *config.Config, p interface { var identity2Session session.Session require.NoError(t, faker.FakeData(&identity2)) require.NoError(t, faker.FakeData(&identity2Session)) + require.Zero(t, identity2.ID) + require.Zero(t, identity2Session.ID) // Create seed identities _, l := testhelpers.NewNetwork(t, ctx, p) require.NoError(t, l.CreateIdentity(ctx, &identity1)) require.NoError(t, l.CreateIdentity(ctx, &identity2)) + require.NotZero(t, identity1.ID) + require.NotZero(t, identity2.ID) seedSessionIDs := make([]uuid.UUID, 5) seedSessionsList := make([]session.Session, 5) diff --git a/x/slices/concat.go b/x/slices/concat.go new file mode 100644 index 000000000000..bb59474eed52 --- /dev/null +++ b/x/slices/concat.go @@ -0,0 +1,27 @@ +// copied from https://cs.opensource.google/go/go/+/refs/tags/go1.22.0:src/slices/slices.go;l=501-515;drc=2551fffd2c06cf0655ebbbd11d9b1e70a5b2e9cb +// Delete when Kratos moves to Go 1.22 + +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package slices defines various functions useful with slices of any type. +package slices + +import slices_ "slices" + +// Concat returns a new slice concatenating the passed in slices. +func Concat[S ~[]E, E any](slices ...S) S { + size := 0 + for _, s := range slices { + size += len(s) + if size < 0 { + panic("len out of range") + } + } + newslice := slices_.Grow[S](nil, size) + for _, s := range slices { + newslice = append(newslice, s...) + } + return newslice +}