@logto/schemas 1.11.0 → 1.13.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.
- package/alterations/1.12.0-1700031616-update-org-role-foreign-keys.ts +35 -0
- package/alterations/1.12.0-1701054133-add-unique-constraint-to-the-sso-connector-name.ts +21 -0
- package/alterations/1.12.0-1701245520-add-single-sign-on-enabled-flag-to-sie.ts +20 -0
- package/alterations/1.13.0-1702274830-add-new-third-party-column-to-applications-table.ts +20 -0
- package/alterations/1.13.0-1702372401-add-application-permissions-tables.ts +93 -0
- package/alterations/1.13.0-1702544178-sync-tenant-orgs.ts +296 -0
- package/alterations/1.13.0-1702871078-protected-application-type.ts +24 -0
- package/alterations/1.13.0-1702877515-protected-app-configs.ts +18 -0
- package/alterations/1.13.0-1702978120-application-sign-in-experience-table.ts +61 -0
- package/alterations/1.13.0-1703229996-daily-token-usage.ts +62 -0
- package/alterations/1.13.0-1703230000-update-tenant-roles.ts +94 -0
- package/alterations/1.13.0-1704692973-remove-legacy-resources.ts +147 -0
- package/alterations/1.13.0-1704934999-add-magic-links-table.ts +37 -0
- package/alterations/1.13.0-1704935001-add-organization-invitation-tables.ts +78 -0
- package/alterations/1.13.0-1705288654-add-application-user-consent-organizations-table.ts +62 -0
- package/alterations/1.13.0-1705991158-update-invitation-indices.ts +32 -0
- package/alterations/1.13.0-1706449174-update-organization-invitation-column.ts +24 -0
- package/alterations/1.13.0-1706510290-protected-app-host-index.ts +21 -0
- package/alterations/1.13.0-1706512952-restore-get-started-page.ts +17 -0
- package/alterations/1.13.0-1706528755-remove-magic-links.ts +46 -0
- package/alterations/1.13.0-1706585206-protected-app-custom-domain-unique.ts +21 -0
- package/alterations/utils/1704934999-tables.ts +49 -0
- package/alterations/utils/README.md +9 -0
- package/alterations-js/1.12.0-1700031616-update-org-role-foreign-keys.d.ts +3 -0
- package/alterations-js/1.12.0-1700031616-update-org-role-foreign-keys.js +31 -0
- package/alterations-js/1.12.0-1701054133-add-unique-constraint-to-the-sso-connector-name.d.ts +3 -0
- package/alterations-js/1.12.0-1701054133-add-unique-constraint-to-the-sso-connector-name.js +17 -0
- package/alterations-js/1.12.0-1701245520-add-single-sign-on-enabled-flag-to-sie.d.ts +3 -0
- package/alterations-js/1.12.0-1701245520-add-single-sign-on-enabled-flag-to-sie.js +16 -0
- package/alterations-js/1.13.0-1702274830-add-new-third-party-column-to-applications-table.d.ts +3 -0
- package/alterations-js/1.13.0-1702274830-add-new-third-party-column-to-applications-table.js +16 -0
- package/alterations-js/1.13.0-1702372401-add-application-permissions-tables.d.ts +3 -0
- package/alterations-js/1.13.0-1702372401-add-application-permissions-tables.js +79 -0
- package/alterations-js/1.13.0-1702544178-sync-tenant-orgs.d.ts +18 -0
- package/alterations-js/1.13.0-1702544178-sync-tenant-orgs.js +225 -0
- package/alterations-js/1.13.0-1702871078-protected-application-type.d.ts +3 -0
- package/alterations-js/1.13.0-1702871078-protected-application-type.js +20 -0
- package/alterations-js/1.13.0-1702877515-protected-app-configs.d.ts +3 -0
- package/alterations-js/1.13.0-1702877515-protected-app-configs.js +14 -0
- package/alterations-js/1.13.0-1702978120-application-sign-in-experience-table.d.ts +3 -0
- package/alterations-js/1.13.0-1702978120-application-sign-in-experience-table.js +51 -0
- package/alterations-js/1.13.0-1703229996-daily-token-usage.d.ts +3 -0
- package/alterations-js/1.13.0-1703229996-daily-token-usage.js +51 -0
- package/alterations-js/1.13.0-1703230000-update-tenant-roles.d.ts +11 -0
- package/alterations-js/1.13.0-1703230000-update-tenant-roles.js +87 -0
- package/alterations-js/1.13.0-1704692973-remove-legacy-resources.d.ts +3 -0
- package/alterations-js/1.13.0-1704692973-remove-legacy-resources.js +124 -0
- package/alterations-js/1.13.0-1704934999-add-magic-links-table.d.ts +3 -0
- package/alterations-js/1.13.0-1704934999-add-magic-links-table.js +32 -0
- package/alterations-js/1.13.0-1704935001-add-organization-invitation-tables.d.ts +3 -0
- package/alterations-js/1.13.0-1704935001-add-organization-invitation-tables.js +72 -0
- package/alterations-js/1.13.0-1705288654-add-application-user-consent-organizations-table.d.ts +3 -0
- package/alterations-js/1.13.0-1705288654-add-application-user-consent-organizations-table.js +52 -0
- package/alterations-js/1.13.0-1705991158-update-invitation-indices.d.ts +7 -0
- package/alterations-js/1.13.0-1705991158-update-invitation-indices.js +27 -0
- package/alterations-js/1.13.0-1706449174-update-organization-invitation-column.d.ts +3 -0
- package/alterations-js/1.13.0-1706449174-update-organization-invitation-column.js +20 -0
- package/alterations-js/1.13.0-1706510290-protected-app-host-index.d.ts +3 -0
- package/alterations-js/1.13.0-1706510290-protected-app-host-index.js +17 -0
- package/alterations-js/1.13.0-1706512952-restore-get-started-page.d.ts +3 -0
- package/alterations-js/1.13.0-1706512952-restore-get-started-page.js +13 -0
- package/alterations-js/1.13.0-1706528755-remove-magic-links.d.ts +3 -0
- package/alterations-js/1.13.0-1706528755-remove-magic-links.js +41 -0
- package/alterations-js/1.13.0-1706585206-protected-app-custom-domain-unique.d.ts +3 -0
- package/alterations-js/1.13.0-1706585206-protected-app-custom-domain-unique.js +17 -0
- package/alterations-js/utils/1704934999-tables.d.ts +11 -0
- package/alterations-js/utils/1704934999-tables.js +43 -0
- package/lib/consts/index.d.ts +1 -0
- package/lib/consts/index.js +1 -0
- package/lib/consts/subscriptions.d.ts +6 -0
- package/lib/consts/subscriptions.js +7 -0
- package/lib/db-entries/application-sign-in-experience.d.ts +26 -0
- package/lib/db-entries/application-sign-in-experience.js +42 -0
- package/lib/db-entries/application-user-consent-organization-scope.d.ts +24 -0
- package/lib/db-entries/application-user-consent-organization-scope.js +29 -0
- package/lib/db-entries/application-user-consent-organization.d.ts +22 -0
- package/lib/db-entries/application-user-consent-organization.js +33 -0
- package/lib/db-entries/application-user-consent-resource-scope.d.ts +24 -0
- package/lib/db-entries/application-user-consent-resource-scope.js +29 -0
- package/lib/db-entries/application-user-consent-user-scope.d.ts +24 -0
- package/lib/db-entries/application-user-consent-user-scope.js +29 -0
- package/lib/db-entries/application.d.ts +6 -2
- package/lib/db-entries/application.js +9 -1
- package/lib/db-entries/custom-types.d.ts +8 -1
- package/lib/db-entries/custom-types.js +8 -0
- package/lib/db-entries/daily-token-usage.d.ts +20 -0
- package/lib/db-entries/daily-token-usage.js +33 -0
- package/lib/db-entries/index.d.ts +8 -0
- package/lib/db-entries/index.js +8 -0
- package/lib/db-entries/organization-invitation-role-relation.d.ts +24 -0
- package/lib/db-entries/organization-invitation-role-relation.js +29 -0
- package/lib/db-entries/organization-invitation.d.ts +53 -0
- package/lib/db-entries/organization-invitation.js +58 -0
- package/lib/db-entries/sign-in-experience.d.ts +3 -1
- package/lib/db-entries/sign-in-experience.js +4 -0
- package/lib/db-entries/sso-connector.d.ts +2 -2
- package/lib/foundations/jsonb-types/applications.d.ts +380 -0
- package/lib/foundations/jsonb-types/applications.js +29 -0
- package/lib/foundations/jsonb-types/custom-domain.d.ts +37 -89
- package/lib/foundations/jsonb-types/custom-domain.js +4 -9
- package/lib/foundations/jsonb-types/index.d.ts +1 -0
- package/lib/foundations/jsonb-types/index.js +1 -0
- package/lib/foundations/jsonb-types/sso-connector.d.ts +3 -0
- package/lib/foundations/jsonb-types/sso-connector.js +1 -0
- package/lib/models/tenants.d.ts +1 -1
- package/lib/seeds/application.d.ts +1 -1
- package/lib/seeds/application.js +3 -1
- package/lib/seeds/cloud-api.d.ts +0 -2
- package/lib/seeds/cloud-api.js +0 -3
- package/lib/seeds/management-api.d.ts +77 -6
- package/lib/seeds/management-api.js +14 -10
- package/lib/types/application.d.ts +576 -1
- package/lib/types/application.js +42 -1
- package/lib/types/connector.js +1 -1
- package/lib/types/consent.d.ts +568 -0
- package/lib/types/consent.js +47 -0
- package/lib/types/domain.d.ts +21 -65
- package/lib/types/hook.d.ts +1 -0
- package/lib/types/index.d.ts +3 -0
- package/lib/types/index.js +3 -0
- package/lib/types/interactions.d.ts +6 -6
- package/lib/types/logto-config.d.ts +32 -6
- package/lib/types/logto-config.js +12 -2
- package/lib/types/mapi-proxy.d.ts +30 -0
- package/lib/types/mapi-proxy.js +49 -0
- package/lib/types/organization.d.ts +10 -1
- package/lib/types/organization.js +4 -1
- package/lib/types/sso-connector.d.ts +63 -66
- package/lib/types/sso-connector.js +41 -7
- package/lib/types/system.d.ts +28 -1
- package/lib/types/system.js +17 -0
- package/lib/types/tenant-organization.d.ts +107 -0
- package/lib/types/tenant-organization.js +145 -0
- package/lib/types/tenant.d.ts +0 -1
- package/lib/types/tenant.js +2 -1
- package/lib/types/user-assets.d.ts +5 -5
- package/lib/types/user-assets.js +1 -0
- package/lib/types/user.d.ts +17 -15
- package/lib/types/user.js +2 -2
- package/lib/utils/domain.d.ts +10 -0
- package/lib/utils/domain.js +28 -0
- package/lib/utils/domain.test.d.ts +1 -0
- package/lib/utils/domain.test.js +34 -0
- package/lib/utils/index.d.ts +1 -0
- package/lib/utils/index.js +1 -0
- package/package.json +15 -15
- package/tables/application_sign_in_experiences.sql +15 -0
- package/tables/application_user_consent_organization_scopes.sql +14 -0
- package/tables/application_user_consent_organizations.sql +16 -0
- package/tables/application_user_consent_resource_scopes.sql +14 -0
- package/tables/application_user_consent_user_scopes.sql +13 -0
- package/tables/applications.sql +16 -1
- package/tables/daily_token_usage.sql +11 -0
- package/tables/organization_invitation_role_relations.sql +14 -0
- package/tables/organization_invitations.sql +36 -0
- package/tables/organization_role_user_relations.sql +8 -6
- package/tables/sign_in_experiences.sql +1 -0
- package/tables/sso_connectors.sql +4 -2
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { ConsoleLog } from '@logto/shared';
|
|
2
|
+
import { sql } from 'slonik';
|
|
3
|
+
|
|
4
|
+
import type { AlterationScript } from '../lib/types/alteration.js';
|
|
5
|
+
|
|
6
|
+
const consoleLog = new ConsoleLog();
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* This script update the following in the admin tenant:
|
|
10
|
+
*
|
|
11
|
+
* - Remove `owner` organization role.
|
|
12
|
+
* - Add `manage:tenant` scope to `admin` organization role.
|
|
13
|
+
* - Add `delete:data` scope to `member` organization role.
|
|
14
|
+
* - Update descriptions accordingly.
|
|
15
|
+
*/
|
|
16
|
+
const alteration: AlterationScript = {
|
|
17
|
+
up: async (pool) => {
|
|
18
|
+
// Update existing owner to admin.
|
|
19
|
+
await pool.query(sql`
|
|
20
|
+
update organization_role_user_relations
|
|
21
|
+
set organization_role_id = 'admin'
|
|
22
|
+
where tenant_id = 'admin'
|
|
23
|
+
and organization_role_id = 'owner';
|
|
24
|
+
`);
|
|
25
|
+
await pool.query(sql`
|
|
26
|
+
delete from organization_roles
|
|
27
|
+
where tenant_id = 'admin'
|
|
28
|
+
and name = 'owner';
|
|
29
|
+
`);
|
|
30
|
+
await pool.query(sql`
|
|
31
|
+
insert into organization_role_scope_relations (tenant_id, organization_role_id, organization_scope_id)
|
|
32
|
+
values
|
|
33
|
+
('admin', 'admin', 'manage-tenant'),
|
|
34
|
+
('admin', 'member', 'delete-data');
|
|
35
|
+
`);
|
|
36
|
+
await pool.query(sql`
|
|
37
|
+
update organization_roles
|
|
38
|
+
set description = 'Admin of the tenant, who has all permissions.'
|
|
39
|
+
where tenant_id = 'admin'
|
|
40
|
+
and name = 'admin';
|
|
41
|
+
`);
|
|
42
|
+
await pool.query(sql`
|
|
43
|
+
update organization_roles
|
|
44
|
+
set description = 'Member of the tenant, who has permissions to operate the tenant data, but not the tenant settings.'
|
|
45
|
+
where tenant_id = 'admin'
|
|
46
|
+
and name = 'member';
|
|
47
|
+
`);
|
|
48
|
+
},
|
|
49
|
+
down: async (pool) => {
|
|
50
|
+
// Add back owner role
|
|
51
|
+
await pool.query(sql`
|
|
52
|
+
insert into organization_roles (tenant_id, id, name, description)
|
|
53
|
+
values
|
|
54
|
+
('admin', 'owner', 'owner', 'Owner of the tenant, who has all permissions.');
|
|
55
|
+
`);
|
|
56
|
+
// Insert scope relations
|
|
57
|
+
await pool.query(sql`
|
|
58
|
+
insert into organization_role_scope_relations (tenant_id, organization_role_id, organization_scope_id)
|
|
59
|
+
values
|
|
60
|
+
('admin', 'owner', 'read-data'),
|
|
61
|
+
('admin', 'owner', 'write-data'),
|
|
62
|
+
('admin', 'owner', 'delete-data'),
|
|
63
|
+
('admin', 'owner', 'invite-member'),
|
|
64
|
+
('admin', 'owner', 'remove-member'),
|
|
65
|
+
('admin', 'owner', 'update-member-role'),
|
|
66
|
+
('admin', 'owner', 'manage-tenant');
|
|
67
|
+
`);
|
|
68
|
+
// Remove added scopes
|
|
69
|
+
await pool.query(sql`
|
|
70
|
+
delete from organization_role_scope_relations
|
|
71
|
+
where tenant_id = 'admin'
|
|
72
|
+
and (organization_role_id = 'admin' and organization_scope_id = 'manage-tenant')
|
|
73
|
+
or (organization_role_id = 'member' and organization_scope_id = 'delete-data');
|
|
74
|
+
`);
|
|
75
|
+
// Update descriptions
|
|
76
|
+
await pool.query(sql`
|
|
77
|
+
update organization_roles
|
|
78
|
+
set description = 'Admin of the tenant, who has all permissions except managing the tenant settings.'
|
|
79
|
+
where tenant_id = 'admin'
|
|
80
|
+
and name = 'admin';
|
|
81
|
+
`);
|
|
82
|
+
await pool.query(sql`
|
|
83
|
+
update organization_roles
|
|
84
|
+
set description = 'Member of the tenant, who has limited permissions on reading and writing the tenant data.'
|
|
85
|
+
where tenant_id = 'admin'
|
|
86
|
+
and name = 'member';
|
|
87
|
+
`);
|
|
88
|
+
consoleLog.warn(
|
|
89
|
+
'Original owners are not restored since the owner role has more permissions than admin, and we cannot tell which users are the original owners.'
|
|
90
|
+
);
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
export default alteration;
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { generateStandardId } from '@logto/shared/universal';
|
|
2
|
+
import { sql } from 'slonik';
|
|
3
|
+
|
|
4
|
+
import type { AlterationScript } from '../lib/types/alteration.js';
|
|
5
|
+
|
|
6
|
+
const alteration: AlterationScript = {
|
|
7
|
+
up: async (pool) => {
|
|
8
|
+
// Delete all legacy roles in the admin tenant
|
|
9
|
+
await pool.query(sql`
|
|
10
|
+
delete from roles
|
|
11
|
+
where tenant_id = 'admin'
|
|
12
|
+
and name like '%:admin'
|
|
13
|
+
and name not like 'machine:mapi:%'
|
|
14
|
+
and name != 'default:admin';
|
|
15
|
+
`);
|
|
16
|
+
// Update default role description
|
|
17
|
+
await pool.query(sql`
|
|
18
|
+
update roles
|
|
19
|
+
set description = 'Legacy user role for accessing default Management API. Used in OSS only.'
|
|
20
|
+
where tenant_id = 'admin'
|
|
21
|
+
and name = 'default:admin';
|
|
22
|
+
`);
|
|
23
|
+
// Delete `manage:tenant` scope from the Cloud API resource
|
|
24
|
+
await pool.query(sql`
|
|
25
|
+
delete from scopes
|
|
26
|
+
using resources
|
|
27
|
+
where scopes.tenant_id = 'admin'
|
|
28
|
+
and scopes.name = 'manage:tenant'
|
|
29
|
+
and scopes.resource_id = resources.id
|
|
30
|
+
and resources.indicator = 'https://cloud.logto.io/api';
|
|
31
|
+
`);
|
|
32
|
+
},
|
|
33
|
+
down: async (pool) => {
|
|
34
|
+
console.log('Add `manage:tenant` scope to the Cloud API resource');
|
|
35
|
+
await pool.query(sql`
|
|
36
|
+
insert into scopes (tenant_id, id, name, description, resource_id)
|
|
37
|
+
values ('admin', 'manage:tenant', 'manage:tenant', 'Allow managing existing tenants, including create without limitation, update, and delete.', (
|
|
38
|
+
select id from resources where tenant_id = 'admin' and indicator = 'https://cloud.logto.io/api'
|
|
39
|
+
));
|
|
40
|
+
`);
|
|
41
|
+
|
|
42
|
+
console.log('Update default role description');
|
|
43
|
+
await pool.query(sql`
|
|
44
|
+
update roles
|
|
45
|
+
set description = 'Admin tenant admin role for Logto tenant default.'
|
|
46
|
+
where tenant_id = 'admin'
|
|
47
|
+
and name = 'default:admin';
|
|
48
|
+
`);
|
|
49
|
+
|
|
50
|
+
console.log('Add legacy roles in the admin tenant');
|
|
51
|
+
const existingTenantIds = await pool.any<{ id: string }>(sql`
|
|
52
|
+
select id from tenants where id != 'default';
|
|
53
|
+
`);
|
|
54
|
+
await pool.query(sql`
|
|
55
|
+
insert into roles (tenant_id, id, name, description)
|
|
56
|
+
values ${sql.join(
|
|
57
|
+
existingTenantIds.map((tenant) => {
|
|
58
|
+
return sql`
|
|
59
|
+
(
|
|
60
|
+
'admin',
|
|
61
|
+
${`${tenant.id}:admin`},
|
|
62
|
+
${`${tenant.id}:admin`},
|
|
63
|
+
${`Admin tenant admin role for Logto tenant ${tenant.id}.`}
|
|
64
|
+
)
|
|
65
|
+
`;
|
|
66
|
+
}),
|
|
67
|
+
sql`, `
|
|
68
|
+
)};
|
|
69
|
+
`);
|
|
70
|
+
|
|
71
|
+
console.log('Restore assigned Management API scopes to the legacy roles');
|
|
72
|
+
await pool.query(sql`
|
|
73
|
+
insert into roles_scopes (tenant_id, id, role_id, scope_id)
|
|
74
|
+
values ${sql.join(
|
|
75
|
+
existingTenantIds.map((tenant) => {
|
|
76
|
+
return sql`
|
|
77
|
+
(
|
|
78
|
+
'admin',
|
|
79
|
+
${`${tenant.id}:admin`},
|
|
80
|
+
${`${tenant.id}:admin`},
|
|
81
|
+
(
|
|
82
|
+
select scopes.id from scopes
|
|
83
|
+
join resources on scopes.resource_id = resources.id
|
|
84
|
+
and resources.indicator = ${`https://${tenant.id}.logto.app/api`}
|
|
85
|
+
where scopes.tenant_id = 'admin'
|
|
86
|
+
and scopes.name = 'all'
|
|
87
|
+
)
|
|
88
|
+
)
|
|
89
|
+
`;
|
|
90
|
+
}),
|
|
91
|
+
sql`, `
|
|
92
|
+
)};
|
|
93
|
+
`);
|
|
94
|
+
|
|
95
|
+
console.log('Assign to legacy roles to users according to the tenant organization roles');
|
|
96
|
+
const adminUsersOrganizations = await pool.any<{ userId: string; organizationId: string }>(sql`
|
|
97
|
+
select user_id as "userId", organization_id as "organizationId"
|
|
98
|
+
from organization_role_user_relations
|
|
99
|
+
where tenant_id = 'admin'
|
|
100
|
+
and organization_role_id = 'admin'
|
|
101
|
+
and organization_id != 't-default'
|
|
102
|
+
and organization_id like 't-%';
|
|
103
|
+
`);
|
|
104
|
+
await pool.query(sql`
|
|
105
|
+
insert into users_roles (tenant_id, id, user_id, role_id)
|
|
106
|
+
values ${sql.join(
|
|
107
|
+
adminUsersOrganizations.map((relation) => {
|
|
108
|
+
return sql`
|
|
109
|
+
(
|
|
110
|
+
'admin',
|
|
111
|
+
${`${relation.userId}:${relation.organizationId.slice(2)}:admin`},
|
|
112
|
+
${relation.userId},
|
|
113
|
+
${`${relation.organizationId.slice(2)}:admin`}
|
|
114
|
+
)
|
|
115
|
+
`;
|
|
116
|
+
}),
|
|
117
|
+
sql`, `
|
|
118
|
+
)};
|
|
119
|
+
`);
|
|
120
|
+
|
|
121
|
+
console.log('Assign back cloud scopes to the legacy admin user');
|
|
122
|
+
await pool.query(sql`
|
|
123
|
+
insert into roles_scopes (tenant_id, id, role_id, scope_id)
|
|
124
|
+
values ${sql.join(
|
|
125
|
+
['send:sms', 'send:email', 'create:affiliate', 'manage:affiliate', 'manage:tenant'].map(
|
|
126
|
+
(scope) => {
|
|
127
|
+
return sql`
|
|
128
|
+
(
|
|
129
|
+
'admin',
|
|
130
|
+
${generateStandardId()},
|
|
131
|
+
'admin:admin',
|
|
132
|
+
(
|
|
133
|
+
select id from scopes
|
|
134
|
+
where tenant_id = 'admin'
|
|
135
|
+
and name = ${scope}
|
|
136
|
+
)
|
|
137
|
+
)
|
|
138
|
+
`;
|
|
139
|
+
}
|
|
140
|
+
),
|
|
141
|
+
sql`, `
|
|
142
|
+
)};
|
|
143
|
+
`);
|
|
144
|
+
},
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
export default alteration;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { sql } from 'slonik';
|
|
2
|
+
|
|
3
|
+
import type { AlterationScript } from '../lib/types/alteration.js';
|
|
4
|
+
|
|
5
|
+
import { applyTableRls, dropTableRls } from './utils/1704934999-tables.js';
|
|
6
|
+
|
|
7
|
+
const alteration: AlterationScript = {
|
|
8
|
+
up: async (pool) => {
|
|
9
|
+
await pool.query(sql`
|
|
10
|
+
create table magic_links (
|
|
11
|
+
tenant_id varchar(21) not null
|
|
12
|
+
references tenants (id) on update cascade on delete cascade,
|
|
13
|
+
/** The unique identifier of the link. */
|
|
14
|
+
id varchar(21) not null,
|
|
15
|
+
/** The token that can be used to verify the link. */
|
|
16
|
+
token varchar(32) not null,
|
|
17
|
+
/** The time when the link was created. */
|
|
18
|
+
created_at timestamptz not null default (now()),
|
|
19
|
+
/** The time when the link was consumed. */
|
|
20
|
+
consumed_at timestamptz,
|
|
21
|
+
primary key (id)
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
create index magic_links__token
|
|
25
|
+
on magic_links (tenant_id, token);
|
|
26
|
+
`);
|
|
27
|
+
await applyTableRls(pool, 'magic_links');
|
|
28
|
+
},
|
|
29
|
+
down: async (pool) => {
|
|
30
|
+
await dropTableRls(pool, 'magic_links');
|
|
31
|
+
await pool.query(sql`
|
|
32
|
+
drop table magic_links;
|
|
33
|
+
`);
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export default alteration;
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { sql } from 'slonik';
|
|
2
|
+
|
|
3
|
+
import type { AlterationScript } from '../lib/types/alteration.js';
|
|
4
|
+
|
|
5
|
+
import { applyTableRls, dropTableRls } from './utils/1704934999-tables.js';
|
|
6
|
+
|
|
7
|
+
const alteration: AlterationScript = {
|
|
8
|
+
up: async (pool) => {
|
|
9
|
+
await pool.query(sql`
|
|
10
|
+
create type organization_invitation_status as enum ('Pending', 'Accepted', 'Expired', 'Revoked');
|
|
11
|
+
|
|
12
|
+
create table organization_invitations (
|
|
13
|
+
tenant_id varchar(21) not null
|
|
14
|
+
references tenants (id) on update cascade on delete cascade,
|
|
15
|
+
/** The unique identifier of the invitation. */
|
|
16
|
+
id varchar(21) not null,
|
|
17
|
+
/** The user ID who sent the invitation. */
|
|
18
|
+
inviter_id varchar(21) not null,
|
|
19
|
+
/** The email address or other identifier of the invitee. */
|
|
20
|
+
invitee varchar(256) not null,
|
|
21
|
+
/** The user ID of who accepted the invitation. */
|
|
22
|
+
accepted_user_id varchar(21)
|
|
23
|
+
references users (id) on update cascade on delete cascade,
|
|
24
|
+
/** The ID of the organization to which the invitee is invited. */
|
|
25
|
+
organization_id varchar(21) not null,
|
|
26
|
+
/** The status of the invitation. */
|
|
27
|
+
status organization_invitation_status not null,
|
|
28
|
+
/** The ID of the magic link that can be used to accept the invitation. */
|
|
29
|
+
magic_link_id varchar(21)
|
|
30
|
+
references magic_links (id) on update cascade on delete cascade,
|
|
31
|
+
/** The time when the invitation was created. */
|
|
32
|
+
created_at timestamptz not null default (now()),
|
|
33
|
+
/** The time when the invitation status was last updated. */
|
|
34
|
+
updated_at timestamptz not null default (now()),
|
|
35
|
+
/** The time when the invitation expires. */
|
|
36
|
+
expires_at timestamptz not null,
|
|
37
|
+
primary key (id),
|
|
38
|
+
foreign key (tenant_id, inviter_id, organization_id)
|
|
39
|
+
references organization_user_relations (tenant_id, user_id, organization_id)
|
|
40
|
+
on update cascade on delete cascade
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
-- Ensure there is only one pending invitation for a given invitee and organization.
|
|
44
|
+
create unique index organization_invitations__invitee_organization_id
|
|
45
|
+
on organization_invitations (tenant_id, invitee, organization_id)
|
|
46
|
+
where status = 'Pending';
|
|
47
|
+
`);
|
|
48
|
+
await applyTableRls(pool, 'organization_invitations');
|
|
49
|
+
|
|
50
|
+
await pool.query(sql`
|
|
51
|
+
create table organization_invitation_role_relations (
|
|
52
|
+
tenant_id varchar(21) not null
|
|
53
|
+
references tenants (id) on update cascade on delete cascade,
|
|
54
|
+
/** The ID of the invitation. */
|
|
55
|
+
invitation_id varchar(21) not null
|
|
56
|
+
references organization_invitations (id) on update cascade on delete cascade,
|
|
57
|
+
/** The ID of the organization role. */
|
|
58
|
+
organization_role_id varchar(21) not null
|
|
59
|
+
references organization_roles (id) on update cascade on delete cascade,
|
|
60
|
+
primary key (tenant_id, invitation_id, organization_role_id)
|
|
61
|
+
);
|
|
62
|
+
`);
|
|
63
|
+
await applyTableRls(pool, 'organization_invitation_role_relations');
|
|
64
|
+
},
|
|
65
|
+
down: async (pool) => {
|
|
66
|
+
await dropTableRls(pool, 'organization_invitation_role_relations');
|
|
67
|
+
await pool.query(sql`
|
|
68
|
+
drop table organization_invitation_role_relations;
|
|
69
|
+
`);
|
|
70
|
+
await dropTableRls(pool, 'organization_invitations');
|
|
71
|
+
await pool.query(sql`
|
|
72
|
+
drop table organization_invitations;
|
|
73
|
+
drop type organization_invitation_status;
|
|
74
|
+
`);
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
export default alteration;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { type CommonQueryMethods, sql } from 'slonik';
|
|
2
|
+
|
|
3
|
+
import type { AlterationScript } from '../lib/types/alteration.js';
|
|
4
|
+
|
|
5
|
+
const getDatabaseName = async (pool: CommonQueryMethods) => {
|
|
6
|
+
const { currentDatabase } = await pool.one<{ currentDatabase: string }>(sql`
|
|
7
|
+
select current_database();
|
|
8
|
+
`);
|
|
9
|
+
|
|
10
|
+
return currentDatabase.replaceAll('-', '_');
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
const enableRls = async (pool: CommonQueryMethods, database: string, table: string) => {
|
|
14
|
+
const baseRoleId = sql.identifier([`logto_tenant_${database}`]);
|
|
15
|
+
|
|
16
|
+
await pool.query(sql`
|
|
17
|
+
create trigger set_tenant_id before insert on ${sql.identifier([table])}
|
|
18
|
+
for each row execute procedure set_tenant_id();
|
|
19
|
+
|
|
20
|
+
alter table ${sql.identifier([table])} enable row level security;
|
|
21
|
+
|
|
22
|
+
create policy ${sql.identifier([`${table}_tenant_id`])} on ${sql.identifier([table])}
|
|
23
|
+
as restrictive
|
|
24
|
+
using (tenant_id = (select id from tenants where db_user = current_user));
|
|
25
|
+
|
|
26
|
+
create policy ${sql.identifier([`${table}_modification`])} on ${sql.identifier([table])}
|
|
27
|
+
using (true);
|
|
28
|
+
|
|
29
|
+
grant select, insert, update, delete on ${sql.identifier([table])} to ${baseRoleId};
|
|
30
|
+
`);
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const alteration: AlterationScript = {
|
|
34
|
+
up: async (pool) => {
|
|
35
|
+
const database = await getDatabaseName(pool);
|
|
36
|
+
|
|
37
|
+
await pool.query(sql`
|
|
38
|
+
create table application_user_consent_organizations (
|
|
39
|
+
tenant_id varchar(21) not null
|
|
40
|
+
references tenants (id) on update cascade on delete cascade,
|
|
41
|
+
application_id varchar(21) not null
|
|
42
|
+
references applications (id) on update cascade on delete cascade,
|
|
43
|
+
organization_id varchar(21) not null,
|
|
44
|
+
user_id varchar(21) not null,
|
|
45
|
+
primary key (tenant_id, application_id, organization_id, user_id),
|
|
46
|
+
/** User's consent to an application should be synchronized with the user's membership in the organization. */
|
|
47
|
+
foreign key (tenant_id, organization_id, user_id)
|
|
48
|
+
references organization_user_relations (tenant_id, organization_id, user_id)
|
|
49
|
+
on update cascade on delete cascade
|
|
50
|
+
)
|
|
51
|
+
`);
|
|
52
|
+
|
|
53
|
+
await enableRls(pool, database, 'application_user_consent_organizations');
|
|
54
|
+
},
|
|
55
|
+
down: async (pool) => {
|
|
56
|
+
await pool.query(sql`
|
|
57
|
+
drop table application_user_consent_organizations;
|
|
58
|
+
`);
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
export default alteration;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { sql } from 'slonik';
|
|
2
|
+
|
|
3
|
+
import type { AlterationScript } from '../lib/types/alteration.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @fileoverview Separates the index (tenant_id, inviter_id, organization_id) to two indices. Also
|
|
7
|
+
* makes the inviter_id nullable.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const alteration: AlterationScript = {
|
|
11
|
+
up: async (pool) => {
|
|
12
|
+
await pool.query(sql`
|
|
13
|
+
alter table organization_invitations
|
|
14
|
+
drop constraint organization_invitations_tenant_id_inviter_id_organization_fkey,
|
|
15
|
+
add foreign key (inviter_id) references users (id) on update cascade on delete cascade,
|
|
16
|
+
add foreign key (organization_id) references organizations (id) on update cascade on delete cascade,
|
|
17
|
+
alter column inviter_id drop not null;
|
|
18
|
+
`);
|
|
19
|
+
},
|
|
20
|
+
down: async (pool) => {
|
|
21
|
+
await pool.query(sql`
|
|
22
|
+
alter table organization_invitations
|
|
23
|
+
drop constraint organization_invitations_inviter_id_fkey,
|
|
24
|
+
drop constraint organization_invitations_organization_id_fkey,
|
|
25
|
+
add foreign key (tenant_id, inviter_id, organization_id) references organization_user_relations
|
|
26
|
+
(tenant_id, user_id, organization_id) on update cascade on delete cascade,
|
|
27
|
+
alter column inviter_id set not null;
|
|
28
|
+
`);
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export default alteration;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { sql } from '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 organization_invitation_role_relations
|
|
9
|
+
rename column invitation_id to organization_invitation_id;
|
|
10
|
+
alter table organization_invitation_role_relations
|
|
11
|
+
rename constraint organization_invitation_role_relations_invitation_id_fkey to organization_invitation_role_re_organization_invitation_id_fkey;
|
|
12
|
+
`);
|
|
13
|
+
},
|
|
14
|
+
down: async (pool) => {
|
|
15
|
+
await pool.query(sql`
|
|
16
|
+
alter table organization_invitation_role_relations
|
|
17
|
+
rename column organization_invitation_id to invitation_id;
|
|
18
|
+
alter table organization_invitation_role_relations
|
|
19
|
+
rename constraint organization_invitation_role_re_organization_invitation_id_fkey to organization_invitation_role_relations_invitation_id_fkey;
|
|
20
|
+
`);
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export default alteration;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { sql } from '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
|
+
create unique index applications__protected_app_metadata_host
|
|
9
|
+
on applications (
|
|
10
|
+
(protected_app_metadata->>'host')
|
|
11
|
+
);
|
|
12
|
+
`);
|
|
13
|
+
},
|
|
14
|
+
down: async (pool) => {
|
|
15
|
+
await pool.query(sql`
|
|
16
|
+
drop index applications__protected_app_metadata_host;
|
|
17
|
+
`);
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export default alteration;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { sql } from '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
|
+
update users
|
|
9
|
+
set custom_data = custom_data #- '{adminConsolePreferences, getStartedHidden}';
|
|
10
|
+
`);
|
|
11
|
+
},
|
|
12
|
+
down: async () => {
|
|
13
|
+
// Do nothing as the data change is not reversible
|
|
14
|
+
},
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export default alteration;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { sql } from 'slonik';
|
|
2
|
+
|
|
3
|
+
import type { AlterationScript } from '../lib/types/alteration.js';
|
|
4
|
+
|
|
5
|
+
import { applyTableRls, dropTableRls } from './utils/1704934999-tables.js';
|
|
6
|
+
|
|
7
|
+
const alteration: AlterationScript = {
|
|
8
|
+
up: async (pool) => {
|
|
9
|
+
await pool.query(sql`
|
|
10
|
+
alter table organization_invitations
|
|
11
|
+
drop column magic_link_id;
|
|
12
|
+
`);
|
|
13
|
+
await dropTableRls(pool, 'magic_links');
|
|
14
|
+
await pool.query(sql`
|
|
15
|
+
drop table magic_links;
|
|
16
|
+
`);
|
|
17
|
+
},
|
|
18
|
+
down: async (pool) => {
|
|
19
|
+
await pool.query(sql`
|
|
20
|
+
create table magic_links (
|
|
21
|
+
tenant_id varchar(21) not null
|
|
22
|
+
references tenants (id) on update cascade on delete cascade,
|
|
23
|
+
/** The unique identifier of the link. */
|
|
24
|
+
id varchar(21) not null,
|
|
25
|
+
/** The token that can be used to verify the link. */
|
|
26
|
+
token varchar(32) not null,
|
|
27
|
+
/** The time when the link was created. */
|
|
28
|
+
created_at timestamptz not null default (now()),
|
|
29
|
+
/** The time when the link was consumed. */
|
|
30
|
+
consumed_at timestamptz,
|
|
31
|
+
primary key (id)
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
create index magic_links__token
|
|
35
|
+
on magic_links (tenant_id, token);
|
|
36
|
+
`);
|
|
37
|
+
await applyTableRls(pool, 'magic_links');
|
|
38
|
+
await pool.query(sql`
|
|
39
|
+
alter table organization_invitations
|
|
40
|
+
add column magic_link_id varchar(21)
|
|
41
|
+
references magic_links (id) on update cascade on delete cascade;
|
|
42
|
+
`);
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export default alteration;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { sql } from '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
|
+
create unique index applications__protected_app_metadata_custom_domain
|
|
9
|
+
on applications (
|
|
10
|
+
(protected_app_metadata->'customDomains'->0->>'domain')
|
|
11
|
+
);
|
|
12
|
+
`);
|
|
13
|
+
},
|
|
14
|
+
down: async (pool) => {
|
|
15
|
+
await pool.query(sql`
|
|
16
|
+
drop index applications__protected_app_metadata_custom_domain;
|
|
17
|
+
`);
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export default alteration;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { type CommonQueryMethods, sql } from 'slonik';
|
|
2
|
+
|
|
3
|
+
const getId = (value: string) => sql.identifier([value]);
|
|
4
|
+
|
|
5
|
+
const getDatabaseName = async (pool: CommonQueryMethods) => {
|
|
6
|
+
const { currentDatabase } = await pool.one<{ currentDatabase: string }>(sql`
|
|
7
|
+
select current_database();
|
|
8
|
+
`);
|
|
9
|
+
|
|
10
|
+
return currentDatabase.replaceAll('-', '_');
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* A function to call after the table is created. It will apply the necessary row-level security
|
|
15
|
+
* policies and triggers to the table.
|
|
16
|
+
*/
|
|
17
|
+
export const applyTableRls = async (pool: CommonQueryMethods, tableName: string) => {
|
|
18
|
+
const database = await getDatabaseName(pool);
|
|
19
|
+
const baseRoleId = getId(`logto_tenant_${database}`);
|
|
20
|
+
const table = getId(tableName);
|
|
21
|
+
|
|
22
|
+
await pool.query(sql`
|
|
23
|
+
create trigger set_tenant_id before insert on ${table}
|
|
24
|
+
for each row execute procedure set_tenant_id();
|
|
25
|
+
|
|
26
|
+
alter table ${table} enable row level security;
|
|
27
|
+
|
|
28
|
+
create policy ${getId(`${tableName}_tenant_id`)} on ${table}
|
|
29
|
+
as restrictive
|
|
30
|
+
using (tenant_id = (select id from tenants where db_user = current_user));
|
|
31
|
+
|
|
32
|
+
create policy ${getId(`${tableName}_modification`)} on ${table}
|
|
33
|
+
using (true);
|
|
34
|
+
|
|
35
|
+
grant select, insert, update, delete on ${table} to ${baseRoleId};
|
|
36
|
+
`);
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* A function to call before the table is dropped. It will remove the row-level security policies
|
|
41
|
+
* and triggers from the table.
|
|
42
|
+
*/
|
|
43
|
+
export const dropTableRls = async (pool: CommonQueryMethods, tableName: string) => {
|
|
44
|
+
await pool.query(sql`
|
|
45
|
+
drop policy ${getId(`${tableName}_modification`)} on ${getId(tableName)};
|
|
46
|
+
drop policy ${getId(`${tableName}_tenant_id`)} on ${getId(tableName)};
|
|
47
|
+
drop trigger set_tenant_id on ${getId(tableName)};
|
|
48
|
+
`);
|
|
49
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# Alteration utils
|
|
2
|
+
|
|
3
|
+
This directory contains utilities for database alteration scripts.
|
|
4
|
+
|
|
5
|
+
Due to the nature of alteration, all utility functions should be maintained in an immutable way. This means when a function needs to be changed, a new file should be created with the following name format:
|
|
6
|
+
|
|
7
|
+
`<timestamp>-<function-or-purpose>.js`
|
|
8
|
+
|
|
9
|
+
The timestamp should be in the format of epoch time in seconds. The original file should be kept for historical purposes.
|