@logto/schemas 1.10.0 → 1.11.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.10.1-1695647183-update-private-key-type.ts +108 -0
- package/alterations/1.10.1-1696657546-organization-tables.ts +150 -0
- package/alterations/1.10.1-1697683802-add-sso-connectors-table.ts +66 -0
- package/alterations/1.10.1-1698646271-add-organization-created-flag.ts +75 -0
- package/alterations/1.10.1-1698820410-add-user-sso-identities-table.ts +61 -0
- package/alterations/1.10.1-1698910485-user-logto-data.ts +20 -0
- package/alterations/1.11.0-1699422979-add-sso-connector-id-col-to-user-sso-identities-table.ts +18 -0
- package/alterations/1.11.0-1699598903-remove-sso-only-column-in-sso-connectors-table.ts +18 -0
- package/alterations-js/1.10.1-1695647183-update-private-key-type.d.ts +3 -0
- package/alterations-js/1.10.1-1695647183-update-private-key-type.js +50 -0
- package/alterations-js/1.10.1-1696657546-organization-tables.d.ts +3 -0
- package/alterations-js/1.10.1-1696657546-organization-tables.js +136 -0
- package/alterations-js/1.10.1-1697683802-add-sso-connectors-table.d.ts +3 -0
- package/alterations-js/1.10.1-1697683802-add-sso-connectors-table.js +58 -0
- package/alterations-js/1.10.1-1698646271-add-organization-created-flag.d.ts +3 -0
- package/alterations-js/1.10.1-1698646271-add-organization-created-flag.js +26 -0
- package/alterations-js/1.10.1-1698820410-add-user-sso-identities-table.d.ts +4 -0
- package/alterations-js/1.10.1-1698820410-add-user-sso-identities-table.js +53 -0
- package/alterations-js/1.10.1-1698910485-user-logto-data.d.ts +3 -0
- package/alterations-js/1.10.1-1698910485-user-logto-data.js +16 -0
- package/alterations-js/1.11.0-1699422979-add-sso-connector-id-col-to-user-sso-identities-table.d.ts +3 -0
- package/alterations-js/1.11.0-1699422979-add-sso-connector-id-col-to-user-sso-identities-table.js +14 -0
- package/alterations-js/1.11.0-1699598903-remove-sso-only-column-in-sso-connectors-table.d.ts +3 -0
- package/alterations-js/1.11.0-1699598903-remove-sso-only-column-in-sso-connectors-table.js +14 -0
- package/lib/db-entries/application.d.ts +7 -1
- package/lib/db-entries/application.js +1 -0
- package/lib/db-entries/applications-role.d.ts +7 -1
- package/lib/db-entries/applications-role.js +1 -0
- package/lib/db-entries/connector.d.ts +7 -1
- package/lib/db-entries/connector.js +1 -0
- package/lib/db-entries/custom-phrase.d.ts +7 -1
- package/lib/db-entries/custom-phrase.js +1 -0
- package/lib/db-entries/daily-active-user.d.ts +7 -1
- package/lib/db-entries/daily-active-user.js +1 -0
- package/lib/db-entries/domain.d.ts +7 -1
- package/lib/db-entries/domain.js +1 -0
- package/lib/db-entries/hook.d.ts +7 -1
- package/lib/db-entries/hook.js +1 -0
- package/lib/db-entries/index.d.ts +8 -0
- package/lib/db-entries/index.js +8 -0
- package/lib/db-entries/log.d.ts +7 -1
- package/lib/db-entries/log.js +1 -0
- package/lib/db-entries/logto-config.d.ts +10 -4
- package/lib/db-entries/logto-config.js +4 -3
- package/lib/db-entries/oidc-model-instance.d.ts +7 -1
- package/lib/db-entries/oidc-model-instance.js +1 -0
- package/lib/db-entries/organization-role-scope-relation.d.ts +20 -0
- package/lib/db-entries/organization-role-scope-relation.js +29 -0
- package/lib/db-entries/organization-role-user-relation.d.ts +22 -0
- package/lib/db-entries/organization-role-user-relation.js +33 -0
- package/lib/db-entries/organization-role.d.ts +28 -0
- package/lib/db-entries/organization-role.js +33 -0
- package/lib/db-entries/organization-scope.d.ts +28 -0
- package/lib/db-entries/organization-scope.js +33 -0
- package/lib/db-entries/organization-user-relation.d.ts +20 -0
- package/lib/db-entries/organization-user-relation.js +29 -0
- package/lib/db-entries/organization.d.ts +32 -0
- package/lib/db-entries/organization.js +37 -0
- package/lib/db-entries/passcode.d.ts +7 -1
- package/lib/db-entries/passcode.js +1 -0
- package/lib/db-entries/resource.d.ts +7 -1
- package/lib/db-entries/resource.js +1 -0
- package/lib/db-entries/role.d.ts +7 -1
- package/lib/db-entries/role.js +1 -0
- package/lib/db-entries/roles-scope.d.ts +7 -1
- package/lib/db-entries/roles-scope.js +1 -0
- package/lib/db-entries/scope.d.ts +7 -1
- package/lib/db-entries/scope.js +1 -0
- package/lib/db-entries/sentinel-activity.d.ts +7 -1
- package/lib/db-entries/sentinel-activity.js +1 -0
- package/lib/db-entries/service-log.d.ts +7 -1
- package/lib/db-entries/service-log.js +1 -0
- package/lib/db-entries/sign-in-experience.d.ts +7 -1
- package/lib/db-entries/sign-in-experience.js +1 -0
- package/lib/db-entries/sso-connector.d.ts +46 -0
- package/lib/db-entries/sso-connector.js +54 -0
- package/lib/db-entries/system.d.ts +7 -1
- package/lib/db-entries/system.js +1 -0
- package/lib/db-entries/user-sso-identity.d.ts +32 -0
- package/lib/db-entries/user-sso-identity.js +50 -0
- package/lib/db-entries/user.d.ts +9 -1
- package/lib/db-entries/user.js +5 -0
- package/lib/db-entries/users-role.d.ts +7 -1
- package/lib/db-entries/users-role.js +1 -0
- package/lib/db-entries/verification-status.d.ts +7 -1
- package/lib/db-entries/verification-status.js +1 -0
- package/lib/foundations/index.d.ts +1 -1
- package/lib/foundations/index.js +1 -1
- package/lib/foundations/jsonb-types/custom-domain.d.ts +134 -0
- package/lib/foundations/jsonb-types/custom-domain.js +36 -0
- package/lib/foundations/jsonb-types/hooks.d.ts +32 -0
- package/lib/foundations/jsonb-types/hooks.js +24 -0
- package/lib/foundations/jsonb-types/index.d.ts +15 -0
- package/lib/foundations/jsonb-types/index.js +16 -0
- package/lib/foundations/jsonb-types/logs.d.ts +106 -0
- package/lib/foundations/jsonb-types/logs.js +20 -0
- package/lib/foundations/jsonb-types/oidc-module.d.ts +80 -0
- package/lib/foundations/jsonb-types/oidc-module.js +54 -0
- package/lib/foundations/jsonb-types/phrases.d.ts +5 -0
- package/lib/foundations/jsonb-types/phrases.js +2 -0
- package/lib/foundations/jsonb-types/sentinel.d.ts +27 -0
- package/lib/foundations/jsonb-types/sentinel.js +28 -0
- package/lib/foundations/jsonb-types/sign-in-experience.d.ts +118 -0
- package/lib/foundations/jsonb-types/sign-in-experience.js +56 -0
- package/lib/foundations/jsonb-types/sso-connector.d.ts +14 -0
- package/lib/foundations/jsonb-types/sso-connector.js +6 -0
- package/lib/foundations/jsonb-types/users.d.ts +285 -0
- package/lib/foundations/jsonb-types/users.js +47 -0
- package/lib/foundations/schemas.d.ts +11 -13
- package/lib/models/tenants.d.ts +8 -16
- package/lib/models/tenants.js +1 -6
- package/lib/seeds/logto-config.js +1 -0
- package/lib/types/application.d.ts +51 -1
- package/lib/types/application.js +7 -1
- package/lib/types/connector.d.ts +516 -2360
- package/lib/types/domain.d.ts +65 -27
- package/lib/types/hook.d.ts +15 -16
- package/lib/types/index.d.ts +4 -0
- package/lib/types/index.js +4 -0
- package/lib/types/interactions.d.ts +502 -10
- package/lib/types/interactions.js +83 -5
- package/lib/types/log/interaction.d.ts +4 -3
- package/lib/types/log/interaction.js +1 -0
- package/lib/types/logto-config.d.ts +74 -2
- package/lib/types/logto-config.js +38 -3
- package/lib/types/mfa.d.ts +211 -0
- package/lib/types/mfa.js +62 -0
- package/lib/types/organization.d.ts +48 -0
- package/lib/types/organization.js +21 -0
- package/lib/types/role.d.ts +5 -3
- package/lib/types/scope.d.ts +12 -27
- package/lib/types/sso-connector.d.ts +137 -0
- package/lib/types/sso-connector.js +24 -0
- package/lib/types/system.d.ts +26 -7
- package/lib/types/system.js +8 -0
- package/lib/types/tenant.d.ts +5 -0
- package/lib/types/tenant.js +6 -0
- package/lib/types/user-assets.d.ts +2 -2
- package/lib/types/user.d.ts +209 -66
- package/lib/types/user.js +8 -2
- package/package.json +7 -7
- package/tables/logto_configs.sql +1 -1
- package/tables/organization_role_scope_relations.sql +12 -0
- package/tables/organization_role_user_relations.sql +14 -0
- package/tables/organization_roles.sql +19 -0
- package/tables/organization_scopes.sql +19 -0
- package/tables/organization_user_relations.sql +12 -0
- package/tables/organizations.sql +19 -0
- package/tables/sso_connectors.sql +28 -0
- package/tables/user_sso_identities.sql +20 -0
- package/tables/users.sql +1 -0
- package/lib/foundations/jsonb-types.d.ts +0 -673
- package/lib/foundations/jsonb-types.js +0 -260
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { generateStandardId } from '@logto/shared';
|
|
2
|
+
import type { DatabaseTransactionConnection } from 'slonik';
|
|
3
|
+
import { sql } from 'slonik';
|
|
4
|
+
|
|
5
|
+
import type { AlterationScript } from '../lib/types/alteration.js';
|
|
6
|
+
|
|
7
|
+
const targetConfigKeys = ['oidc.cookieKeys', 'oidc.privateKeys'];
|
|
8
|
+
|
|
9
|
+
type OldPrivateKeyData = {
|
|
10
|
+
tenantId: string;
|
|
11
|
+
value: string[];
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
type PrivateKey = {
|
|
15
|
+
id: string;
|
|
16
|
+
value: string;
|
|
17
|
+
createdAt: number;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
type NewPrivateKeyData = {
|
|
21
|
+
tenantId: string;
|
|
22
|
+
value: PrivateKey[];
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Alternate string array private signing keys to JSON array
|
|
27
|
+
* "oidc.cookieKeys": string[] -> PrivateKey[]
|
|
28
|
+
* "oidc.privateKeys": string[] -> PrivateKey[]
|
|
29
|
+
* @param configKey oidc.cookieKeys | oidc.privateKeys
|
|
30
|
+
* @param logtoConfig existing private key data for a specific tenant
|
|
31
|
+
* @param pool postgres database connection pool
|
|
32
|
+
*/
|
|
33
|
+
const alterPrivateKeysInLogtoConfig = async (
|
|
34
|
+
configKey: string,
|
|
35
|
+
logtoConfig: OldPrivateKeyData,
|
|
36
|
+
pool: DatabaseTransactionConnection
|
|
37
|
+
) => {
|
|
38
|
+
const { tenantId, value: oldPrivateKey } = logtoConfig;
|
|
39
|
+
|
|
40
|
+
// Use tenant creation time as `createdAt` timestamp for new private keys
|
|
41
|
+
const tenantData = await pool.maybeOne<{ createdAt: number }>(
|
|
42
|
+
sql`select * from tenants where id = ${tenantId}`
|
|
43
|
+
);
|
|
44
|
+
const newPrivateKeyData: PrivateKey[] = oldPrivateKey.map((key) => ({
|
|
45
|
+
id: generateStandardId(),
|
|
46
|
+
value: key,
|
|
47
|
+
createdAt: Math.floor((tenantData?.createdAt ?? Date.now()) / 1000),
|
|
48
|
+
}));
|
|
49
|
+
|
|
50
|
+
await pool.query(
|
|
51
|
+
sql`update logto_configs set value = ${JSON.stringify(
|
|
52
|
+
newPrivateKeyData
|
|
53
|
+
)} where tenant_id = ${tenantId} and key = ${configKey}`
|
|
54
|
+
);
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Rollback JSON array private signing keys to string array
|
|
59
|
+
* "oidc.cookieKeys": PrivateKey[] -> string[]
|
|
60
|
+
* "oidc.privateKeys": PrivateKey[] -> string[]
|
|
61
|
+
* @param configKey oidc.cookieKeys | oidc.privateKeys
|
|
62
|
+
* @param logtoConfig new private key data for a specific tenant
|
|
63
|
+
* @param pool postgres database connection pool
|
|
64
|
+
*/
|
|
65
|
+
const rollbackPrivateKeysInLogtoConfig = async (
|
|
66
|
+
configKey: string,
|
|
67
|
+
logtoConfig: NewPrivateKeyData,
|
|
68
|
+
pool: DatabaseTransactionConnection
|
|
69
|
+
) => {
|
|
70
|
+
const { tenantId, value: newPrivateKeyData } = logtoConfig;
|
|
71
|
+
|
|
72
|
+
const oldPrivateKeys = newPrivateKeyData.map(({ value }) => value);
|
|
73
|
+
|
|
74
|
+
await pool.query(
|
|
75
|
+
sql`update logto_configs set value = ${JSON.stringify(
|
|
76
|
+
oldPrivateKeys
|
|
77
|
+
)} where tenant_id = ${tenantId} and key = ${configKey}`
|
|
78
|
+
);
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
const alteration: AlterationScript = {
|
|
82
|
+
up: async (pool) => {
|
|
83
|
+
await Promise.all(
|
|
84
|
+
targetConfigKeys.map(async (configKey) => {
|
|
85
|
+
const rows = await pool.many<OldPrivateKeyData>(
|
|
86
|
+
sql`select * from logto_configs where key = ${configKey}`
|
|
87
|
+
);
|
|
88
|
+
await Promise.all(
|
|
89
|
+
rows.map(async (row) => alterPrivateKeysInLogtoConfig(configKey, row, pool))
|
|
90
|
+
);
|
|
91
|
+
})
|
|
92
|
+
);
|
|
93
|
+
},
|
|
94
|
+
down: async (pool) => {
|
|
95
|
+
await Promise.all(
|
|
96
|
+
targetConfigKeys.map(async (configKey) => {
|
|
97
|
+
const rows = await pool.many<NewPrivateKeyData>(
|
|
98
|
+
sql`select * from logto_configs where key = ${configKey}`
|
|
99
|
+
);
|
|
100
|
+
await Promise.all(
|
|
101
|
+
rows.map(async (row) => rollbackPrivateKeysInLogtoConfig(configKey, row, pool))
|
|
102
|
+
);
|
|
103
|
+
})
|
|
104
|
+
);
|
|
105
|
+
},
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
export default alteration;
|
|
@@ -0,0 +1,150 @@
|
|
|
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 organizations (
|
|
39
|
+
tenant_id varchar(21) not null
|
|
40
|
+
references tenants (id) on update cascade on delete cascade,
|
|
41
|
+
/** The globally unique identifier of the organization. */
|
|
42
|
+
id varchar(21) not null,
|
|
43
|
+
/** The organization's name for display. */
|
|
44
|
+
name varchar(128) not null,
|
|
45
|
+
/** A brief description of the organization. */
|
|
46
|
+
description varchar(256),
|
|
47
|
+
/** When the organization was created. */
|
|
48
|
+
created_at timestamptz not null default(now()),
|
|
49
|
+
primary key (id)
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
create index organizations__id
|
|
53
|
+
on organizations (tenant_id, id);
|
|
54
|
+
`);
|
|
55
|
+
await enableRls(pool, database, 'organizations');
|
|
56
|
+
|
|
57
|
+
await pool.query(sql`
|
|
58
|
+
create table organization_roles (
|
|
59
|
+
tenant_id varchar(21) not null
|
|
60
|
+
references tenants (id) on update cascade on delete cascade,
|
|
61
|
+
/** The globally unique identifier of the organization role. */
|
|
62
|
+
id varchar(21) not null,
|
|
63
|
+
/** The organization role's name, unique within the organization template. */
|
|
64
|
+
name varchar(128) not null,
|
|
65
|
+
/** A brief description of the organization role. */
|
|
66
|
+
description varchar(256),
|
|
67
|
+
primary key (id),
|
|
68
|
+
constraint organization_roles__name
|
|
69
|
+
unique (tenant_id, name)
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
create index organization_roles__id
|
|
73
|
+
on organization_roles (tenant_id, id);
|
|
74
|
+
`);
|
|
75
|
+
await enableRls(pool, database, 'organization_roles');
|
|
76
|
+
|
|
77
|
+
await pool.query(sql`
|
|
78
|
+
create table organization_scopes (
|
|
79
|
+
tenant_id varchar(21) not null
|
|
80
|
+
references tenants (id) on update cascade on delete cascade,
|
|
81
|
+
/** The globally unique identifier of the organization scope. */
|
|
82
|
+
id varchar(21) not null,
|
|
83
|
+
/** The organization scope's name, unique within the organization template. */
|
|
84
|
+
name varchar(128) not null,
|
|
85
|
+
/** A brief description of the organization scope. */
|
|
86
|
+
description varchar(256),
|
|
87
|
+
primary key (id),
|
|
88
|
+
constraint organization_scopes__name
|
|
89
|
+
unique (tenant_id, name)
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
create index organization_scopes__id
|
|
93
|
+
on organization_scopes (tenant_id, id);
|
|
94
|
+
`);
|
|
95
|
+
await enableRls(pool, database, 'organization_scopes');
|
|
96
|
+
|
|
97
|
+
await pool.query(sql`
|
|
98
|
+
create table organization_role_user_relations (
|
|
99
|
+
tenant_id varchar(21) not null
|
|
100
|
+
references tenants (id) on update cascade on delete cascade,
|
|
101
|
+
organization_id varchar(21) not null
|
|
102
|
+
references organizations (id) on update cascade on delete cascade,
|
|
103
|
+
organization_role_id varchar(21) not null
|
|
104
|
+
references organization_roles (id) on update cascade on delete cascade,
|
|
105
|
+
user_id varchar(21) not null
|
|
106
|
+
references users (id) on update cascade on delete cascade,
|
|
107
|
+
primary key (tenant_id, organization_id, organization_role_id, user_id)
|
|
108
|
+
);
|
|
109
|
+
`);
|
|
110
|
+
await enableRls(pool, database, 'organization_role_user_relations');
|
|
111
|
+
|
|
112
|
+
await pool.query(sql`
|
|
113
|
+
create table organization_role_scope_relations (
|
|
114
|
+
tenant_id varchar(21) not null
|
|
115
|
+
references tenants (id) on update cascade on delete cascade,
|
|
116
|
+
organization_role_id varchar(21) not null
|
|
117
|
+
references organization_roles (id) on update cascade on delete cascade,
|
|
118
|
+
organization_scope_id varchar(21) not null
|
|
119
|
+
references organization_scopes (id) on update cascade on delete cascade,
|
|
120
|
+
primary key (tenant_id, organization_role_id, organization_scope_id)
|
|
121
|
+
);
|
|
122
|
+
`);
|
|
123
|
+
await enableRls(pool, database, 'organization_role_scope_relations');
|
|
124
|
+
|
|
125
|
+
await pool.query(sql`
|
|
126
|
+
create table organization_user_relations (
|
|
127
|
+
tenant_id varchar(21) not null
|
|
128
|
+
references tenants (id) on update cascade on delete cascade,
|
|
129
|
+
organization_id varchar(21) not null
|
|
130
|
+
references organizations (id) on update cascade on delete cascade,
|
|
131
|
+
user_id varchar(21) not null
|
|
132
|
+
references users (id) on update cascade on delete cascade,
|
|
133
|
+
primary key (tenant_id, organization_id, user_id)
|
|
134
|
+
);
|
|
135
|
+
`);
|
|
136
|
+
await enableRls(pool, database, 'organization_user_relations');
|
|
137
|
+
},
|
|
138
|
+
down: async (pool) => {
|
|
139
|
+
await pool.query(sql`
|
|
140
|
+
drop table organization_role_scope_relations;
|
|
141
|
+
drop table organization_role_user_relations;
|
|
142
|
+
drop table organization_scopes;
|
|
143
|
+
drop table organization_roles;
|
|
144
|
+
drop table organization_user_relations;
|
|
145
|
+
drop table organizations;
|
|
146
|
+
`);
|
|
147
|
+
},
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
export default alteration;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { type CommonQueryMethods, sql } from 'slonik';
|
|
2
|
+
|
|
3
|
+
import type { AlterationScript } from '../lib/types/alteration.js';
|
|
4
|
+
|
|
5
|
+
const getId = (value: string) => sql.identifier([value]);
|
|
6
|
+
|
|
7
|
+
const getDatabaseName = async (pool: CommonQueryMethods) => {
|
|
8
|
+
const { currentDatabase } = await pool.one<{ currentDatabase: string }>(sql`
|
|
9
|
+
select current_database();
|
|
10
|
+
`);
|
|
11
|
+
|
|
12
|
+
return currentDatabase.replaceAll('-', '_');
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const alteration: AlterationScript = {
|
|
16
|
+
up: async (pool) => {
|
|
17
|
+
const database = await getDatabaseName(pool);
|
|
18
|
+
const baseRoleId = getId(`logto_tenant_${database}`);
|
|
19
|
+
|
|
20
|
+
await pool.query(sql`
|
|
21
|
+
create table sso_connectors (
|
|
22
|
+
tenant_id varchar(21) not null
|
|
23
|
+
references tenants (id) on update cascade on delete cascade,
|
|
24
|
+
id varchar(128) not null,
|
|
25
|
+
provider_name varchar(128) not null,
|
|
26
|
+
connector_name varchar(128) not null,
|
|
27
|
+
config jsonb not null default '{}'::jsonb,
|
|
28
|
+
domains jsonb not null default '[]'::jsonb,
|
|
29
|
+
branding jsonb not null default '{}'::jsonb,
|
|
30
|
+
sync_profile boolean not null default FALSE,
|
|
31
|
+
sso_only boolean not null default FALSE,
|
|
32
|
+
created_at timestamptz not null default(now()),
|
|
33
|
+
primary key (id)
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
create index sso_connectors__id
|
|
37
|
+
on sso_connectors (tenant_id, id);
|
|
38
|
+
|
|
39
|
+
create index sso_connectors__id__provider_name
|
|
40
|
+
on sso_connectors (tenant_id, id, provider_name);
|
|
41
|
+
|
|
42
|
+
create trigger set_tenant_id before insert on sso_connectors
|
|
43
|
+
for each row execute procedure set_tenant_id();
|
|
44
|
+
|
|
45
|
+
alter table sso_connectors enable row level security;
|
|
46
|
+
|
|
47
|
+
create policy sso_connectors_tenant_id on sso_connectors
|
|
48
|
+
as restrictive
|
|
49
|
+
using (tenant_id = (select id from tenants where db_user = current_user));
|
|
50
|
+
|
|
51
|
+
create policy sso_connectors_modification on sso_connectors
|
|
52
|
+
using (true);
|
|
53
|
+
|
|
54
|
+
grant select, insert, update, delete on sso_connectors to ${baseRoleId};
|
|
55
|
+
`);
|
|
56
|
+
},
|
|
57
|
+
down: async (pool) => {
|
|
58
|
+
await pool.query(sql`
|
|
59
|
+
drop policy sso_connectors_modification on sso_connectors;
|
|
60
|
+
drop policy sso_connectors_tenant_id on sso_connectors;
|
|
61
|
+
drop table sso_connectors;
|
|
62
|
+
`);
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
export default alteration;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import type { DatabaseTransactionConnection } from 'slonik';
|
|
2
|
+
import { sql } from 'slonik';
|
|
3
|
+
|
|
4
|
+
import type { AlterationScript } from '../lib/types/alteration.js';
|
|
5
|
+
|
|
6
|
+
const adminConsoleConfigKey = 'adminConsole';
|
|
7
|
+
|
|
8
|
+
type OldAdminConsoleData = {
|
|
9
|
+
signInExperienceCustomized: boolean;
|
|
10
|
+
} & Record<string, unknown>;
|
|
11
|
+
|
|
12
|
+
type OldLogtoAdminConsoleConfig = {
|
|
13
|
+
tenantId: string;
|
|
14
|
+
value: OldAdminConsoleData;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
type NewAdminConsoleData = {
|
|
18
|
+
signInExperienceCustomized: boolean;
|
|
19
|
+
organizationCreated: boolean;
|
|
20
|
+
} & Record<string, unknown>;
|
|
21
|
+
|
|
22
|
+
type NewLogtoAdminConsoleConfig = {
|
|
23
|
+
tenantId: string;
|
|
24
|
+
value: NewAdminConsoleData;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const alterAdminConsoleData = async (
|
|
28
|
+
logtoConfig: OldLogtoAdminConsoleConfig,
|
|
29
|
+
pool: DatabaseTransactionConnection
|
|
30
|
+
) => {
|
|
31
|
+
const { tenantId, value: oldAdminConsoleConfig } = logtoConfig;
|
|
32
|
+
|
|
33
|
+
const newAdminConsoleData: NewAdminConsoleData = {
|
|
34
|
+
...oldAdminConsoleConfig,
|
|
35
|
+
organizationCreated: false,
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
await pool.query(
|
|
39
|
+
sql`update logto_configs set value = ${JSON.stringify(
|
|
40
|
+
newAdminConsoleData
|
|
41
|
+
)} where tenant_id = ${tenantId} and key = ${adminConsoleConfigKey}`
|
|
42
|
+
);
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const rollbackAdminConsoleData = async (
|
|
46
|
+
logtoConfig: NewLogtoAdminConsoleConfig,
|
|
47
|
+
pool: DatabaseTransactionConnection
|
|
48
|
+
) => {
|
|
49
|
+
const { tenantId, value: newAdminConsoleConfig } = logtoConfig;
|
|
50
|
+
|
|
51
|
+
const { organizationCreated, ...oldAdminConsoleData } = newAdminConsoleConfig;
|
|
52
|
+
|
|
53
|
+
await pool.query(
|
|
54
|
+
sql`update logto_configs set value = ${JSON.stringify(
|
|
55
|
+
oldAdminConsoleData
|
|
56
|
+
)} where tenant_id = ${tenantId} and key = ${adminConsoleConfigKey}`
|
|
57
|
+
);
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const alteration: AlterationScript = {
|
|
61
|
+
up: async (pool) => {
|
|
62
|
+
const rows = await pool.many<OldLogtoAdminConsoleConfig>(
|
|
63
|
+
sql`select * from logto_configs where key = ${adminConsoleConfigKey}`
|
|
64
|
+
);
|
|
65
|
+
await Promise.all(rows.map(async (row) => alterAdminConsoleData(row, pool)));
|
|
66
|
+
},
|
|
67
|
+
down: async (pool) => {
|
|
68
|
+
const rows = await pool.many<NewLogtoAdminConsoleConfig>(
|
|
69
|
+
sql`select * from logto_configs where key = ${adminConsoleConfigKey}`
|
|
70
|
+
);
|
|
71
|
+
await Promise.all(rows.map(async (row) => rollbackAdminConsoleData(row, pool)));
|
|
72
|
+
},
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
export default alteration;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { type CommonQueryMethods, sql } from 'slonik';
|
|
2
|
+
|
|
3
|
+
import type { AlterationScript } from '../lib/types/alteration.js';
|
|
4
|
+
|
|
5
|
+
const getId = (value: string) => sql.identifier([value]);
|
|
6
|
+
|
|
7
|
+
const getDatabaseName = async (pool: CommonQueryMethods) => {
|
|
8
|
+
const { currentDatabase } = await pool.one<{ currentDatabase: string }>(sql`
|
|
9
|
+
select current_database();
|
|
10
|
+
`);
|
|
11
|
+
|
|
12
|
+
return currentDatabase.replaceAll('-', '_');
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
/** The alteration script for adding `sso_identities` column to the users table. */
|
|
16
|
+
const alteration: AlterationScript = {
|
|
17
|
+
up: async (pool) => {
|
|
18
|
+
const database = await getDatabaseName(pool);
|
|
19
|
+
const baseRoleId = getId(`logto_tenant_${database}`);
|
|
20
|
+
|
|
21
|
+
await pool.query(sql`
|
|
22
|
+
create table user_sso_identities (
|
|
23
|
+
tenant_id varchar(21) not null
|
|
24
|
+
references tenants (id) on update cascade on delete cascade,
|
|
25
|
+
id varchar(21) not null,
|
|
26
|
+
user_id varchar(12) not null
|
|
27
|
+
references users (id) on update cascade on delete cascade,
|
|
28
|
+
issuer varchar(256) not null,
|
|
29
|
+
identity_id varchar(128) not null,
|
|
30
|
+
detail jsonb not null default '{}'::jsonb,
|
|
31
|
+
created_at timestamp not null default(now()),
|
|
32
|
+
primary key (id),
|
|
33
|
+
constraint user_sso_identities__issuer__identity_id
|
|
34
|
+
unique (tenant_id, issuer, identity_id)
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
create trigger set_tenant_id before insert on user_sso_identities
|
|
38
|
+
for each row execute procedure set_tenant_id();
|
|
39
|
+
|
|
40
|
+
alter table user_sso_identities enable row level security;
|
|
41
|
+
|
|
42
|
+
create policy user_sso_identities_tenant_id on user_sso_identities
|
|
43
|
+
as restrictive
|
|
44
|
+
using (tenant_id = (select id from tenants where db_user = current_user));
|
|
45
|
+
|
|
46
|
+
create policy user_sso_identities_modification on user_sso_identities
|
|
47
|
+
using (true);
|
|
48
|
+
|
|
49
|
+
grant select, insert, update, delete on user_sso_identities to ${baseRoleId};
|
|
50
|
+
`);
|
|
51
|
+
},
|
|
52
|
+
down: async (pool) => {
|
|
53
|
+
await pool.query(sql`
|
|
54
|
+
drop policy user_sso_identities_modification on user_sso_identities;
|
|
55
|
+
drop policy user_sso_identities_tenant_id on user_sso_identities;
|
|
56
|
+
drop table user_sso_identities;
|
|
57
|
+
`);
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
export default alteration;
|
|
@@ -0,0 +1,20 @@
|
|
|
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 users
|
|
9
|
+
add column if not exists logto_config jsonb not null default '{}'::jsonb;
|
|
10
|
+
`);
|
|
11
|
+
},
|
|
12
|
+
down: async (pool) => {
|
|
13
|
+
await pool.query(sql`
|
|
14
|
+
alter table users
|
|
15
|
+
drop column logto_config;
|
|
16
|
+
`);
|
|
17
|
+
},
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export default alteration;
|
package/alterations/1.11.0-1699422979-add-sso-connector-id-col-to-user-sso-identities-table.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
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 user_sso_identities add column sso_connector_id varchar(128) not null references sso_connectors (id) on update cascade on delete cascade;
|
|
9
|
+
`);
|
|
10
|
+
},
|
|
11
|
+
down: async (pool) => {
|
|
12
|
+
await pool.query(sql`
|
|
13
|
+
alter table user_sso_identities drop column sso_connector_id;
|
|
14
|
+
`);
|
|
15
|
+
},
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export default alteration;
|
|
@@ -0,0 +1,18 @@
|
|
|
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 sso_connectors drop column sso_only;
|
|
9
|
+
`);
|
|
10
|
+
},
|
|
11
|
+
down: async (pool) => {
|
|
12
|
+
await pool.query(sql`
|
|
13
|
+
alter table sso_connectors add column sso_only boolean not null default FALSE;
|
|
14
|
+
`);
|
|
15
|
+
},
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export default alteration;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { generateStandardId } from '@logto/shared';
|
|
2
|
+
import { sql } from 'slonik';
|
|
3
|
+
const targetConfigKeys = ['oidc.cookieKeys', 'oidc.privateKeys'];
|
|
4
|
+
/**
|
|
5
|
+
* Alternate string array private signing keys to JSON array
|
|
6
|
+
* "oidc.cookieKeys": string[] -> PrivateKey[]
|
|
7
|
+
* "oidc.privateKeys": string[] -> PrivateKey[]
|
|
8
|
+
* @param configKey oidc.cookieKeys | oidc.privateKeys
|
|
9
|
+
* @param logtoConfig existing private key data for a specific tenant
|
|
10
|
+
* @param pool postgres database connection pool
|
|
11
|
+
*/
|
|
12
|
+
const alterPrivateKeysInLogtoConfig = async (configKey, logtoConfig, pool) => {
|
|
13
|
+
const { tenantId, value: oldPrivateKey } = logtoConfig;
|
|
14
|
+
// Use tenant creation time as `createdAt` timestamp for new private keys
|
|
15
|
+
const tenantData = await pool.maybeOne(sql `select * from tenants where id = ${tenantId}`);
|
|
16
|
+
const newPrivateKeyData = oldPrivateKey.map((key) => ({
|
|
17
|
+
id: generateStandardId(),
|
|
18
|
+
value: key,
|
|
19
|
+
createdAt: Math.floor((tenantData?.createdAt ?? Date.now()) / 1000),
|
|
20
|
+
}));
|
|
21
|
+
await pool.query(sql `update logto_configs set value = ${JSON.stringify(newPrivateKeyData)} where tenant_id = ${tenantId} and key = ${configKey}`);
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* Rollback JSON array private signing keys to string array
|
|
25
|
+
* "oidc.cookieKeys": PrivateKey[] -> string[]
|
|
26
|
+
* "oidc.privateKeys": PrivateKey[] -> string[]
|
|
27
|
+
* @param configKey oidc.cookieKeys | oidc.privateKeys
|
|
28
|
+
* @param logtoConfig new private key data for a specific tenant
|
|
29
|
+
* @param pool postgres database connection pool
|
|
30
|
+
*/
|
|
31
|
+
const rollbackPrivateKeysInLogtoConfig = async (configKey, logtoConfig, pool) => {
|
|
32
|
+
const { tenantId, value: newPrivateKeyData } = logtoConfig;
|
|
33
|
+
const oldPrivateKeys = newPrivateKeyData.map(({ value }) => value);
|
|
34
|
+
await pool.query(sql `update logto_configs set value = ${JSON.stringify(oldPrivateKeys)} where tenant_id = ${tenantId} and key = ${configKey}`);
|
|
35
|
+
};
|
|
36
|
+
const alteration = {
|
|
37
|
+
up: async (pool) => {
|
|
38
|
+
await Promise.all(targetConfigKeys.map(async (configKey) => {
|
|
39
|
+
const rows = await pool.many(sql `select * from logto_configs where key = ${configKey}`);
|
|
40
|
+
await Promise.all(rows.map(async (row) => alterPrivateKeysInLogtoConfig(configKey, row, pool)));
|
|
41
|
+
}));
|
|
42
|
+
},
|
|
43
|
+
down: async (pool) => {
|
|
44
|
+
await Promise.all(targetConfigKeys.map(async (configKey) => {
|
|
45
|
+
const rows = await pool.many(sql `select * from logto_configs where key = ${configKey}`);
|
|
46
|
+
await Promise.all(rows.map(async (row) => rollbackPrivateKeysInLogtoConfig(configKey, row, pool)));
|
|
47
|
+
}));
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
export default alteration;
|