@logto/schemas 1.29.0 → 1.30.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. package/alterations/1.30.0-1750744685-add-triggers-to-delete-secrets-on-social-identities-deletion.ts +81 -0
  2. package/alterations/1.30.0-1750748516-add-enable-token-storage-column-to-connectors-table.ts +20 -0
  3. package/alterations/1.30.0-1751255436-split-secret-connector-relatioins-table.ts +359 -0
  4. package/alterations/1.30.0-1751337183-add-require-mfa-on-sign-in-to-users.ts +20 -0
  5. package/alterations/1.30.0-1751400000-move-require-mfa-on-sign-in-to-logto-config.ts +21 -0
  6. package/alterations/1.30.0-1751529530-add-enable-token-storage-column-to-sso-connectors-table.ts +20 -0
  7. package/alterations/1.30.0-1752630302-alterate-enable-column-default-value-in-account-centers-table.ts +20 -0
  8. package/alterations/1.30.0-1753669579-add-organization-user-relations-foreign-key.ts +46 -0
  9. package/alterations-js/1.30.0-1750744685-add-triggers-to-delete-secrets-on-social-identities-deletion.js +76 -0
  10. package/alterations-js/1.30.0-1750748516-add-enable-token-storage-column-to-connectors-table.js +16 -0
  11. package/alterations-js/1.30.0-1751255436-split-secret-connector-relatioins-table.js +338 -0
  12. package/alterations-js/1.30.0-1751337183-add-require-mfa-on-sign-in-to-users.js +16 -0
  13. package/alterations-js/1.30.0-1751400000-move-require-mfa-on-sign-in-to-logto-config.js +17 -0
  14. package/alterations-js/1.30.0-1751529530-add-enable-token-storage-column-to-sso-connectors-table.js +16 -0
  15. package/alterations-js/1.30.0-1752630302-alterate-enable-column-default-value-in-account-centers-table.js +16 -0
  16. package/alterations-js/1.30.0-1753669579-add-organization-user-relations-foreign-key.js +38 -0
  17. package/lib/consts/oidc.d.ts +9 -1
  18. package/lib/consts/oidc.js +5 -0
  19. package/lib/db-entries/connector.d.ts +5 -1
  20. package/lib/db-entries/connector.js +4 -0
  21. package/lib/db-entries/index.d.ts +2 -1
  22. package/lib/db-entries/index.js +2 -1
  23. package/lib/db-entries/secret-enterprise-sso-connector-relation.d.ts +28 -0
  24. package/lib/db-entries/secret-enterprise-sso-connector-relation.js +37 -0
  25. package/lib/db-entries/secret-social-connector-relation.d.ts +28 -0
  26. package/lib/db-entries/secret-social-connector-relation.js +37 -0
  27. package/lib/db-entries/secret.d.ts +9 -9
  28. package/lib/db-entries/secret.js +9 -9
  29. package/lib/db-entries/sso-connector.d.ts +5 -1
  30. package/lib/db-entries/sso-connector.js +4 -0
  31. package/lib/foundations/jsonb-types/custom-profile-fields.d.ts +336 -11
  32. package/lib/foundations/jsonb-types/custom-profile-fields.js +17 -9
  33. package/lib/foundations/jsonb-types/secrets.d.ts +2 -0
  34. package/lib/foundations/jsonb-types/secrets.js +5 -0
  35. package/lib/foundations/jsonb-types/sign-in-experience.d.ts +3 -1
  36. package/lib/foundations/jsonb-types/sign-in-experience.js +2 -0
  37. package/lib/foundations/jsonb-types/users.d.ts +94 -0
  38. package/lib/foundations/jsonb-types/users.js +11 -0
  39. package/lib/types/connector.d.ts +39 -0
  40. package/lib/types/connector.js +1 -0
  41. package/lib/types/consent.d.ts +40 -0
  42. package/lib/types/custom-profile-fields.d.ts +1410 -133
  43. package/lib/types/custom-profile-fields.js +51 -15
  44. package/lib/types/index.d.ts +2 -0
  45. package/lib/types/index.js +2 -0
  46. package/lib/types/interactions.d.ts +175 -1
  47. package/lib/types/interactions.js +48 -1
  48. package/lib/types/logto-config/index.d.ts +214 -179
  49. package/lib/types/logto-config/jwt-customizer.d.ts +453 -328
  50. package/lib/types/logto-config/jwt-customizer.js +6 -1
  51. package/lib/types/secrets.d.ts +436 -0
  52. package/lib/types/secrets.js +73 -0
  53. package/lib/types/sign-in-experience.d.ts +19 -1
  54. package/lib/types/sign-in-experience.js +3 -1
  55. package/lib/types/sso-connector.d.ts +5 -2
  56. package/lib/types/user-logto-config.d.ts +45 -0
  57. package/lib/types/user-logto-config.js +18 -0
  58. package/lib/types/user.d.ts +615 -0
  59. package/lib/types/user.js +14 -1
  60. package/lib/types/verification-records/backup-code-verification.d.ts +18 -0
  61. package/lib/types/verification-records/backup-code-verification.js +3 -0
  62. package/lib/types/verification-records/enterprise-sso-verification.d.ts +145 -0
  63. package/lib/types/verification-records/enterprise-sso-verification.js +5 -0
  64. package/lib/types/verification-records/new-password-identity-verification.d.ts +31 -0
  65. package/lib/types/verification-records/new-password-identity-verification.js +4 -0
  66. package/lib/types/verification-records/social-verification.d.ts +164 -0
  67. package/lib/types/verification-records/social-verification.js +6 -0
  68. package/lib/types/verification-records/totp-verification.d.ts +18 -0
  69. package/lib/types/verification-records/totp-verification.js +3 -0
  70. package/lib/types/verification-records/web-authn-verification.d.ts +44 -0
  71. package/lib/types/verification-records/web-authn-verification.js +5 -0
  72. package/package.json +6 -6
  73. package/tables/account_centers.sql +1 -1
  74. package/tables/connectors.sql +2 -0
  75. package/tables/organization_user_relations.sql +4 -1
  76. package/tables/secret_enterprise_sso_connector_relations.sql +60 -0
  77. package/tables/secret_social_connector_relations.sql +75 -0
  78. package/tables/secrets.sql +4 -4
  79. package/tables/sso_connectors.sql +2 -0
  80. package/tables/users.sql +2 -1
  81. package/lib/db-entries/secret-connector-relation.d.ts +0 -40
  82. package/lib/db-entries/secret-connector-relation.js +0 -49
  83. package/tables/secret_connector_relations.sql +0 -78
@@ -0,0 +1,81 @@
1
+ import { sql } from '@silverhand/slonik';
2
+
3
+ import type { AlterationScript } from '../lib/types/alteration.js';
4
+
5
+ const alteration: AlterationScript = {
6
+ up: async (pool) => {
7
+ /** Trigger function to delete secret when the SSO identity is deleted. */
8
+ await pool.query(sql`
9
+ create function delete_secret_on_sso_identity_delete()
10
+ returns trigger as $$
11
+ begin
12
+ delete from secrets
13
+ where id in (
14
+ select secret_id from secret_connector_relations
15
+ where tenant_id = old.tenant_id
16
+ and sso_connector_issuer = old.issuer
17
+ and sso_identity_id = old.identity_id
18
+ )
19
+ -- we also need to ensure that the secret is associated with the correct user
20
+ and user_id = old.user_id;
21
+ return old;
22
+ end;
23
+ $$ language plpgsql;
24
+
25
+ create trigger delete_secret_before_sso_identity_delete
26
+ before delete on user_sso_identities
27
+ for each row
28
+ execute procedure delete_secret_on_sso_identity_delete();
29
+ `);
30
+
31
+ /** Trigger function to delete associated secrets when social identities are deleted. */
32
+ await pool.query(sql`
33
+ create function delete_secrets_on_social_identity_delete()
34
+ returns trigger as $$
35
+ declare
36
+ target text;
37
+ old_identity jsonb;
38
+ new_identity jsonb;
39
+ begin
40
+ -- Loop over old identities to detect deletions or modifications
41
+ for target in select jsonb_object_keys(old.identities)
42
+ loop
43
+ old_identity := old.identities -> target;
44
+ new_identity := new.identities -> target;
45
+
46
+ -- If the identity was deleted or modified, delete the associated secret
47
+ if new_identity is null or (new_identity->>'userId') is distinct from (old_identity->>'userId') then
48
+ -- Identity was removed or changed, delete the corresponding secrets
49
+ delete from secrets
50
+ using secret_connector_relations
51
+ where secrets.id = secret_connector_relations.secret_id
52
+ -- Ensure we are deleting the correct social identity
53
+ and secret_connector_relations.social_connector_target = target
54
+ and secret_connector_relations.social_identity_id = old_identity->>'userId'
55
+ -- Ensure we delete the correct user's secret
56
+ and secrets.user_id = old.id;
57
+ end if;
58
+ end loop;
59
+
60
+ return new;
61
+ end;
62
+ $$ language plpgsql;
63
+
64
+ create trigger delete_secrets_before_social_identity_delete
65
+ before update of identities on users
66
+ for each row
67
+ execute procedure delete_secrets_on_social_identity_delete();
68
+ `);
69
+ },
70
+ down: async (pool) => {
71
+ await pool.query(sql`
72
+ drop trigger if exists delete_secret_before_sso_identity_delete on user_sso_identities;
73
+ drop function if exists delete_secret_on_sso_identity_delete();
74
+
75
+ drop trigger if exists delete_secrets_before_social_identity_delete on users;
76
+ drop function if exists delete_secrets_on_social_identity_delete();
77
+ `);
78
+ },
79
+ };
80
+
81
+ export default alteration;
@@ -0,0 +1,20 @@
1
+ import { sql } from '@silverhand/slonik';
2
+
3
+ import type { AlterationScript } from '../lib/types/alteration.js';
4
+
5
+ const alteration: AlterationScript = {
6
+ up: async (pool) => {
7
+ await pool.query(sql`
8
+ alter table connectors
9
+ add column enable_token_storage boolean not null default FALSE;
10
+ `);
11
+ },
12
+ down: async (pool) => {
13
+ await pool.query(sql`
14
+ alter table connectors
15
+ drop column if exists enable_token_storage;
16
+ `);
17
+ },
18
+ };
19
+
20
+ export default alteration;
@@ -0,0 +1,359 @@
1
+ /**
2
+ * @fileoverview
3
+ * This migration script alters the database schema to split the `secret_connector_relations` table into two separate tables:
4
+ * `secret_social_connector_relations` and `secret_enterprise_sso_connector_relations`.
5
+ */
6
+
7
+ import { sql } from '@silverhand/slonik';
8
+
9
+ import type { AlterationScript } from '../lib/types/alteration.js';
10
+
11
+ import { applyTableRls, dropTableRls } from './utils/1704934999-tables.js';
12
+
13
+ const alteration: AlterationScript = {
14
+ up: async (pool) => {
15
+ // Drop all triggers
16
+ await pool.query(sql`
17
+ drop trigger if exists delete_secrets_before_social_connector_delete on connectors;
18
+ drop function if exists delete_secrets_on_social_connector_delete();
19
+
20
+ drop trigger if exists delete_secrets_before_sso_connector_delete on sso_connectors;
21
+ drop function if exists delete_secrets_on_sso_connector_delete;
22
+
23
+ drop trigger if exists delete_secret_before_sso_identity_delete on user_sso_identities;
24
+ drop function if exists delete_secret_on_sso_identity_delete();
25
+
26
+ drop trigger if exists delete_secrets_before_social_identity_delete on users;
27
+ drop function if exists delete_secrets_on_social_identity_delete();
28
+ `);
29
+
30
+ // Drop the existing `secret_connector_relations` table
31
+ await pool.query(sql`
32
+ drop table if exists secret_connector_relations;
33
+ `);
34
+
35
+ // Create the new `secret_social_connector_relations` table
36
+ await pool.query(sql`
37
+ create table secret_social_connector_relations (
38
+ tenant_id varchar(21) not null
39
+ references tenants (id) on update cascade on delete cascade,
40
+ secret_id varchar(21) not null
41
+ references secrets (id) on update cascade on delete cascade,
42
+ /** Social connector ID foreign reference. Only present for secrets that store social connector tokens. Note: avoid directly cascading deletes here, need to delete the secrets first.*/
43
+ connector_id varchar(128) not null
44
+ references connectors (id) on update cascade,
45
+ /** The target of the social connector. e.g. 'github', 'google', etc. */
46
+ target varchar(256) not null,
47
+ /** User social identity ID foreign reference. Only present for secrets that store social identity tokens. */
48
+ identity_id varchar(128) not null,
49
+ primary key (tenant_id, secret_id),
50
+ /** Ensures that each social identity is associated with only one secret. */
51
+ constraint secret_social_connector_relations__target__identity_id
52
+ unique (tenant_id, target, identity_id)
53
+ );
54
+ `);
55
+
56
+ // Create triggers for the new `secret_social_connector_relations` table
57
+ await pool.query(sql`
58
+ /** Trigger function to delete secrets when the social connector is deleted. */
59
+ create function delete_secrets_on_social_connector_delete()
60
+ returns trigger as $$
61
+ begin
62
+ delete from secrets
63
+ where id in (
64
+ select secret_id from secret_social_connector_relations
65
+ where tenant_id = old.tenant_id and connector_id = old.id
66
+ );
67
+ return old;
68
+ end;
69
+ $$ language plpgsql;
70
+
71
+ create trigger delete_secrets_before_social_connector_delete
72
+ before delete on connectors
73
+ for each row
74
+ execute procedure delete_secrets_on_social_connector_delete();
75
+
76
+ /** Trigger function to delete associated secrets when social identities are deleted. */
77
+ create function delete_secrets_on_social_identity_delete()
78
+ returns trigger as $$
79
+ declare
80
+ identity_target text;
81
+ old_identity jsonb;
82
+ new_identity jsonb;
83
+ begin
84
+ -- Loop over old identities to detect deletions or modifications
85
+ for identity_target in select jsonb_object_keys(old.identities)
86
+ loop
87
+ old_identity := old.identities -> identity_target;
88
+ new_identity := new.identities -> identity_target;
89
+
90
+ -- If the identity was deleted or modified, delete the associated secret
91
+ if new_identity is null or (new_identity->>'userId') is distinct from (old_identity->>'userId') then
92
+ -- Identity was removed or changed, delete the corresponding secrets
93
+ delete from secrets
94
+ using secret_social_connector_relations
95
+ where secrets.id = secret_social_connector_relations.secret_id
96
+ -- Ensure we are deleting the correct social identity
97
+ and secret_social_connector_relations.target = identity_target
98
+ and secret_social_connector_relations.identity_id = old_identity->>'userId'
99
+ -- Ensure we delete the correct user's secret
100
+ and secrets.user_id = old.id;
101
+ end if;
102
+ end loop;
103
+
104
+ return new;
105
+ end;
106
+ $$ language plpgsql;
107
+
108
+ create trigger delete_secrets_before_social_identity_delete
109
+ before update of identities on users
110
+ for each row
111
+ execute procedure delete_secrets_on_social_identity_delete();
112
+ `);
113
+
114
+ await applyTableRls(pool, 'secret_social_connector_relations');
115
+
116
+ // Create the new `secret_enterprise_sso_connector_relations` table
117
+ await pool.query(sql`
118
+ create table secret_enterprise_sso_connector_relations (
119
+ tenant_id varchar(21) not null
120
+ references tenants (id) on update cascade on delete cascade,
121
+ secret_id varchar(21) not null
122
+ references secrets (id) on update cascade on delete cascade,
123
+ /** SSO connector ID foreign reference. Only present for secrets that store SSO connector tokens. Note: avoid directly cascading deletes here, need to delete the secrets first.*/
124
+ sso_connector_id varchar(128) not null
125
+ references sso_connectors (id) on update cascade,
126
+ /** User SSO connector issuer. Only present for secrets that store SSO connector tokens. */
127
+ issuer varchar(256) not null,
128
+ /** User SSO identity ID. Only present for secrets that store SSO identity tokens. */
129
+ identity_id varchar(128) not null,
130
+ primary key (tenant_id, secret_id),
131
+ /** Ensures that each SSO identity is associated with only one secret. */
132
+ foreign key (tenant_id, issuer, identity_id)
133
+ references user_sso_identities (tenant_id, issuer, identity_id) on update cascade
134
+ );
135
+ `);
136
+
137
+ // Create triggers for the new `secret_enterprise_sso_connector_relations` table
138
+ await pool.query(sql`
139
+ /** Trigger function to delete secrets when the SSO connector is deleted. */
140
+ create function delete_secrets_on_sso_connector_delete()
141
+ returns trigger as $$
142
+ begin
143
+ delete from secrets
144
+ where id in (
145
+ select secret_id from secret_enterprise_sso_connector_relations
146
+ where tenant_id = old.tenant_id and sso_connector_id = old.id
147
+ );
148
+ return old;
149
+ end;
150
+ $$ language plpgsql;
151
+
152
+ create trigger delete_secrets_before_sso_connector_delete
153
+ before delete on sso_connectors
154
+ for each row
155
+ execute procedure delete_secrets_on_sso_connector_delete();
156
+
157
+ /** Trigger function to delete secret when the SSO identity is deleted. */
158
+ create function delete_secret_on_sso_identity_delete()
159
+ returns trigger as $$
160
+ begin
161
+ delete from secrets
162
+ where id in (
163
+ select secret_id from secret_enterprise_sso_connector_relations
164
+ where tenant_id = old.tenant_id
165
+ and issuer = old.issuer
166
+ and identity_id = old.identity_id
167
+ )
168
+ -- we also need to ensure that the secret is associated with the correct user
169
+ and user_id = old.user_id;
170
+ return old;
171
+ end;
172
+ $$ language plpgsql;
173
+
174
+ create trigger delete_secret_before_sso_identity_delete
175
+ before delete on user_sso_identities
176
+ for each row
177
+ execute procedure delete_secret_on_sso_identity_delete();
178
+ `);
179
+
180
+ await applyTableRls(pool, 'secret_enterprise_sso_connector_relations');
181
+ },
182
+ down: async (pool) => {
183
+ // Drop all triggers
184
+ await pool.query(sql`
185
+ drop trigger if exists delete_secrets_before_social_connector_delete on connectors;
186
+ drop function if exists delete_secrets_on_social_connector_delete;
187
+
188
+ drop trigger if exists delete_secrets_before_sso_connector_delete on sso_connectors;
189
+ drop function if exists delete_secrets_on_sso_connector_delete;
190
+
191
+ drop trigger if exists delete_secret_before_sso_identity_delete on user_sso_identities;
192
+ drop function if exists delete_secret_on_sso_identity_delete();
193
+
194
+ drop trigger if exists delete_secrets_before_social_identity_delete on users;
195
+ drop function if exists delete_secrets_on_social_identity_delete();
196
+ `);
197
+
198
+ // Drop the new `secret_social_connector_relations` table
199
+ await dropTableRls(pool, 'secret_social_connector_relations');
200
+ await pool.query(sql`
201
+ drop table if exists secret_social_connector_relations;
202
+ `);
203
+
204
+ // Drop the new `secret_enterprise_sso_connector_relations` table
205
+ await dropTableRls(pool, 'secret_enterprise_sso_connector_relations');
206
+ await pool.query(sql`
207
+ drop table if exists secret_enterprise_sso_connector_relations;
208
+ `);
209
+
210
+ // Create secret_connector_relations
211
+ await pool.query(sql`
212
+ create table secret_connector_relations (
213
+ tenant_id varchar(21) not null
214
+ references tenants (id) on update cascade on delete cascade,
215
+ secret_id varchar(21) not null
216
+ references secrets (id) on update cascade on delete cascade,
217
+ /** Social connector ID foreign reference. Only present for secrets that store social connector tokens. Note: avoid directly cascading deletes here, need to delete the secrets first.*/
218
+ connector_id varchar(128)
219
+ references connectors (id) on update cascade,
220
+ /** SSO connector ID foreign reference. Only present for secrets that store SSO connector tokens. Note: avoid directly cascading deletes here, need to delete the secrets first.*/
221
+ sso_connector_id varchar(128)
222
+ references sso_connectors (id) on update cascade,
223
+ /** The target of the social connector. e.g. 'github', 'google', etc. */
224
+ social_connector_target varchar(256),
225
+ /** User social identity ID foreign reference. Only present for secrets that store social identity tokens. */
226
+ social_identity_id varchar(128),
227
+ /** User sso connector issuer. Only present for secrets that store SSO connector tokens. */
228
+ sso_connector_issuer varchar(256),
229
+ /** User SSO identity ID. Only present for secrets that store SSO identity tokens. */
230
+ sso_identity_id varchar(128),
231
+ primary key (tenant_id, secret_id),
232
+ /** Ensures that each social identity is associated with only one secret. */
233
+ constraint secret_connector_relations__target__social_identity_id
234
+ unique (tenant_id, social_connector_target, social_identity_id),
235
+ /** Ensures that each SSO identity is associated with only one secret. */
236
+ foreign key (tenant_id, sso_connector_issuer, sso_identity_id)
237
+ references user_sso_identities (tenant_id, issuer, identity_id) on update cascade,
238
+ /** Ensure that each secret is associated with a social connector or SSO connector, but not both at the same time. */
239
+ constraint secret_connector_relations__connector_id__sso_connector_id
240
+ check (
241
+ (
242
+ connector_id is not null and social_connector_target is not null and social_identity_id is not null and
243
+ sso_connector_id is null and sso_identity_id is null
244
+ ) or (
245
+ connector_id is null and social_connector_target is null and social_identity_id is null and
246
+ sso_connector_id is not null and sso_identity_id is not null
247
+ )
248
+ )
249
+ );
250
+ `);
251
+
252
+ await applyTableRls(pool, 'secret_connector_relations');
253
+
254
+ /** Trigger function to delete secrets when the social connector is deleted. */
255
+ await pool.query(sql`
256
+ create function delete_secrets_on_social_connector_delete()
257
+ returns trigger as $$
258
+ begin
259
+ delete from secrets
260
+ where id in (
261
+ select secret_id from secret_connector_relations
262
+ where tenant_id = old.tenant_id and connector_id = old.id
263
+ );
264
+ return old;
265
+ end;
266
+ $$ language plpgsql;
267
+
268
+ create trigger delete_secrets_before_social_connector_delete
269
+ before delete on connectors
270
+ for each row
271
+ execute procedure delete_secrets_on_social_connector_delete();
272
+ `);
273
+
274
+ /** Trigger function to delete secrets when the SSO connector is deleted. */
275
+ await pool.query(sql`
276
+ create function delete_secrets_on_sso_connector_delete()
277
+ returns trigger as $$
278
+ begin
279
+ delete from secrets
280
+ where id in (
281
+ select secret_id from secret_connector_relations
282
+ where tenant_id = old.tenant_id and sso_connector_id = old.id
283
+ );
284
+ return old;
285
+ end;
286
+ $$ language plpgsql;
287
+
288
+ create trigger delete_secrets_before_sso_connector_delete
289
+ before delete on sso_connectors
290
+ for each row
291
+ execute procedure delete_secrets_on_sso_connector_delete();
292
+ `);
293
+
294
+ /** Trigger function to delete secret when the SSO identity is deleted. */
295
+ await pool.query(sql`
296
+ create function delete_secret_on_sso_identity_delete()
297
+ returns trigger as $$
298
+ begin
299
+ delete from secrets
300
+ where id in (
301
+ select secret_id from secret_connector_relations
302
+ where tenant_id = old.tenant_id
303
+ and sso_connector_issuer = old.issuer
304
+ and sso_identity_id = old.identity_id
305
+ )
306
+ -- we also need to ensure that the secret is associated with the correct user
307
+ and user_id = old.user_id;
308
+ return old;
309
+ end;
310
+ $$ language plpgsql;
311
+
312
+ create trigger delete_secret_before_sso_identity_delete
313
+ before delete on user_sso_identities
314
+ for each row
315
+ execute procedure delete_secret_on_sso_identity_delete();
316
+ `);
317
+
318
+ /** Trigger function to delete associated secrets when social identities are deleted. */
319
+ await pool.query(sql`
320
+ create function delete_secrets_on_social_identity_delete()
321
+ returns trigger as $$
322
+ declare
323
+ target text;
324
+ old_identity jsonb;
325
+ new_identity jsonb;
326
+ begin
327
+ -- Loop over old identities to detect deletions or modifications
328
+ for target in select jsonb_object_keys(old.identities)
329
+ loop
330
+ old_identity := old.identities -> target;
331
+ new_identity := new.identities -> target;
332
+
333
+ -- If the identity was deleted or modified, delete the associated secret
334
+ if new_identity is null or (new_identity->>'userId') is distinct from (old_identity->>'userId') then
335
+ -- Identity was removed or changed, delete the corresponding secrets
336
+ delete from secrets
337
+ using secret_connector_relations
338
+ where secrets.id = secret_connector_relations.secret_id
339
+ -- Ensure we are deleting the correct social identity
340
+ and secret_connector_relations.social_connector_target = target
341
+ and secret_connector_relations.social_identity_id = old_identity->>'userId'
342
+ -- Ensure we delete the correct user's secret
343
+ and secrets.user_id = old.id;
344
+ end if;
345
+ end loop;
346
+
347
+ return new;
348
+ end;
349
+ $$ language plpgsql;
350
+
351
+ create trigger delete_secrets_before_social_identity_delete
352
+ before update of identities on users
353
+ for each row
354
+ execute procedure delete_secrets_on_social_identity_delete();
355
+ `);
356
+ },
357
+ };
358
+
359
+ export default alteration;
@@ -0,0 +1,20 @@
1
+ import { sql } from '@silverhand/slonik';
2
+
3
+ import type { AlterationScript } from '../lib/types/alteration.js';
4
+
5
+ const alteration: AlterationScript = {
6
+ up: async (pool) => {
7
+ await pool.query(sql`
8
+ alter table users
9
+ add column require_mfa_on_sign_in boolean not null default true;
10
+ `);
11
+ },
12
+ down: async (pool) => {
13
+ await pool.query(sql`
14
+ alter table users
15
+ drop column require_mfa_on_sign_in;
16
+ `);
17
+ },
18
+ };
19
+
20
+ export default alteration;
@@ -0,0 +1,21 @@
1
+ import { sql } from '@silverhand/slonik';
2
+
3
+ import type { AlterationScript } from '../lib/types/alteration.js';
4
+
5
+ const alteration: AlterationScript = {
6
+ up: async (pool) => {
7
+ // No need to migrate existing data, as the feature is not released yet.
8
+ await pool.query(sql`
9
+ alter table users
10
+ drop column require_mfa_on_sign_in;
11
+ `);
12
+ },
13
+ down: async (pool) => {
14
+ await pool.query(sql`
15
+ alter table users
16
+ add column require_mfa_on_sign_in boolean not null default true;
17
+ `);
18
+ },
19
+ };
20
+
21
+ export default alteration;
@@ -0,0 +1,20 @@
1
+ import { sql } from '@silverhand/slonik';
2
+
3
+ import type { AlterationScript } from '../lib/types/alteration.js';
4
+
5
+ const alteration: AlterationScript = {
6
+ up: async (pool) => {
7
+ await pool.query(sql`
8
+ alter table sso_connectors
9
+ add column enable_token_storage boolean not null default FALSE;
10
+ `);
11
+ },
12
+ down: async (pool) => {
13
+ await pool.query(sql`
14
+ alter table sso_connectors
15
+ drop column if exists enable_token_storage;
16
+ `);
17
+ },
18
+ };
19
+
20
+ export default alteration;
@@ -0,0 +1,20 @@
1
+ import { sql } from '@silverhand/slonik';
2
+
3
+ import type { AlterationScript } from '../lib/types/alteration.js';
4
+
5
+ const alteration: AlterationScript = {
6
+ up: async (pool) => {
7
+ await pool.query(sql`
8
+ alter table account_centers
9
+ alter column enabled set default true;
10
+ `);
11
+ },
12
+ down: async (pool) => {
13
+ await pool.query(sql`
14
+ alter table account_centers
15
+ alter column enabled set default false;
16
+ `);
17
+ },
18
+ };
19
+
20
+ export default alteration;
@@ -0,0 +1,46 @@
1
+ import { sql } from '@silverhand/slonik';
2
+
3
+ import type { AlterationScript } from '../lib/types/alteration.js';
4
+
5
+ const alteration: AlterationScript = {
6
+ up: async (pool) => {
7
+ // Drop the existing index in users table
8
+ await pool.query(sql`
9
+ drop index if exists users__id;
10
+ `);
11
+
12
+ // Create a new unique index in users table
13
+ await pool.query(sql`
14
+ create unique index users__id
15
+ on users (tenant_id, id);
16
+ `);
17
+
18
+ // Apply the foreign key constraint to organization_user_relations table
19
+ await pool.query(sql`
20
+ alter table organization_user_relations
21
+ add constraint organization_user_relations__user_id__fk
22
+ foreign key (tenant_id, user_id)
23
+ references users (tenant_id, id)
24
+ on update cascade on delete cascade;
25
+ `);
26
+ },
27
+ down: async (pool) => {
28
+ await pool.query(sql`
29
+ alter table organization_user_relations
30
+ drop constraint organization_user_relations__user_id__fk;
31
+ `);
32
+
33
+ // Drop the unique index in users table
34
+ await pool.query(sql`
35
+ drop index if exists users__id;
36
+ `);
37
+
38
+ // Recreate the old index in users table
39
+ await pool.query(sql`
40
+ create index users__id
41
+ on users (tenant_id, id);
42
+ `);
43
+ },
44
+ };
45
+
46
+ export default alteration;
@@ -0,0 +1,76 @@
1
+ import { sql } from '@silverhand/slonik';
2
+ const alteration = {
3
+ up: async (pool) => {
4
+ /** Trigger function to delete secret when the SSO identity is deleted. */
5
+ await pool.query(sql `
6
+ create function delete_secret_on_sso_identity_delete()
7
+ returns trigger as $$
8
+ begin
9
+ delete from secrets
10
+ where id in (
11
+ select secret_id from secret_connector_relations
12
+ where tenant_id = old.tenant_id
13
+ and sso_connector_issuer = old.issuer
14
+ and sso_identity_id = old.identity_id
15
+ )
16
+ -- we also need to ensure that the secret is associated with the correct user
17
+ and user_id = old.user_id;
18
+ return old;
19
+ end;
20
+ $$ language plpgsql;
21
+
22
+ create trigger delete_secret_before_sso_identity_delete
23
+ before delete on user_sso_identities
24
+ for each row
25
+ execute procedure delete_secret_on_sso_identity_delete();
26
+ `);
27
+ /** Trigger function to delete associated secrets when social identities are deleted. */
28
+ await pool.query(sql `
29
+ create function delete_secrets_on_social_identity_delete()
30
+ returns trigger as $$
31
+ declare
32
+ target text;
33
+ old_identity jsonb;
34
+ new_identity jsonb;
35
+ begin
36
+ -- Loop over old identities to detect deletions or modifications
37
+ for target in select jsonb_object_keys(old.identities)
38
+ loop
39
+ old_identity := old.identities -> target;
40
+ new_identity := new.identities -> target;
41
+
42
+ -- If the identity was deleted or modified, delete the associated secret
43
+ if new_identity is null or (new_identity->>'userId') is distinct from (old_identity->>'userId') then
44
+ -- Identity was removed or changed, delete the corresponding secrets
45
+ delete from secrets
46
+ using secret_connector_relations
47
+ where secrets.id = secret_connector_relations.secret_id
48
+ -- Ensure we are deleting the correct social identity
49
+ and secret_connector_relations.social_connector_target = target
50
+ and secret_connector_relations.social_identity_id = old_identity->>'userId'
51
+ -- Ensure we delete the correct user's secret
52
+ and secrets.user_id = old.id;
53
+ end if;
54
+ end loop;
55
+
56
+ return new;
57
+ end;
58
+ $$ language plpgsql;
59
+
60
+ create trigger delete_secrets_before_social_identity_delete
61
+ before update of identities on users
62
+ for each row
63
+ execute procedure delete_secrets_on_social_identity_delete();
64
+ `);
65
+ },
66
+ down: async (pool) => {
67
+ await pool.query(sql `
68
+ drop trigger if exists delete_secret_before_sso_identity_delete on user_sso_identities;
69
+ drop function if exists delete_secret_on_sso_identity_delete();
70
+
71
+ drop trigger if exists delete_secrets_before_social_identity_delete on users;
72
+ drop function if exists delete_secrets_on_social_identity_delete();
73
+ `);
74
+ },
75
+ };
76
+ export default alteration;
@@ -0,0 +1,16 @@
1
+ import { sql } from '@silverhand/slonik';
2
+ const alteration = {
3
+ up: async (pool) => {
4
+ await pool.query(sql `
5
+ alter table connectors
6
+ add column enable_token_storage boolean not null default FALSE;
7
+ `);
8
+ },
9
+ down: async (pool) => {
10
+ await pool.query(sql `
11
+ alter table connectors
12
+ drop column if exists enable_token_storage;
13
+ `);
14
+ },
15
+ };
16
+ export default alteration;