diff --git a/docs/docs/.static/api.json b/docs/docs/.static/api.json index 0003f42f350a..575340d8422c 100755 --- a/docs/docs/.static/api.json +++ b/docs/docs/.static/api.json @@ -2258,7 +2258,7 @@ }, "/recovery/link": { "post": { - "description": "This endpoint creates a recovery link which should be given to the user in order for them to recover\n(or activate) their account.", + "description": "This endpoint creates a recovery link which should be given to the user in order for them to recover\n(or activate) their account. It's possible to generate a link for an account without a recovery address.", "operationId": "adminCreateSelfServiceRecoveryLink", "requestBody": { "content": { diff --git a/docs/docs/admin/managing-users-identities.mdx b/docs/docs/admin/managing-users-identities.mdx index 1d289aabf968..2384ed203f91 100644 --- a/docs/docs/admin/managing-users-identities.mdx +++ b/docs/docs/admin/managing-users-identities.mdx @@ -61,7 +61,10 @@ to use for this user. Similarly, the trait key/values depend on your schema as well. The command shown does not create a password for the identity or any other type of credential. Instead, we will use another REST call to create a recovery link (here "invite link" is probably more appropriate, but the flow uses an -account recovery link). +account recovery link). It's possible to generate a link for an account without +a recovery address via the admin API, but if the recovery link expires the user +won't be able to reinitiate the flow by himself as long as the recovery address +has been added. To create the account recovery link, use: diff --git a/persistence/sql/migratest/fixtures/recovery_token/1b667e6d-8fda-4194-a765-08185185d7e4.json b/persistence/sql/migratest/fixtures/recovery_token/1b667e6d-8fda-4194-a765-08185185d7e4.json index 02d118e1dcd3..3445efb4b86a 100644 --- a/persistence/sql/migratest/fixtures/recovery_token/1b667e6d-8fda-4194-a765-08185185d7e4.json +++ b/persistence/sql/migratest/fixtures/recovery_token/1b667e6d-8fda-4194-a765-08185185d7e4.json @@ -2,5 +2,6 @@ "id": "1b667e6d-8fda-4194-a765-08185185d7e4", "recovery_address": null, "expires_at": "2013-10-07T08:23:19Z", - "issued_at": "2013-10-07T08:23:19Z" + "issued_at": "2013-10-07T08:23:19Z", + "identity_id": "a251ebc2-880c-4f76-a8f3-38e6940eab0e" } diff --git a/persistence/sql/migratest/fixtures/recovery_token/5529d454-2946-404e-b681-d950f8657fd0.json b/persistence/sql/migratest/fixtures/recovery_token/5529d454-2946-404e-b681-d950f8657fd0.json index 6652f1443480..0442a81d848e 100644 --- a/persistence/sql/migratest/fixtures/recovery_token/5529d454-2946-404e-b681-d950f8657fd0.json +++ b/persistence/sql/migratest/fixtures/recovery_token/5529d454-2946-404e-b681-d950f8657fd0.json @@ -2,5 +2,6 @@ "id": "5529d454-2946-404e-b681-d950f8657fd0", "recovery_address": null, "expires_at": "2000-01-01T00:00:00Z", - "issued_at": "2000-01-01T00:00:00Z" + "issued_at": "2000-01-01T00:00:00Z", + "identity_id": "a251ebc2-880c-4f76-a8f3-38e6940eab0e" } diff --git a/persistence/sql/migratest/fixtures/recovery_token/77ca3f5c-cd39-488b-9f1d-cc7166d14bdc.json b/persistence/sql/migratest/fixtures/recovery_token/77ca3f5c-cd39-488b-9f1d-cc7166d14bdc.json index 1b3012339dc8..1f1c0a479af7 100644 --- a/persistence/sql/migratest/fixtures/recovery_token/77ca3f5c-cd39-488b-9f1d-cc7166d14bdc.json +++ b/persistence/sql/migratest/fixtures/recovery_token/77ca3f5c-cd39-488b-9f1d-cc7166d14bdc.json @@ -2,5 +2,6 @@ "id": "77ca3f5c-cd39-488b-9f1d-cc7166d14bdc", "recovery_address": null, "expires_at": "2000-01-01T00:00:00Z", - "issued_at": "2000-01-01T00:00:00Z" + "issued_at": "2000-01-01T00:00:00Z", + "identity_id": "a251ebc2-880c-4f76-a8f3-38e6940eab0e" } diff --git a/persistence/sql/migratest/testdata/20210913095309_testdata.sql b/persistence/sql/migratest/testdata/20210913095309_testdata.sql new file mode 100644 index 000000000000..c34e690b3983 --- /dev/null +++ b/persistence/sql/migratest/testdata/20210913095309_testdata.sql @@ -0,0 +1,4 @@ +UPDATE identity_recovery_tokens SET identity_id = 'a251ebc2-880c-4f76-a8f3-38e6940eab0e' WHERE id = '5529d454-2946-404e-b681-d950f8657fd0'; +UPDATE identity_recovery_tokens SET identity_id = 'a251ebc2-880c-4f76-a8f3-38e6940eab0e' WHERE id = '77ca3f5c-cd39-488b-9f1d-cc7166d14bdc'; +UPDATE identity_recovery_tokens SET identity_id = 'a251ebc2-880c-4f76-a8f3-38e6940eab0e' WHERE id = '1b667e6d-8fda-4194-a765-08185185d7e4'; + diff --git a/persistence/sql/migrations/sql/20210913095309000000_identity_recovery_tokens.cockroach.down.sql b/persistence/sql/migrations/sql/20210913095309000000_identity_recovery_tokens.cockroach.down.sql new file mode 100644 index 000000000000..cedce356c5f4 --- /dev/null +++ b/persistence/sql/migrations/sql/20210913095309000000_identity_recovery_tokens.cockroach.down.sql @@ -0,0 +1 @@ +ALTER TABLE "identity_recovery_tokens" DROP COLUMN "identity_id"; \ No newline at end of file diff --git a/persistence/sql/migrations/sql/20210913095309000000_identity_recovery_tokens.cockroach.up.sql b/persistence/sql/migrations/sql/20210913095309000000_identity_recovery_tokens.cockroach.up.sql new file mode 100644 index 000000000000..575a9eaf4ed7 --- /dev/null +++ b/persistence/sql/migrations/sql/20210913095309000000_identity_recovery_tokens.cockroach.up.sql @@ -0,0 +1 @@ +ALTER TABLE "identity_recovery_tokens" DROP CONSTRAINT "identity_recovery_tokens_identity_recovery_addresses_id_fk"; \ No newline at end of file diff --git a/persistence/sql/migrations/sql/20210913095309000000_identity_recovery_tokens.mysql.down.sql b/persistence/sql/migrations/sql/20210913095309000000_identity_recovery_tokens.mysql.down.sql new file mode 100644 index 000000000000..81cbf64a745e --- /dev/null +++ b/persistence/sql/migrations/sql/20210913095309000000_identity_recovery_tokens.mysql.down.sql @@ -0,0 +1 @@ +ALTER TABLE `identity_recovery_tokens` DROP COLUMN `identity_id`; \ No newline at end of file diff --git a/persistence/sql/migrations/sql/20210913095309000000_identity_recovery_tokens.mysql.up.sql b/persistence/sql/migrations/sql/20210913095309000000_identity_recovery_tokens.mysql.up.sql new file mode 100644 index 000000000000..462174b48a78 --- /dev/null +++ b/persistence/sql/migrations/sql/20210913095309000000_identity_recovery_tokens.mysql.up.sql @@ -0,0 +1 @@ +ALTER TABLE `identity_recovery_tokens` MODIFY `identity_recovery_address_id` char(36); \ No newline at end of file diff --git a/persistence/sql/migrations/sql/20210913095309000000_identity_recovery_tokens.postgres.down.sql b/persistence/sql/migrations/sql/20210913095309000000_identity_recovery_tokens.postgres.down.sql new file mode 100644 index 000000000000..cedce356c5f4 --- /dev/null +++ b/persistence/sql/migrations/sql/20210913095309000000_identity_recovery_tokens.postgres.down.sql @@ -0,0 +1 @@ +ALTER TABLE "identity_recovery_tokens" DROP COLUMN "identity_id"; \ No newline at end of file diff --git a/persistence/sql/migrations/sql/20210913095309000000_identity_recovery_tokens.postgres.up.sql b/persistence/sql/migrations/sql/20210913095309000000_identity_recovery_tokens.postgres.up.sql new file mode 100644 index 000000000000..df4509bf5869 --- /dev/null +++ b/persistence/sql/migrations/sql/20210913095309000000_identity_recovery_tokens.postgres.up.sql @@ -0,0 +1 @@ +ALTER TABLE "identity_recovery_tokens" ALTER COLUMN "identity_recovery_address_id" TYPE UUID, ALTER COLUMN "identity_recovery_address_id" DROP NOT NULL; \ No newline at end of file diff --git a/persistence/sql/migrations/sql/20210913095309000000_identity_recovery_tokens.sqlite3.down.sql b/persistence/sql/migrations/sql/20210913095309000000_identity_recovery_tokens.sqlite3.down.sql new file mode 100644 index 000000000000..1ebb1c9fdfcd --- /dev/null +++ b/persistence/sql/migrations/sql/20210913095309000000_identity_recovery_tokens.sqlite3.down.sql @@ -0,0 +1 @@ +ALTER TABLE "_identity_recovery_tokens_tmp" RENAME TO "identity_recovery_tokens"; \ No newline at end of file diff --git a/persistence/sql/migrations/sql/20210913095309000000_identity_recovery_tokens.sqlite3.up.sql b/persistence/sql/migrations/sql/20210913095309000000_identity_recovery_tokens.sqlite3.up.sql new file mode 100644 index 000000000000..bd7b1220f2f8 --- /dev/null +++ b/persistence/sql/migrations/sql/20210913095309000000_identity_recovery_tokens.sqlite3.up.sql @@ -0,0 +1 @@ +DROP INDEX IF EXISTS "identity_recovery_tokens_nid_idx"; \ No newline at end of file diff --git a/persistence/sql/migrations/sql/20210913095309000001_identity_recovery_tokens.cockroach.down.sql b/persistence/sql/migrations/sql/20210913095309000001_identity_recovery_tokens.cockroach.down.sql new file mode 100644 index 000000000000..574e76ed18e1 --- /dev/null +++ b/persistence/sql/migrations/sql/20210913095309000001_identity_recovery_tokens.cockroach.down.sql @@ -0,0 +1 @@ +ALTER TABLE "identity_recovery_tokens" DROP CONSTRAINT "identity_recovery_tokens_identity_id_fk_idx"; \ No newline at end of file diff --git a/persistence/sql/migrations/sql/20210913095309000001_identity_recovery_tokens.cockroach.up.sql b/persistence/sql/migrations/sql/20210913095309000001_identity_recovery_tokens.cockroach.up.sql new file mode 100644 index 000000000000..f87c72536715 --- /dev/null +++ b/persistence/sql/migrations/sql/20210913095309000001_identity_recovery_tokens.cockroach.up.sql @@ -0,0 +1 @@ +ALTER TABLE "identity_recovery_tokens" RENAME COLUMN "identity_recovery_address_id" TO "_identity_recovery_address_id_tmp"; \ No newline at end of file diff --git a/persistence/sql/migrations/sql/20210913095309000001_identity_recovery_tokens.mysql.down.sql b/persistence/sql/migrations/sql/20210913095309000001_identity_recovery_tokens.mysql.down.sql new file mode 100644 index 000000000000..d54e7d289790 --- /dev/null +++ b/persistence/sql/migrations/sql/20210913095309000001_identity_recovery_tokens.mysql.down.sql @@ -0,0 +1 @@ +ALTER TABLE `identity_recovery_tokens` DROP FOREIGN KEY `identity_recovery_tokens_identity_id_fk_idx`; \ No newline at end of file diff --git a/persistence/sql/migrations/sql/20210913095309000001_identity_recovery_tokens.mysql.up.sql b/persistence/sql/migrations/sql/20210913095309000001_identity_recovery_tokens.mysql.up.sql new file mode 100644 index 000000000000..ece79987ea44 --- /dev/null +++ b/persistence/sql/migrations/sql/20210913095309000001_identity_recovery_tokens.mysql.up.sql @@ -0,0 +1 @@ +ALTER TABLE `identity_recovery_tokens` ADD COLUMN `identity_id` char(36); \ No newline at end of file diff --git a/persistence/sql/migrations/sql/20210913095309000001_identity_recovery_tokens.postgres.down.sql b/persistence/sql/migrations/sql/20210913095309000001_identity_recovery_tokens.postgres.down.sql new file mode 100644 index 000000000000..574e76ed18e1 --- /dev/null +++ b/persistence/sql/migrations/sql/20210913095309000001_identity_recovery_tokens.postgres.down.sql @@ -0,0 +1 @@ +ALTER TABLE "identity_recovery_tokens" DROP CONSTRAINT "identity_recovery_tokens_identity_id_fk_idx"; \ No newline at end of file diff --git a/persistence/sql/migrations/sql/20210913095309000001_identity_recovery_tokens.postgres.up.sql b/persistence/sql/migrations/sql/20210913095309000001_identity_recovery_tokens.postgres.up.sql new file mode 100644 index 000000000000..5cfffe9524ca --- /dev/null +++ b/persistence/sql/migrations/sql/20210913095309000001_identity_recovery_tokens.postgres.up.sql @@ -0,0 +1 @@ +ALTER TABLE "identity_recovery_tokens" ADD COLUMN "identity_id" UUID; \ No newline at end of file diff --git a/persistence/sql/migrations/sql/20210913095309000001_identity_recovery_tokens.sqlite3.down.sql b/persistence/sql/migrations/sql/20210913095309000001_identity_recovery_tokens.sqlite3.down.sql new file mode 100644 index 000000000000..40f190843c16 --- /dev/null +++ b/persistence/sql/migrations/sql/20210913095309000001_identity_recovery_tokens.sqlite3.down.sql @@ -0,0 +1,2 @@ + +DROP TABLE "identity_recovery_tokens"; \ No newline at end of file diff --git a/persistence/sql/migrations/sql/20210913095309000001_identity_recovery_tokens.sqlite3.up.sql b/persistence/sql/migrations/sql/20210913095309000001_identity_recovery_tokens.sqlite3.up.sql new file mode 100644 index 000000000000..fc7fe837896a --- /dev/null +++ b/persistence/sql/migrations/sql/20210913095309000001_identity_recovery_tokens.sqlite3.up.sql @@ -0,0 +1 @@ +DROP INDEX IF EXISTS "identity_recovery_addresses_code_idx"; \ No newline at end of file diff --git a/persistence/sql/migrations/sql/20210913095309000002_identity_recovery_tokens.cockroach.down.sql b/persistence/sql/migrations/sql/20210913095309000002_identity_recovery_tokens.cockroach.down.sql new file mode 100644 index 000000000000..062bbc0190c2 --- /dev/null +++ b/persistence/sql/migrations/sql/20210913095309000002_identity_recovery_tokens.cockroach.down.sql @@ -0,0 +1 @@ +ALTER TABLE "identity_recovery_tokens" ADD CONSTRAINT "identity_recovery_tokens_identity_recovery_addresses_id_fk" FOREIGN KEY ("identity_recovery_address_id") REFERENCES "identity_recovery_addresses" ("id") ON UPDATE NO ACTION ON DELETE CASCADE; \ No newline at end of file diff --git a/persistence/sql/migrations/sql/20210913095309000002_identity_recovery_tokens.cockroach.up.sql b/persistence/sql/migrations/sql/20210913095309000002_identity_recovery_tokens.cockroach.up.sql new file mode 100644 index 000000000000..87e9e3fe13c8 --- /dev/null +++ b/persistence/sql/migrations/sql/20210913095309000002_identity_recovery_tokens.cockroach.up.sql @@ -0,0 +1 @@ +ALTER TABLE "identity_recovery_tokens" ADD COLUMN "identity_recovery_address_id" UUID; \ No newline at end of file diff --git a/persistence/sql/migrations/sql/20210913095309000002_identity_recovery_tokens.mysql.down.sql b/persistence/sql/migrations/sql/20210913095309000002_identity_recovery_tokens.mysql.down.sql new file mode 100644 index 000000000000..46ed80101613 --- /dev/null +++ b/persistence/sql/migrations/sql/20210913095309000002_identity_recovery_tokens.mysql.down.sql @@ -0,0 +1 @@ +ALTER TABLE `identity_recovery_tokens` MODIFY `identity_recovery_address_id` char(36) NOT NULL; \ No newline at end of file diff --git a/persistence/sql/migrations/sql/20210913095309000002_identity_recovery_tokens.mysql.up.sql b/persistence/sql/migrations/sql/20210913095309000002_identity_recovery_tokens.mysql.up.sql new file mode 100644 index 000000000000..48d951bae394 --- /dev/null +++ b/persistence/sql/migrations/sql/20210913095309000002_identity_recovery_tokens.mysql.up.sql @@ -0,0 +1 @@ +UPDATE identity_recovery_tokens SET identity_id=(SELECT identity_id FROM identity_recovery_addresses WHERE id=identity_recovery_address_id) WHERE identity_id = '' OR identity_id IS NULL; \ No newline at end of file diff --git a/persistence/sql/migrations/sql/20210913095309000002_identity_recovery_tokens.postgres.down.sql b/persistence/sql/migrations/sql/20210913095309000002_identity_recovery_tokens.postgres.down.sql new file mode 100644 index 000000000000..5fa2db460f60 --- /dev/null +++ b/persistence/sql/migrations/sql/20210913095309000002_identity_recovery_tokens.postgres.down.sql @@ -0,0 +1 @@ +ALTER TABLE "identity_recovery_tokens" ALTER COLUMN "identity_recovery_address_id" TYPE UUID, ALTER COLUMN "identity_recovery_address_id" SET NOT NULL; \ No newline at end of file diff --git a/persistence/sql/migrations/sql/20210913095309000002_identity_recovery_tokens.postgres.up.sql b/persistence/sql/migrations/sql/20210913095309000002_identity_recovery_tokens.postgres.up.sql new file mode 100644 index 000000000000..20ba4845f7d4 --- /dev/null +++ b/persistence/sql/migrations/sql/20210913095309000002_identity_recovery_tokens.postgres.up.sql @@ -0,0 +1 @@ +UPDATE identity_recovery_tokens SET identity_id=(SELECT identity_id FROM identity_recovery_addresses WHERE id=identity_recovery_address_id) WHERE identity_id = '00000000-0000-0000-0000-000000000000' OR identity_id IS NULL; \ No newline at end of file diff --git a/persistence/sql/migrations/sql/20210913095309000002_identity_recovery_tokens.sqlite3.down.sql b/persistence/sql/migrations/sql/20210913095309000002_identity_recovery_tokens.sqlite3.down.sql new file mode 100644 index 000000000000..c367ddec440b --- /dev/null +++ b/persistence/sql/migrations/sql/20210913095309000002_identity_recovery_tokens.sqlite3.down.sql @@ -0,0 +1 @@ +INSERT INTO "_identity_recovery_tokens_tmp" (id, token, used, used_at, identity_recovery_address_id, selfservice_recovery_flow_id, created_at, updated_at, expires_at, issued_at, nid) SELECT id, token, used, used_at, identity_recovery_address_id, selfservice_recovery_flow_id, created_at, updated_at, expires_at, issued_at, nid FROM "identity_recovery_tokens"; \ No newline at end of file diff --git a/persistence/sql/migrations/sql/20210913095309000002_identity_recovery_tokens.sqlite3.up.sql b/persistence/sql/migrations/sql/20210913095309000002_identity_recovery_tokens.sqlite3.up.sql new file mode 100644 index 000000000000..03159a3cd7a1 --- /dev/null +++ b/persistence/sql/migrations/sql/20210913095309000002_identity_recovery_tokens.sqlite3.up.sql @@ -0,0 +1 @@ +DROP INDEX IF EXISTS "identity_recovery_addresses_code_uq_idx"; \ No newline at end of file diff --git a/persistence/sql/migrations/sql/20210913095309000003_identity_recovery_tokens.cockroach.down.sql b/persistence/sql/migrations/sql/20210913095309000003_identity_recovery_tokens.cockroach.down.sql new file mode 100644 index 000000000000..1acf8f5d3829 --- /dev/null +++ b/persistence/sql/migrations/sql/20210913095309000003_identity_recovery_tokens.cockroach.down.sql @@ -0,0 +1 @@ +ALTER TABLE "identity_recovery_tokens" DROP COLUMN "_identity_recovery_address_id_tmp"; \ No newline at end of file diff --git a/persistence/sql/migrations/sql/20210913095309000003_identity_recovery_tokens.cockroach.up.sql b/persistence/sql/migrations/sql/20210913095309000003_identity_recovery_tokens.cockroach.up.sql new file mode 100644 index 000000000000..87706a1f442a --- /dev/null +++ b/persistence/sql/migrations/sql/20210913095309000003_identity_recovery_tokens.cockroach.up.sql @@ -0,0 +1 @@ +UPDATE "identity_recovery_tokens" SET "identity_recovery_address_id" = "_identity_recovery_address_id_tmp"; \ No newline at end of file diff --git a/persistence/sql/migrations/sql/20210913095309000003_identity_recovery_tokens.mysql.down.sql b/persistence/sql/migrations/sql/20210913095309000003_identity_recovery_tokens.mysql.down.sql new file mode 100644 index 000000000000..5e215d68e86f --- /dev/null +++ b/persistence/sql/migrations/sql/20210913095309000003_identity_recovery_tokens.mysql.down.sql @@ -0,0 +1 @@ +DELETE FROM identity_recovery_tokens WHERE identity_recovery_address_id IS NULL; \ No newline at end of file diff --git a/persistence/sql/migrations/sql/20210913095309000003_identity_recovery_tokens.mysql.up.sql b/persistence/sql/migrations/sql/20210913095309000003_identity_recovery_tokens.mysql.up.sql new file mode 100644 index 000000000000..c3a24ca20ed8 --- /dev/null +++ b/persistence/sql/migrations/sql/20210913095309000003_identity_recovery_tokens.mysql.up.sql @@ -0,0 +1 @@ +ALTER TABLE `identity_recovery_tokens` MODIFY `identity_id` char(36) NOT NULL; \ No newline at end of file diff --git a/persistence/sql/migrations/sql/20210913095309000003_identity_recovery_tokens.postgres.down.sql b/persistence/sql/migrations/sql/20210913095309000003_identity_recovery_tokens.postgres.down.sql new file mode 100644 index 000000000000..5e215d68e86f --- /dev/null +++ b/persistence/sql/migrations/sql/20210913095309000003_identity_recovery_tokens.postgres.down.sql @@ -0,0 +1 @@ +DELETE FROM identity_recovery_tokens WHERE identity_recovery_address_id IS NULL; \ No newline at end of file diff --git a/persistence/sql/migrations/sql/20210913095309000003_identity_recovery_tokens.postgres.up.sql b/persistence/sql/migrations/sql/20210913095309000003_identity_recovery_tokens.postgres.up.sql new file mode 100644 index 000000000000..de2bef999e83 --- /dev/null +++ b/persistence/sql/migrations/sql/20210913095309000003_identity_recovery_tokens.postgres.up.sql @@ -0,0 +1 @@ +ALTER TABLE "identity_recovery_tokens" ALTER COLUMN "identity_id" TYPE UUID, ALTER COLUMN "identity_id" SET NOT NULL; \ No newline at end of file diff --git a/persistence/sql/migrations/sql/20210913095309000003_identity_recovery_tokens.sqlite3.down.sql b/persistence/sql/migrations/sql/20210913095309000003_identity_recovery_tokens.sqlite3.down.sql new file mode 100644 index 000000000000..95955abf07ed --- /dev/null +++ b/persistence/sql/migrations/sql/20210913095309000003_identity_recovery_tokens.sqlite3.down.sql @@ -0,0 +1 @@ +CREATE INDEX "identity_recovery_tokens_nid_idx" ON "_identity_recovery_tokens_tmp" (id, nid); \ No newline at end of file diff --git a/persistence/sql/migrations/sql/20210913095309000003_identity_recovery_tokens.sqlite3.up.sql b/persistence/sql/migrations/sql/20210913095309000003_identity_recovery_tokens.sqlite3.up.sql new file mode 100644 index 000000000000..2adb379e258e --- /dev/null +++ b/persistence/sql/migrations/sql/20210913095309000003_identity_recovery_tokens.sqlite3.up.sql @@ -0,0 +1,15 @@ +CREATE TABLE "_identity_recovery_tokens_tmp" ( +"id" TEXT PRIMARY KEY, +"token" TEXT NOT NULL, +"used" bool NOT NULL DEFAULT 'false', +"used_at" DATETIME, +"identity_recovery_address_id" char(36), +"selfservice_recovery_flow_id" char(36), +"created_at" DATETIME NOT NULL, +"updated_at" DATETIME NOT NULL, +"expires_at" DATETIME NOT NULL DEFAULT '2000-01-01 00:00:00', +"issued_at" DATETIME NOT NULL DEFAULT '2000-01-01 00:00:00', +"nid" char(36), +FOREIGN KEY (selfservice_recovery_flow_id) REFERENCES selfservice_recovery_flows (id) ON UPDATE NO ACTION ON DELETE CASCADE, +FOREIGN KEY (identity_recovery_address_id) REFERENCES identity_recovery_addresses (id) ON UPDATE NO ACTION ON DELETE CASCADE +); \ No newline at end of file diff --git a/persistence/sql/migrations/sql/20210913095309000004_identity_recovery_tokens.cockroach.down.sql b/persistence/sql/migrations/sql/20210913095309000004_identity_recovery_tokens.cockroach.down.sql new file mode 100644 index 000000000000..ac2e73802929 --- /dev/null +++ b/persistence/sql/migrations/sql/20210913095309000004_identity_recovery_tokens.cockroach.down.sql @@ -0,0 +1 @@ +ALTER TABLE "identity_recovery_tokens" ALTER COLUMN "identity_recovery_address_id" SET NOT NULL; \ No newline at end of file diff --git a/persistence/sql/migrations/sql/20210913095309000004_identity_recovery_tokens.cockroach.up.sql b/persistence/sql/migrations/sql/20210913095309000004_identity_recovery_tokens.cockroach.up.sql new file mode 100644 index 000000000000..1acf8f5d3829 --- /dev/null +++ b/persistence/sql/migrations/sql/20210913095309000004_identity_recovery_tokens.cockroach.up.sql @@ -0,0 +1 @@ +ALTER TABLE "identity_recovery_tokens" DROP COLUMN "_identity_recovery_address_id_tmp"; \ No newline at end of file diff --git a/persistence/sql/migrations/sql/20210913095309000004_identity_recovery_tokens.mysql.down.sql b/persistence/sql/migrations/sql/20210913095309000004_identity_recovery_tokens.mysql.down.sql new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/persistence/sql/migrations/sql/20210913095309000004_identity_recovery_tokens.mysql.up.sql b/persistence/sql/migrations/sql/20210913095309000004_identity_recovery_tokens.mysql.up.sql new file mode 100644 index 000000000000..e641159c4ac4 --- /dev/null +++ b/persistence/sql/migrations/sql/20210913095309000004_identity_recovery_tokens.mysql.up.sql @@ -0,0 +1 @@ +ALTER TABLE `identity_recovery_tokens` ADD CONSTRAINT `identity_recovery_tokens_identity_id_fk_idx` FOREIGN KEY (`identity_id`) REFERENCES `identities` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE; \ No newline at end of file diff --git a/persistence/sql/migrations/sql/20210913095309000004_identity_recovery_tokens.postgres.down.sql b/persistence/sql/migrations/sql/20210913095309000004_identity_recovery_tokens.postgres.down.sql new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/persistence/sql/migrations/sql/20210913095309000004_identity_recovery_tokens.postgres.up.sql b/persistence/sql/migrations/sql/20210913095309000004_identity_recovery_tokens.postgres.up.sql new file mode 100644 index 000000000000..45a3a3a47558 --- /dev/null +++ b/persistence/sql/migrations/sql/20210913095309000004_identity_recovery_tokens.postgres.up.sql @@ -0,0 +1 @@ +ALTER TABLE "identity_recovery_tokens" ADD CONSTRAINT "identity_recovery_tokens_identity_id_fk_idx" FOREIGN KEY ("identity_id") REFERENCES "identities" ("id") ON UPDATE RESTRICT ON DELETE CASCADE; \ No newline at end of file diff --git a/persistence/sql/migrations/sql/20210913095309000004_identity_recovery_tokens.sqlite3.down.sql b/persistence/sql/migrations/sql/20210913095309000004_identity_recovery_tokens.sqlite3.down.sql new file mode 100644 index 000000000000..d26632345e93 --- /dev/null +++ b/persistence/sql/migrations/sql/20210913095309000004_identity_recovery_tokens.sqlite3.down.sql @@ -0,0 +1 @@ +CREATE INDEX "identity_recovery_addresses_code_idx" ON "_identity_recovery_tokens_tmp" (token); \ No newline at end of file diff --git a/persistence/sql/migrations/sql/20210913095309000004_identity_recovery_tokens.sqlite3.up.sql b/persistence/sql/migrations/sql/20210913095309000004_identity_recovery_tokens.sqlite3.up.sql new file mode 100644 index 000000000000..95955abf07ed --- /dev/null +++ b/persistence/sql/migrations/sql/20210913095309000004_identity_recovery_tokens.sqlite3.up.sql @@ -0,0 +1 @@ +CREATE INDEX "identity_recovery_tokens_nid_idx" ON "_identity_recovery_tokens_tmp" (id, nid); \ No newline at end of file diff --git a/persistence/sql/migrations/sql/20210913095309000005_identity_recovery_tokens.cockroach.down.sql b/persistence/sql/migrations/sql/20210913095309000005_identity_recovery_tokens.cockroach.down.sql new file mode 100644 index 000000000000..87706a1f442a --- /dev/null +++ b/persistence/sql/migrations/sql/20210913095309000005_identity_recovery_tokens.cockroach.down.sql @@ -0,0 +1 @@ +UPDATE "identity_recovery_tokens" SET "identity_recovery_address_id" = "_identity_recovery_address_id_tmp"; \ No newline at end of file diff --git a/persistence/sql/migrations/sql/20210913095309000005_identity_recovery_tokens.cockroach.up.sql b/persistence/sql/migrations/sql/20210913095309000005_identity_recovery_tokens.cockroach.up.sql new file mode 100644 index 000000000000..062bbc0190c2 --- /dev/null +++ b/persistence/sql/migrations/sql/20210913095309000005_identity_recovery_tokens.cockroach.up.sql @@ -0,0 +1 @@ +ALTER TABLE "identity_recovery_tokens" ADD CONSTRAINT "identity_recovery_tokens_identity_recovery_addresses_id_fk" FOREIGN KEY ("identity_recovery_address_id") REFERENCES "identity_recovery_addresses" ("id") ON UPDATE NO ACTION ON DELETE CASCADE; \ No newline at end of file diff --git a/persistence/sql/migrations/sql/20210913095309000005_identity_recovery_tokens.sqlite3.down.sql b/persistence/sql/migrations/sql/20210913095309000005_identity_recovery_tokens.sqlite3.down.sql new file mode 100644 index 000000000000..5da22e3522c7 --- /dev/null +++ b/persistence/sql/migrations/sql/20210913095309000005_identity_recovery_tokens.sqlite3.down.sql @@ -0,0 +1 @@ +CREATE UNIQUE INDEX "identity_recovery_addresses_code_uq_idx" ON "_identity_recovery_tokens_tmp" (token); \ No newline at end of file diff --git a/persistence/sql/migrations/sql/20210913095309000005_identity_recovery_tokens.sqlite3.up.sql b/persistence/sql/migrations/sql/20210913095309000005_identity_recovery_tokens.sqlite3.up.sql new file mode 100644 index 000000000000..d26632345e93 --- /dev/null +++ b/persistence/sql/migrations/sql/20210913095309000005_identity_recovery_tokens.sqlite3.up.sql @@ -0,0 +1 @@ +CREATE INDEX "identity_recovery_addresses_code_idx" ON "_identity_recovery_tokens_tmp" (token); \ No newline at end of file diff --git a/persistence/sql/migrations/sql/20210913095309000006_identity_recovery_tokens.cockroach.down.sql b/persistence/sql/migrations/sql/20210913095309000006_identity_recovery_tokens.cockroach.down.sql new file mode 100644 index 000000000000..87e9e3fe13c8 --- /dev/null +++ b/persistence/sql/migrations/sql/20210913095309000006_identity_recovery_tokens.cockroach.down.sql @@ -0,0 +1 @@ +ALTER TABLE "identity_recovery_tokens" ADD COLUMN "identity_recovery_address_id" UUID; \ No newline at end of file diff --git a/persistence/sql/migrations/sql/20210913095309000006_identity_recovery_tokens.cockroach.up.sql b/persistence/sql/migrations/sql/20210913095309000006_identity_recovery_tokens.cockroach.up.sql new file mode 100644 index 000000000000..5cfffe9524ca --- /dev/null +++ b/persistence/sql/migrations/sql/20210913095309000006_identity_recovery_tokens.cockroach.up.sql @@ -0,0 +1 @@ +ALTER TABLE "identity_recovery_tokens" ADD COLUMN "identity_id" UUID; \ No newline at end of file diff --git a/persistence/sql/migrations/sql/20210913095309000006_identity_recovery_tokens.sqlite3.down.sql b/persistence/sql/migrations/sql/20210913095309000006_identity_recovery_tokens.sqlite3.down.sql new file mode 100644 index 000000000000..9a18fbd72d39 --- /dev/null +++ b/persistence/sql/migrations/sql/20210913095309000006_identity_recovery_tokens.sqlite3.down.sql @@ -0,0 +1,15 @@ +CREATE TABLE "_identity_recovery_tokens_tmp" ( +"id" TEXT PRIMARY KEY, +"token" TEXT NOT NULL, +"used" bool NOT NULL DEFAULT 'false', +"used_at" DATETIME, +"identity_recovery_address_id" char(36) NOT NULL, +"selfservice_recovery_flow_id" char(36), +"created_at" DATETIME NOT NULL, +"updated_at" DATETIME NOT NULL, +"expires_at" DATETIME NOT NULL DEFAULT '2000-01-01 00:00:00', +"issued_at" DATETIME NOT NULL DEFAULT '2000-01-01 00:00:00', +"nid" char(36), +FOREIGN KEY (identity_recovery_address_id) REFERENCES identity_recovery_addresses (id) ON UPDATE NO ACTION ON DELETE CASCADE, +FOREIGN KEY (selfservice_recovery_flow_id) REFERENCES selfservice_recovery_flows (id) ON UPDATE NO ACTION ON DELETE CASCADE +); \ No newline at end of file diff --git a/persistence/sql/migrations/sql/20210913095309000006_identity_recovery_tokens.sqlite3.up.sql b/persistence/sql/migrations/sql/20210913095309000006_identity_recovery_tokens.sqlite3.up.sql new file mode 100644 index 000000000000..5da22e3522c7 --- /dev/null +++ b/persistence/sql/migrations/sql/20210913095309000006_identity_recovery_tokens.sqlite3.up.sql @@ -0,0 +1 @@ +CREATE UNIQUE INDEX "identity_recovery_addresses_code_uq_idx" ON "_identity_recovery_tokens_tmp" (token); \ No newline at end of file diff --git a/persistence/sql/migrations/sql/20210913095309000007_identity_recovery_tokens.cockroach.down.sql b/persistence/sql/migrations/sql/20210913095309000007_identity_recovery_tokens.cockroach.down.sql new file mode 100644 index 000000000000..f87c72536715 --- /dev/null +++ b/persistence/sql/migrations/sql/20210913095309000007_identity_recovery_tokens.cockroach.down.sql @@ -0,0 +1 @@ +ALTER TABLE "identity_recovery_tokens" RENAME COLUMN "identity_recovery_address_id" TO "_identity_recovery_address_id_tmp"; \ No newline at end of file diff --git a/persistence/sql/migrations/sql/20210913095309000007_identity_recovery_tokens.cockroach.up.sql b/persistence/sql/migrations/sql/20210913095309000007_identity_recovery_tokens.cockroach.up.sql new file mode 100644 index 000000000000..20ba4845f7d4 --- /dev/null +++ b/persistence/sql/migrations/sql/20210913095309000007_identity_recovery_tokens.cockroach.up.sql @@ -0,0 +1 @@ +UPDATE identity_recovery_tokens SET identity_id=(SELECT identity_id FROM identity_recovery_addresses WHERE id=identity_recovery_address_id) WHERE identity_id = '00000000-0000-0000-0000-000000000000' OR identity_id IS NULL; \ No newline at end of file diff --git a/persistence/sql/migrations/sql/20210913095309000007_identity_recovery_tokens.sqlite3.down.sql b/persistence/sql/migrations/sql/20210913095309000007_identity_recovery_tokens.sqlite3.down.sql new file mode 100644 index 000000000000..bd7b1220f2f8 --- /dev/null +++ b/persistence/sql/migrations/sql/20210913095309000007_identity_recovery_tokens.sqlite3.down.sql @@ -0,0 +1 @@ +DROP INDEX IF EXISTS "identity_recovery_tokens_nid_idx"; \ No newline at end of file diff --git a/persistence/sql/migrations/sql/20210913095309000007_identity_recovery_tokens.sqlite3.up.sql b/persistence/sql/migrations/sql/20210913095309000007_identity_recovery_tokens.sqlite3.up.sql new file mode 100644 index 000000000000..c367ddec440b --- /dev/null +++ b/persistence/sql/migrations/sql/20210913095309000007_identity_recovery_tokens.sqlite3.up.sql @@ -0,0 +1 @@ +INSERT INTO "_identity_recovery_tokens_tmp" (id, token, used, used_at, identity_recovery_address_id, selfservice_recovery_flow_id, created_at, updated_at, expires_at, issued_at, nid) SELECT id, token, used, used_at, identity_recovery_address_id, selfservice_recovery_flow_id, created_at, updated_at, expires_at, issued_at, nid FROM "identity_recovery_tokens"; \ No newline at end of file diff --git a/persistence/sql/migrations/sql/20210913095309000008_identity_recovery_tokens.cockroach.down.sql b/persistence/sql/migrations/sql/20210913095309000008_identity_recovery_tokens.cockroach.down.sql new file mode 100644 index 000000000000..575a9eaf4ed7 --- /dev/null +++ b/persistence/sql/migrations/sql/20210913095309000008_identity_recovery_tokens.cockroach.down.sql @@ -0,0 +1 @@ +ALTER TABLE "identity_recovery_tokens" DROP CONSTRAINT "identity_recovery_tokens_identity_recovery_addresses_id_fk"; \ No newline at end of file diff --git a/persistence/sql/migrations/sql/20210913095309000008_identity_recovery_tokens.cockroach.up.sql b/persistence/sql/migrations/sql/20210913095309000008_identity_recovery_tokens.cockroach.up.sql new file mode 100644 index 000000000000..35ed38e68bc4 --- /dev/null +++ b/persistence/sql/migrations/sql/20210913095309000008_identity_recovery_tokens.cockroach.up.sql @@ -0,0 +1 @@ +ALTER TABLE "identity_recovery_tokens" RENAME COLUMN "identity_id" TO "_identity_id_tmp"; \ No newline at end of file diff --git a/persistence/sql/migrations/sql/20210913095309000008_identity_recovery_tokens.sqlite3.down.sql b/persistence/sql/migrations/sql/20210913095309000008_identity_recovery_tokens.sqlite3.down.sql new file mode 100644 index 000000000000..fc7fe837896a --- /dev/null +++ b/persistence/sql/migrations/sql/20210913095309000008_identity_recovery_tokens.sqlite3.down.sql @@ -0,0 +1 @@ +DROP INDEX IF EXISTS "identity_recovery_addresses_code_idx"; \ No newline at end of file diff --git a/persistence/sql/migrations/sql/20210913095309000008_identity_recovery_tokens.sqlite3.up.sql b/persistence/sql/migrations/sql/20210913095309000008_identity_recovery_tokens.sqlite3.up.sql new file mode 100644 index 000000000000..7cda02822ec4 --- /dev/null +++ b/persistence/sql/migrations/sql/20210913095309000008_identity_recovery_tokens.sqlite3.up.sql @@ -0,0 +1 @@ +DROP TABLE "identity_recovery_tokens"; \ No newline at end of file diff --git a/persistence/sql/migrations/sql/20210913095309000009_identity_recovery_tokens.cockroach.down.sql b/persistence/sql/migrations/sql/20210913095309000009_identity_recovery_tokens.cockroach.down.sql new file mode 100644 index 000000000000..5e215d68e86f --- /dev/null +++ b/persistence/sql/migrations/sql/20210913095309000009_identity_recovery_tokens.cockroach.down.sql @@ -0,0 +1 @@ +DELETE FROM identity_recovery_tokens WHERE identity_recovery_address_id IS NULL; \ No newline at end of file diff --git a/persistence/sql/migrations/sql/20210913095309000009_identity_recovery_tokens.cockroach.up.sql b/persistence/sql/migrations/sql/20210913095309000009_identity_recovery_tokens.cockroach.up.sql new file mode 100644 index 000000000000..5cfffe9524ca --- /dev/null +++ b/persistence/sql/migrations/sql/20210913095309000009_identity_recovery_tokens.cockroach.up.sql @@ -0,0 +1 @@ +ALTER TABLE "identity_recovery_tokens" ADD COLUMN "identity_id" UUID; \ No newline at end of file diff --git a/persistence/sql/migrations/sql/20210913095309000009_identity_recovery_tokens.sqlite3.down.sql b/persistence/sql/migrations/sql/20210913095309000009_identity_recovery_tokens.sqlite3.down.sql new file mode 100644 index 000000000000..03159a3cd7a1 --- /dev/null +++ b/persistence/sql/migrations/sql/20210913095309000009_identity_recovery_tokens.sqlite3.down.sql @@ -0,0 +1 @@ +DROP INDEX IF EXISTS "identity_recovery_addresses_code_uq_idx"; \ No newline at end of file diff --git a/persistence/sql/migrations/sql/20210913095309000009_identity_recovery_tokens.sqlite3.up.sql b/persistence/sql/migrations/sql/20210913095309000009_identity_recovery_tokens.sqlite3.up.sql new file mode 100644 index 000000000000..1ebb1c9fdfcd --- /dev/null +++ b/persistence/sql/migrations/sql/20210913095309000009_identity_recovery_tokens.sqlite3.up.sql @@ -0,0 +1 @@ +ALTER TABLE "_identity_recovery_tokens_tmp" RENAME TO "identity_recovery_tokens"; \ No newline at end of file diff --git a/persistence/sql/migrations/sql/20210913095309000010_identity_recovery_tokens.cockroach.down.sql b/persistence/sql/migrations/sql/20210913095309000010_identity_recovery_tokens.cockroach.down.sql new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/persistence/sql/migrations/sql/20210913095309000010_identity_recovery_tokens.cockroach.up.sql b/persistence/sql/migrations/sql/20210913095309000010_identity_recovery_tokens.cockroach.up.sql new file mode 100644 index 000000000000..1f600ca0160c --- /dev/null +++ b/persistence/sql/migrations/sql/20210913095309000010_identity_recovery_tokens.cockroach.up.sql @@ -0,0 +1 @@ +UPDATE "identity_recovery_tokens" SET "identity_id" = "_identity_id_tmp"; \ No newline at end of file diff --git a/persistence/sql/migrations/sql/20210913095309000010_identity_recovery_tokens.sqlite3.down.sql b/persistence/sql/migrations/sql/20210913095309000010_identity_recovery_tokens.sqlite3.down.sql new file mode 100644 index 000000000000..1ebb1c9fdfcd --- /dev/null +++ b/persistence/sql/migrations/sql/20210913095309000010_identity_recovery_tokens.sqlite3.down.sql @@ -0,0 +1 @@ +ALTER TABLE "_identity_recovery_tokens_tmp" RENAME TO "identity_recovery_tokens"; \ No newline at end of file diff --git a/persistence/sql/migrations/sql/20210913095309000010_identity_recovery_tokens.sqlite3.up.sql b/persistence/sql/migrations/sql/20210913095309000010_identity_recovery_tokens.sqlite3.up.sql new file mode 100644 index 000000000000..0e3e64dccf38 --- /dev/null +++ b/persistence/sql/migrations/sql/20210913095309000010_identity_recovery_tokens.sqlite3.up.sql @@ -0,0 +1 @@ +ALTER TABLE identity_recovery_tokens ADD COLUMN identity_id CHAR(36) NULL REFERENCES identities(id) ON DELETE CASCADE ON UPDATE RESTRICT; \ No newline at end of file diff --git a/persistence/sql/migrations/sql/20210913095309000011_identity_recovery_tokens.cockroach.down.sql b/persistence/sql/migrations/sql/20210913095309000011_identity_recovery_tokens.cockroach.down.sql new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/persistence/sql/migrations/sql/20210913095309000011_identity_recovery_tokens.cockroach.up.sql b/persistence/sql/migrations/sql/20210913095309000011_identity_recovery_tokens.cockroach.up.sql new file mode 100644 index 000000000000..998578bf46da --- /dev/null +++ b/persistence/sql/migrations/sql/20210913095309000011_identity_recovery_tokens.cockroach.up.sql @@ -0,0 +1 @@ +ALTER TABLE "identity_recovery_tokens" ALTER COLUMN "identity_id" SET NOT NULL; \ No newline at end of file diff --git a/persistence/sql/migrations/sql/20210913095309000011_identity_recovery_tokens.sqlite3.down.sql b/persistence/sql/migrations/sql/20210913095309000011_identity_recovery_tokens.sqlite3.down.sql new file mode 100644 index 000000000000..7cda02822ec4 --- /dev/null +++ b/persistence/sql/migrations/sql/20210913095309000011_identity_recovery_tokens.sqlite3.down.sql @@ -0,0 +1 @@ +DROP TABLE "identity_recovery_tokens"; \ No newline at end of file diff --git a/persistence/sql/migrations/sql/20210913095309000011_identity_recovery_tokens.sqlite3.up.sql b/persistence/sql/migrations/sql/20210913095309000011_identity_recovery_tokens.sqlite3.up.sql new file mode 100644 index 000000000000..48d951bae394 --- /dev/null +++ b/persistence/sql/migrations/sql/20210913095309000011_identity_recovery_tokens.sqlite3.up.sql @@ -0,0 +1 @@ +UPDATE identity_recovery_tokens SET identity_id=(SELECT identity_id FROM identity_recovery_addresses WHERE id=identity_recovery_address_id) WHERE identity_id = '' OR identity_id IS NULL; \ No newline at end of file diff --git a/persistence/sql/migrations/sql/20210913095309000012_identity_recovery_tokens.cockroach.down.sql b/persistence/sql/migrations/sql/20210913095309000012_identity_recovery_tokens.cockroach.down.sql new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/persistence/sql/migrations/sql/20210913095309000012_identity_recovery_tokens.cockroach.up.sql b/persistence/sql/migrations/sql/20210913095309000012_identity_recovery_tokens.cockroach.up.sql new file mode 100644 index 000000000000..7fa1e1cddde0 --- /dev/null +++ b/persistence/sql/migrations/sql/20210913095309000012_identity_recovery_tokens.cockroach.up.sql @@ -0,0 +1 @@ +ALTER TABLE "identity_recovery_tokens" DROP COLUMN "_identity_id_tmp"; \ No newline at end of file diff --git a/persistence/sql/migrations/sql/20210913095309000012_identity_recovery_tokens.sqlite3.down.sql b/persistence/sql/migrations/sql/20210913095309000012_identity_recovery_tokens.sqlite3.down.sql new file mode 100644 index 000000000000..8789bba9eda6 --- /dev/null +++ b/persistence/sql/migrations/sql/20210913095309000012_identity_recovery_tokens.sqlite3.down.sql @@ -0,0 +1 @@ +INSERT INTO "_identity_recovery_tokens_tmp" (id, token, used, used_at, identity_recovery_address_id, selfservice_recovery_flow_id, created_at, updated_at, expires_at, issued_at, nid, identity_id) SELECT id, token, used, used_at, identity_recovery_address_id, selfservice_recovery_flow_id, created_at, updated_at, expires_at, issued_at, nid, identity_id FROM "identity_recovery_tokens"; \ No newline at end of file diff --git a/persistence/sql/migrations/sql/20210913095309000012_identity_recovery_tokens.sqlite3.up.sql b/persistence/sql/migrations/sql/20210913095309000012_identity_recovery_tokens.sqlite3.up.sql new file mode 100644 index 000000000000..9835ff0ec77f --- /dev/null +++ b/persistence/sql/migrations/sql/20210913095309000012_identity_recovery_tokens.sqlite3.up.sql @@ -0,0 +1 @@ +DELETE FROM identity_recovery_tokens WHERE identity_recovery_address_id IS NULL AND identity_id = ''; \ No newline at end of file diff --git a/persistence/sql/migrations/sql/20210913095309000013_identity_recovery_tokens.cockroach.down.sql b/persistence/sql/migrations/sql/20210913095309000013_identity_recovery_tokens.cockroach.down.sql new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/persistence/sql/migrations/sql/20210913095309000013_identity_recovery_tokens.cockroach.up.sql b/persistence/sql/migrations/sql/20210913095309000013_identity_recovery_tokens.cockroach.up.sql new file mode 100644 index 000000000000..45a3a3a47558 --- /dev/null +++ b/persistence/sql/migrations/sql/20210913095309000013_identity_recovery_tokens.cockroach.up.sql @@ -0,0 +1 @@ +ALTER TABLE "identity_recovery_tokens" ADD CONSTRAINT "identity_recovery_tokens_identity_id_fk_idx" FOREIGN KEY ("identity_id") REFERENCES "identities" ("id") ON UPDATE RESTRICT ON DELETE CASCADE; \ No newline at end of file diff --git a/persistence/sql/migrations/sql/20210913095309000013_identity_recovery_tokens.sqlite3.down.sql b/persistence/sql/migrations/sql/20210913095309000013_identity_recovery_tokens.sqlite3.down.sql new file mode 100644 index 000000000000..95955abf07ed --- /dev/null +++ b/persistence/sql/migrations/sql/20210913095309000013_identity_recovery_tokens.sqlite3.down.sql @@ -0,0 +1 @@ +CREATE INDEX "identity_recovery_tokens_nid_idx" ON "_identity_recovery_tokens_tmp" (id, nid); \ No newline at end of file diff --git a/persistence/sql/migrations/sql/20210913095309000013_identity_recovery_tokens.sqlite3.up.sql b/persistence/sql/migrations/sql/20210913095309000013_identity_recovery_tokens.sqlite3.up.sql new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/persistence/sql/migrations/sql/20210913095309000014_identity_recovery_tokens.sqlite3.down.sql b/persistence/sql/migrations/sql/20210913095309000014_identity_recovery_tokens.sqlite3.down.sql new file mode 100644 index 000000000000..d26632345e93 --- /dev/null +++ b/persistence/sql/migrations/sql/20210913095309000014_identity_recovery_tokens.sqlite3.down.sql @@ -0,0 +1 @@ +CREATE INDEX "identity_recovery_addresses_code_idx" ON "_identity_recovery_tokens_tmp" (token); \ No newline at end of file diff --git a/persistence/sql/migrations/sql/20210913095309000014_identity_recovery_tokens.sqlite3.up.sql b/persistence/sql/migrations/sql/20210913095309000014_identity_recovery_tokens.sqlite3.up.sql new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/persistence/sql/migrations/sql/20210913095309000015_identity_recovery_tokens.sqlite3.down.sql b/persistence/sql/migrations/sql/20210913095309000015_identity_recovery_tokens.sqlite3.down.sql new file mode 100644 index 000000000000..5da22e3522c7 --- /dev/null +++ b/persistence/sql/migrations/sql/20210913095309000015_identity_recovery_tokens.sqlite3.down.sql @@ -0,0 +1 @@ +CREATE UNIQUE INDEX "identity_recovery_addresses_code_uq_idx" ON "_identity_recovery_tokens_tmp" (token); \ No newline at end of file diff --git a/persistence/sql/migrations/sql/20210913095309000015_identity_recovery_tokens.sqlite3.up.sql b/persistence/sql/migrations/sql/20210913095309000015_identity_recovery_tokens.sqlite3.up.sql new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/persistence/sql/migrations/sql/20210913095309000016_identity_recovery_tokens.sqlite3.down.sql b/persistence/sql/migrations/sql/20210913095309000016_identity_recovery_tokens.sqlite3.down.sql new file mode 100644 index 000000000000..cb88582eba24 --- /dev/null +++ b/persistence/sql/migrations/sql/20210913095309000016_identity_recovery_tokens.sqlite3.down.sql @@ -0,0 +1,17 @@ +CREATE TABLE "_identity_recovery_tokens_tmp" ( +"id" TEXT PRIMARY KEY, +"token" TEXT NOT NULL, +"used" bool NOT NULL DEFAULT 'false', +"used_at" DATETIME, +"identity_recovery_address_id" char(36) NOT NULL, +"selfservice_recovery_flow_id" char(36), +"created_at" DATETIME NOT NULL, +"updated_at" DATETIME NOT NULL, +"expires_at" DATETIME NOT NULL DEFAULT '2000-01-01 00:00:00', +"issued_at" DATETIME NOT NULL DEFAULT '2000-01-01 00:00:00', +"nid" char(36), +"identity_id" CHAR(36), +FOREIGN KEY (identity_recovery_address_id) REFERENCES identity_recovery_addresses (id) ON UPDATE NO ACTION ON DELETE CASCADE, +FOREIGN KEY (selfservice_recovery_flow_id) REFERENCES selfservice_recovery_flows (id) ON UPDATE NO ACTION ON DELETE CASCADE, +FOREIGN KEY (identity_id) REFERENCES identities (id) ON UPDATE RESTRICT ON DELETE CASCADE +); \ No newline at end of file diff --git a/persistence/sql/migrations/sql/20210913095309000016_identity_recovery_tokens.sqlite3.up.sql b/persistence/sql/migrations/sql/20210913095309000016_identity_recovery_tokens.sqlite3.up.sql new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/persistence/sql/migrations/sql/20210913095309000017_identity_recovery_tokens.sqlite3.down.sql b/persistence/sql/migrations/sql/20210913095309000017_identity_recovery_tokens.sqlite3.down.sql new file mode 100644 index 000000000000..bd7b1220f2f8 --- /dev/null +++ b/persistence/sql/migrations/sql/20210913095309000017_identity_recovery_tokens.sqlite3.down.sql @@ -0,0 +1 @@ +DROP INDEX IF EXISTS "identity_recovery_tokens_nid_idx"; \ No newline at end of file diff --git a/persistence/sql/migrations/sql/20210913095309000017_identity_recovery_tokens.sqlite3.up.sql b/persistence/sql/migrations/sql/20210913095309000017_identity_recovery_tokens.sqlite3.up.sql new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/persistence/sql/migrations/sql/20210913095309000018_identity_recovery_tokens.sqlite3.down.sql b/persistence/sql/migrations/sql/20210913095309000018_identity_recovery_tokens.sqlite3.down.sql new file mode 100644 index 000000000000..fc7fe837896a --- /dev/null +++ b/persistence/sql/migrations/sql/20210913095309000018_identity_recovery_tokens.sqlite3.down.sql @@ -0,0 +1 @@ +DROP INDEX IF EXISTS "identity_recovery_addresses_code_idx"; \ No newline at end of file diff --git a/persistence/sql/migrations/sql/20210913095309000018_identity_recovery_tokens.sqlite3.up.sql b/persistence/sql/migrations/sql/20210913095309000018_identity_recovery_tokens.sqlite3.up.sql new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/persistence/sql/migrations/sql/20210913095309000019_identity_recovery_tokens.sqlite3.down.sql b/persistence/sql/migrations/sql/20210913095309000019_identity_recovery_tokens.sqlite3.down.sql new file mode 100644 index 000000000000..03159a3cd7a1 --- /dev/null +++ b/persistence/sql/migrations/sql/20210913095309000019_identity_recovery_tokens.sqlite3.down.sql @@ -0,0 +1 @@ +DROP INDEX IF EXISTS "identity_recovery_addresses_code_uq_idx"; \ No newline at end of file diff --git a/persistence/sql/migrations/sql/20210913095309000019_identity_recovery_tokens.sqlite3.up.sql b/persistence/sql/migrations/sql/20210913095309000019_identity_recovery_tokens.sqlite3.up.sql new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/persistence/sql/migrations/sql/20210913095309000020_identity_recovery_tokens.sqlite3.down.sql b/persistence/sql/migrations/sql/20210913095309000020_identity_recovery_tokens.sqlite3.down.sql new file mode 100644 index 000000000000..5e215d68e86f --- /dev/null +++ b/persistence/sql/migrations/sql/20210913095309000020_identity_recovery_tokens.sqlite3.down.sql @@ -0,0 +1 @@ +DELETE FROM identity_recovery_tokens WHERE identity_recovery_address_id IS NULL; \ No newline at end of file diff --git a/persistence/sql/migrations/sql/20210913095309000020_identity_recovery_tokens.sqlite3.up.sql b/persistence/sql/migrations/sql/20210913095309000020_identity_recovery_tokens.sqlite3.up.sql new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/persistence/sql/migrations/templates/20210913095309_identity_recovery_tokens.down.fizz b/persistence/sql/migrations/templates/20210913095309_identity_recovery_tokens.down.fizz new file mode 100644 index 000000000000..9884342c669a --- /dev/null +++ b/persistence/sql/migrations/templates/20210913095309_identity_recovery_tokens.down.fizz @@ -0,0 +1,6 @@ +sql("DELETE FROM identity_recovery_tokens WHERE identity_recovery_address_id IS NULL") +change_column("identity_recovery_tokens", "identity_recovery_address_id", "uuid", {"size": 36}) +{{ if not .IsSQLite }} + drop_foreign_key("identity_recovery_tokens", "identity_recovery_tokens_identity_id_fk_idx") +{{ end }} +drop_column("identity_recovery_tokens", "identity_id") diff --git a/persistence/sql/migrations/templates/20210913095309_identity_recovery_tokens.up.fizz b/persistence/sql/migrations/templates/20210913095309_identity_recovery_tokens.up.fizz new file mode 100644 index 000000000000..9a233dd411cd --- /dev/null +++ b/persistence/sql/migrations/templates/20210913095309_identity_recovery_tokens.up.fizz @@ -0,0 +1,20 @@ +change_column("identity_recovery_tokens", "identity_recovery_address_id", "uuid", {"size": 36,"null": true}) + +{{ if .IsSQLite }} + sql("ALTER TABLE identity_recovery_tokens ADD COLUMN identity_id CHAR(36) NULL REFERENCES identities(id) ON DELETE CASCADE ON UPDATE RESTRICT") +{{ else }} + add_column("identity_recovery_tokens", "identity_id", "uuid", {"size": 36,"null": true}) +{{ end }} +{{ if or .IsPostgreSQL .IsCockroach }} + sql("UPDATE identity_recovery_tokens SET identity_id=(SELECT identity_id FROM identity_recovery_addresses WHERE id=identity_recovery_address_id) WHERE identity_id = '00000000-0000-0000-0000-000000000000'") +{{ else }} + sql("UPDATE identity_recovery_tokens SET identity_id=(SELECT identity_id FROM identity_recovery_addresses WHERE id=identity_recovery_address_id) WHERE identity_id = ''") +{{ end }} +{{ if not .IsSQLite }} + change_column("identity_recovery_tokens", "identity_id", "uuid", {"size": 36}) + add_foreign_key("identity_recovery_tokens", "identity_id", {"identities": ["id"]}, { + "name": "identity_recovery_tokens_identity_id_fk_idx", + "on_delete": "CASCADE", + "on_update": "RESTRICT", + }) +{{ end }} diff --git a/persistence/sql/persister_recovery.go b/persistence/sql/persister_recovery.go index 39f715170d88..7a082e0d65fe 100644 --- a/persistence/sql/persister_recovery.go +++ b/persistence/sql/persister_recovery.go @@ -6,16 +6,14 @@ import ( "fmt" "time" - "github.com/ory/kratos/corp" - "github.com/ory/kratos/identity" - "github.com/gobuffalo/pop/v5" "github.com/gofrs/uuid" - "github.com/ory/x/sqlcon" - + "github.com/ory/kratos/corp" + "github.com/ory/kratos/identity" "github.com/ory/kratos/selfservice/flow/recovery" "github.com/ory/kratos/selfservice/strategy/link" + "github.com/ory/x/sqlcon" ) var _ recovery.FlowPersister = new(Persister) @@ -76,9 +74,10 @@ func (p *Persister) UseRecoveryToken(ctx context.Context, token string) (*link.R var ra identity.RecoveryAddress if err := tx.Where("id = ? AND nid = ?", rt.RecoveryAddressID, nid).First(&ra); err != nil { - return sqlcon.HandleError(err) + if !errors.Is(sqlcon.HandleError(err), sqlcon.ErrNoRows) { + return err + } } - rt.RecoveryAddress = &ra /* #nosec G201 TableName is static */ diff --git a/selfservice/strategy/link/strategy_recovery.go b/selfservice/strategy/link/strategy_recovery.go index 8cb8173c693c..26e85cb2f7bd 100644 --- a/selfservice/strategy/link/strategy_recovery.go +++ b/selfservice/strategy/link/strategy_recovery.go @@ -162,23 +162,14 @@ func (s *Strategy) createRecoveryLink(w http.ResponseWriter, r *http.Request, _ s.d.Writer().WriteError(w, r, err) return } - - if len(id.RecoveryAddresses) == 0 { - s.d.Writer().WriteError(w, r, errors.WithStack(herodot.ErrBadRequest.WithReasonf("The identity does not have any recovery addresses set."))) - return - } - - address := id.RecoveryAddresses[0] - token := NewRecoveryToken(&address, expiresIn) + token := NewRecoveryToken(id.ID, expiresIn) if err := s.d.RecoveryTokenPersister().CreateRecoveryToken(r.Context(), token); err != nil { s.d.Writer().WriteError(w, r, err) return } s.d.Audit(). - WithField("via", address.Via). - WithField("identity_id", address.IdentityID). - WithSensitiveField("email_address", address.Value). + WithField("identity_id", id.ID). WithSensitiveField("recovery_link_token", token). Info("A recovery link has been created.") @@ -326,7 +317,7 @@ func (s *Strategy) recoveryUseToken(w http.ResponseWriter, r *http.Request, body return s.retryRecoveryFlowWithError(w, r, flow.TypeBrowser, err) } - recovered, err := s.d.IdentityPool().GetIdentity(r.Context(), token.RecoveryAddress.IdentityID) + recovered, err := s.d.IdentityPool().GetIdentity(r.Context(), token.IdentityID) if err != nil { return s.HandleRecoveryError(w, r, f, nil, err) } diff --git a/selfservice/strategy/link/strategy_recovery_test.go b/selfservice/strategy/link/strategy_recovery_test.go index 416d27879676..37eebaac8c12 100644 --- a/selfservice/strategy/link/strategy_recovery_test.go +++ b/selfservice/strategy/link/strategy_recovery_test.go @@ -96,16 +96,27 @@ func TestAdminStrategy(t *testing.T) { assert.EqualError(t, err.(*kratos.GenericOpenAPIError), "404 Not Found") }) - t.Run("description=should not be able to recover an account that does not have a recovery email", func(t *testing.T) { + t.Run("description=should create a valid recovery link without email", func(t *testing.T) { id := identity.Identity{Traits: identity.Traits(`{}`)} + require.NoError(t, reg.IdentityManager().Create(context.Background(), &id, identity.ManagerAllowWriteProtectedTraits)) - _, _, err := adminSDK.V0alpha2Api.AdminCreateSelfServiceRecoveryLink(context.Background()).AdminCreateSelfServiceRecoveryLinkBody(kratos.AdminCreateSelfServiceRecoveryLinkBody{ + rl, _, err := adminSDK.V0alpha2Api.AdminCreateSelfServiceRecoveryLink(context.Background()).AdminCreateSelfServiceRecoveryLinkBody(kratos.AdminCreateSelfServiceRecoveryLinkBody{ IdentityId: id.ID.String(), + ExpiresIn: pointerx.String("100ms"), }).Execute() - require.IsType(t, err, new(kratos.GenericOpenAPIError), "%T", err) - assert.EqualError(t, err.(*kratos.GenericOpenAPIError), "400 Bad Request") + require.NoError(t, err) + + time.Sleep(time.Millisecond * 100) + checkLink(t, rl, time.Now().Add(conf.SelfServiceFlowRecoveryRequestLifespan())) + res, err := publicTS.Client().Get(rl.RecoveryLink) + require.NoError(t, err) + + require.Equal(t, http.StatusOK, res.StatusCode) + + // We end up here because the link is expired. + assert.Contains(t, res.Request.URL.Path, "/recover", rl.RecoveryLink) }) t.Run("description=should create a valid recovery link and set the expiry time and not be able to recover the account", func(t *testing.T) { diff --git a/selfservice/strategy/link/test/persistence.go b/selfservice/strategy/link/test/persistence.go index 19145e227e46..1991d17164ff 100644 --- a/selfservice/strategy/link/test/persistence.go +++ b/selfservice/strategy/link/test/persistence.go @@ -47,7 +47,7 @@ func TestPersister(ctx context.Context, conf *config.Config, p interface { var i identity.Identity require.NoError(t, faker.FakeData(&i)) - address := &identity.RecoveryAddress{Value: email, Via: identity.RecoveryAddressTypeEmail} + address := &identity.RecoveryAddress{Value: email, Via: identity.RecoveryAddressTypeEmail, IdentityID: i.ID} i.RecoveryAddresses = append(i.RecoveryAddresses, *address) require.NoError(t, p.CreateIdentity(ctx, &i)) @@ -56,6 +56,7 @@ func TestPersister(ctx context.Context, conf *config.Config, p interface { RecoveryAddress: &i.RecoveryAddresses[0], ExpiresAt: time.Now(), IssuedAt: time.Now(), + IdentityID: i.ID, } } @@ -81,9 +82,8 @@ func TestPersister(ctx context.Context, conf *config.Config, p interface { actual, err := p.UseRecoveryToken(ctx, expected.Token) require.NoError(t, err) - assertx.EqualAsJSONExcept(t, expected.RecoveryAddress, actual.RecoveryAddress, []string{"created_at", "updated_at"}) assert.Equal(t, nid, actual.NID) - assert.Equal(t, expected.RecoveryAddress.IdentityID, actual.RecoveryAddress.IdentityID) + assert.Equal(t, expected.IdentityID, actual.IdentityID) assert.NotEqual(t, expected.Token, actual.Token) assert.EqualValues(t, expected.FlowID, actual.FlowID) diff --git a/selfservice/strategy/link/token_recovery.go b/selfservice/strategy/link/token_recovery.go index 4db635a56a2b..37980d2a77d4 100644 --- a/selfservice/strategy/link/token_recovery.go +++ b/selfservice/strategy/link/token_recovery.go @@ -46,10 +46,11 @@ type RecoveryToken struct { // UpdatedAt is a helper struct field for gobuffalo.pop. UpdatedAt time.Time `json:"-" faker:"-" db:"updated_at"` // RecoveryAddressID is a helper struct field for gobuffalo.pop. - RecoveryAddressID uuid.UUID `json:"-" faker:"-" db:"identity_recovery_address_id"` + RecoveryAddressID *uuid.UUID `json:"-" faker:"-" db:"identity_recovery_address_id"` // FlowID is a helper struct field for gobuffalo.pop. - FlowID uuid.NullUUID `json:"-" faker:"-" db:"selfservice_recovery_flow_id"` - NID uuid.UUID `json:"-" faker:"-" db:"nid"` + FlowID uuid.NullUUID `json:"-" faker:"-" db:"selfservice_recovery_flow_id"` + NID uuid.UUID `json:"-" faker:"-" db:"nid"` + IdentityID uuid.UUID `json:"identity_id" faker:"-" db:"identity_id"` } func (RecoveryToken) TableName(ctx context.Context) string { @@ -58,23 +59,32 @@ func (RecoveryToken) TableName(ctx context.Context) string { func NewSelfServiceRecoveryToken(address *identity.RecoveryAddress, f *recovery.Flow, expiresIn time.Duration) *RecoveryToken { now := time.Now().UTC() + var identityID = uuid.UUID{} + var recoveryAddressID = uuid.UUID{} + if address != nil { + identityID = address.IdentityID + recoveryAddressID = address.ID + } return &RecoveryToken{ - ID: x.NewUUID(), - Token: randx.MustString(32, randx.AlphaNum), - RecoveryAddress: address, - ExpiresAt: now.Add(expiresIn), - IssuedAt: now, - FlowID: uuid.NullUUID{UUID: f.ID, Valid: true}} + ID: x.NewUUID(), + Token: randx.MustString(32, randx.AlphaNum), + RecoveryAddress: address, + ExpiresAt: now.Add(expiresIn), + IssuedAt: now, + IdentityID: identityID, + FlowID: uuid.NullUUID{UUID: f.ID, Valid: true}, + RecoveryAddressID: &recoveryAddressID, + } } -func NewRecoveryToken(address *identity.RecoveryAddress, expiresIn time.Duration) *RecoveryToken { +func NewRecoveryToken(identityID uuid.UUID, expiresIn time.Duration) *RecoveryToken { now := time.Now().UTC() return &RecoveryToken{ - ID: x.NewUUID(), - Token: randx.MustString(32, randx.AlphaNum), - RecoveryAddress: address, - ExpiresAt: now.Add(expiresIn), - IssuedAt: now, + ID: x.NewUUID(), + Token: randx.MustString(32, randx.AlphaNum), + ExpiresAt: now.Add(expiresIn), + IssuedAt: now, + IdentityID: identityID, } }