@logto/schemas 1.44.0 → 1.45.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.45.0-1791531333-add-tenant-viewer-role.ts +38 -0
- package/alterations-js/1.45.0-1791531333-add-tenant-viewer-role.js +32 -0
- package/lib/consts/license.d.ts +1 -1
- package/lib/types/system.d.ts +1 -1
- package/lib/types/system.js +1 -1
- package/lib/types/tenant-organization.d.ts +4 -2
- package/lib/types/tenant-organization.js +6 -2
- package/lib/types/tenant-organization.test.d.ts +1 -0
- package/lib/types/tenant-organization.test.js +16 -0
- package/lib/utils/management-api.d.ts +5 -0
- package/lib/utils/management-api.js +7 -1
- package/package.json +3 -3
|
@@ -0,0 +1,38 @@
|
|
|
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
|
+
// Cloud region databases other than the admin tenant's own have no `admin` tenant to hold the role.
|
|
8
|
+
const adminTenant = await pool.maybeOne(sql`
|
|
9
|
+
select id from tenants where id = 'admin'
|
|
10
|
+
`);
|
|
11
|
+
|
|
12
|
+
if (!adminTenant) {
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
await pool.query(sql`
|
|
17
|
+
update organization_scopes
|
|
18
|
+
set description = 'Write the tenant data.'
|
|
19
|
+
where tenant_id = 'admin' and id = 'write-data';
|
|
20
|
+
insert into organization_roles (tenant_id, id, name, description, type)
|
|
21
|
+
values ('admin', 'viewer', 'viewer', 'Viewer of the tenant, who can read the tenant data and members but cannot change anything.', 'User');
|
|
22
|
+
insert into organization_role_scope_relations (tenant_id, organization_role_id, organization_scope_id)
|
|
23
|
+
values ('admin', 'viewer', 'read-data'),
|
|
24
|
+
('admin', 'viewer', 'read-member');
|
|
25
|
+
`);
|
|
26
|
+
},
|
|
27
|
+
down: async (pool) => {
|
|
28
|
+
await pool.query(sql`
|
|
29
|
+
delete from organization_roles
|
|
30
|
+
where tenant_id = 'admin' and id = 'viewer';
|
|
31
|
+
update organization_scopes
|
|
32
|
+
set description = 'Write the tenant data, including creating and updating the tenant.'
|
|
33
|
+
where tenant_id = 'admin' and id = 'write-data';
|
|
34
|
+
`);
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export default alteration;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { sql } from '@silverhand/slonik';
|
|
2
|
+
const alteration = {
|
|
3
|
+
up: async (pool) => {
|
|
4
|
+
// Cloud region databases other than the admin tenant's own have no `admin` tenant to hold the role.
|
|
5
|
+
const adminTenant = await pool.maybeOne(sql `
|
|
6
|
+
select id from tenants where id = 'admin'
|
|
7
|
+
`);
|
|
8
|
+
if (!adminTenant) {
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
await pool.query(sql `
|
|
12
|
+
update organization_scopes
|
|
13
|
+
set description = 'Write the tenant data.'
|
|
14
|
+
where tenant_id = 'admin' and id = 'write-data';
|
|
15
|
+
insert into organization_roles (tenant_id, id, name, description, type)
|
|
16
|
+
values ('admin', 'viewer', 'viewer', 'Viewer of the tenant, who can read the tenant data and members but cannot change anything.', 'User');
|
|
17
|
+
insert into organization_role_scope_relations (tenant_id, organization_role_id, organization_scope_id)
|
|
18
|
+
values ('admin', 'viewer', 'read-data'),
|
|
19
|
+
('admin', 'viewer', 'read-member');
|
|
20
|
+
`);
|
|
21
|
+
},
|
|
22
|
+
down: async (pool) => {
|
|
23
|
+
await pool.query(sql `
|
|
24
|
+
delete from organization_roles
|
|
25
|
+
where tenant_id = 'admin' and id = 'viewer';
|
|
26
|
+
update organization_scopes
|
|
27
|
+
set description = 'Write the tenant data, including creating and updating the tenant.'
|
|
28
|
+
where tenant_id = 'admin' and id = 'write-data';
|
|
29
|
+
`);
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
export default alteration;
|
package/lib/consts/license.d.ts
CHANGED
|
@@ -107,7 +107,7 @@ export declare const licenseQuotaOverridesGuard: z.ZodObject<{
|
|
|
107
107
|
}>;
|
|
108
108
|
export type LicenseQuotaOverrides = z.infer<typeof licenseQuotaOverridesGuard>;
|
|
109
109
|
/**
|
|
110
|
-
* The payload of a license key, i.e. the claims of the
|
|
110
|
+
* The payload of a license key, i.e. the claims of the ES256-signed JWT an operator installs on a
|
|
111
111
|
* self-hosted instance.
|
|
112
112
|
*
|
|
113
113
|
* The signature is what makes the payload trustworthy, so none of it is editable after issuing.
|
package/lib/types/system.d.ts
CHANGED
|
@@ -298,7 +298,7 @@ export declare const cloudflareGuard: Readonly<{
|
|
|
298
298
|
* one license no matter how many instances share the database.
|
|
299
299
|
*/
|
|
300
300
|
export declare const installedLicenseGuard: z.ZodObject<{
|
|
301
|
-
/** The raw
|
|
301
|
+
/** The raw ES256-signed license key JWT, exactly as it was installed. */
|
|
302
302
|
jwt: z.ZodString;
|
|
303
303
|
/** When the key was installed, as an ISO 8601 timestamp. */
|
|
304
304
|
installedAt: z.ZodString;
|
package/lib/types/system.js
CHANGED
|
@@ -162,7 +162,7 @@ export const cloudflareGuard = Object.freeze({
|
|
|
162
162
|
* one license no matter how many instances share the database.
|
|
163
163
|
*/
|
|
164
164
|
export const installedLicenseGuard = z.object({
|
|
165
|
-
/** The raw
|
|
165
|
+
/** The raw ES256-signed license key JWT, exactly as it was installed. */
|
|
166
166
|
jwt: z.string(),
|
|
167
167
|
/** When the key was installed, as an ISO 8601 timestamp. */
|
|
168
168
|
installedAt: z.string(),
|
|
@@ -39,7 +39,7 @@ export declare const getTenantOrganizationCreateData: (tenantId: string) => Read
|
|
|
39
39
|
export declare enum TenantScope {
|
|
40
40
|
/** Read the tenant data. */
|
|
41
41
|
ReadData = "read:data",
|
|
42
|
-
/** Write the tenant data
|
|
42
|
+
/** Write the tenant data. */
|
|
43
43
|
WriteData = "write:data",
|
|
44
44
|
/** Delete data of the tenant. */
|
|
45
45
|
DeleteData = "delete:data",
|
|
@@ -83,7 +83,9 @@ export declare enum TenantRole {
|
|
|
83
83
|
/** Admin of the tenant, who has all permissions. */
|
|
84
84
|
Admin = "admin",
|
|
85
85
|
/** Collaborator of the tenant, who has permissions to operate the tenant data, but not the tenant settings. */
|
|
86
|
-
Collaborator = "collaborator"
|
|
86
|
+
Collaborator = "collaborator",
|
|
87
|
+
/** Viewer of the tenant, who can read the tenant data and members but cannot change anything. */
|
|
88
|
+
Viewer = "viewer"
|
|
87
89
|
}
|
|
88
90
|
/**
|
|
89
91
|
* Given a tenant role, return the corresponding organization role data in the admin tenant.
|
|
@@ -50,7 +50,7 @@ export var TenantScope;
|
|
|
50
50
|
(function (TenantScope) {
|
|
51
51
|
/** Read the tenant data. */
|
|
52
52
|
TenantScope["ReadData"] = "read:data";
|
|
53
|
-
/** Write the tenant data
|
|
53
|
+
/** Write the tenant data. */
|
|
54
54
|
TenantScope["WriteData"] = "write:data";
|
|
55
55
|
/** Delete data of the tenant. */
|
|
56
56
|
TenantScope["DeleteData"] = "delete:data";
|
|
@@ -92,7 +92,7 @@ export const getTenantScope = (scope) => Object.freeze({
|
|
|
92
92
|
});
|
|
93
93
|
const tenantScopeDescriptions = Object.freeze({
|
|
94
94
|
[TenantScope.ReadData]: 'Read the tenant data.',
|
|
95
|
-
[TenantScope.WriteData]: 'Write the tenant data
|
|
95
|
+
[TenantScope.WriteData]: 'Write the tenant data.',
|
|
96
96
|
[TenantScope.DeleteData]: 'Delete data of the tenant.',
|
|
97
97
|
[TenantScope.ReadMember]: 'Read members of the tenant.',
|
|
98
98
|
[TenantScope.InviteMember]: 'Invite members to the tenant.',
|
|
@@ -112,10 +112,13 @@ export var TenantRole;
|
|
|
112
112
|
TenantRole["Admin"] = "admin";
|
|
113
113
|
/** Collaborator of the tenant, who has permissions to operate the tenant data, but not the tenant settings. */
|
|
114
114
|
TenantRole["Collaborator"] = "collaborator";
|
|
115
|
+
/** Viewer of the tenant, who can read the tenant data and members but cannot change anything. */
|
|
116
|
+
TenantRole["Viewer"] = "viewer";
|
|
115
117
|
})(TenantRole || (TenantRole = {}));
|
|
116
118
|
const tenantRoleDescriptions = Object.freeze({
|
|
117
119
|
[TenantRole.Admin]: 'Admin of the tenant, who has all permissions.',
|
|
118
120
|
[TenantRole.Collaborator]: 'Collaborator of the tenant, who has permissions to operate the tenant data, but not the tenant settings.',
|
|
121
|
+
[TenantRole.Viewer]: 'Viewer of the tenant, who can read the tenant data and members but cannot change anything.',
|
|
119
122
|
});
|
|
120
123
|
/**
|
|
121
124
|
* Given a tenant role, return the corresponding organization role data in the admin tenant.
|
|
@@ -155,4 +158,5 @@ export const tenantRoleScopes = Object.freeze({
|
|
|
155
158
|
TenantScope.DeleteData,
|
|
156
159
|
TenantScope.ReadMember,
|
|
157
160
|
],
|
|
161
|
+
[TenantRole.Viewer]: [TenantScope.ReadData, TenantScope.ReadMember],
|
|
158
162
|
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { TenantRole, TenantScope, tenantRoleScopes } from './tenant-organization.js';
|
|
3
|
+
describe('tenantRoleScopes', () => {
|
|
4
|
+
it('grants each tenant role its scopes', () => {
|
|
5
|
+
expect(tenantRoleScopes).toEqual({
|
|
6
|
+
[TenantRole.Admin]: Object.values(TenantScope),
|
|
7
|
+
[TenantRole.Collaborator]: [
|
|
8
|
+
TenantScope.ReadData,
|
|
9
|
+
TenantScope.WriteData,
|
|
10
|
+
TenantScope.DeleteData,
|
|
11
|
+
TenantScope.ReadMember,
|
|
12
|
+
],
|
|
13
|
+
[TenantRole.Viewer]: [TenantScope.ReadData, TenantScope.ReadMember],
|
|
14
|
+
});
|
|
15
|
+
});
|
|
16
|
+
});
|
|
@@ -1 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The pattern of Management API resource indicators, e.g. `https://default.logto.app/api`. It is
|
|
3
|
+
* valid in both JavaScript and PostgreSQL regular expressions.
|
|
4
|
+
*/
|
|
5
|
+
export declare const managementApiIndicatorPattern: string;
|
|
1
6
|
export declare const isManagementApi: (indicator: string) => boolean;
|
|
@@ -1 +1,7 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* The pattern of Management API resource indicators, e.g. `https://default.logto.app/api`. It is
|
|
3
|
+
* valid in both JavaScript and PostgreSQL regular expressions.
|
|
4
|
+
*/
|
|
5
|
+
export const managementApiIndicatorPattern = String.raw `^https://[^.]+\.logto\.app/api$`;
|
|
6
|
+
const managementApiIndicatorRegExp = new RegExp(managementApiIndicatorPattern);
|
|
7
|
+
export const isManagementApi = (indicator) => managementApiIndicatorRegExp.test(indicator);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@logto/schemas",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.45.0",
|
|
4
4
|
"author": "Silverhand Inc. <contact@silverhand.io>",
|
|
5
5
|
"license": "MPL-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -66,11 +66,11 @@
|
|
|
66
66
|
"@withtyped/server": "^0.14.0",
|
|
67
67
|
"nanoid": "^5.0.9",
|
|
68
68
|
"@logto/connector-kit": "^5.1.2",
|
|
69
|
-
"@logto/phrases": "^1.32.0",
|
|
70
69
|
"@logto/language-kit": "^1.4.0",
|
|
70
|
+
"@logto/core-kit": "^2.13.0",
|
|
71
71
|
"@logto/phrases-experience": "^1.15.0",
|
|
72
72
|
"@logto/shared": "^3.4.3",
|
|
73
|
-
"@logto/
|
|
73
|
+
"@logto/phrases": "^1.32.1"
|
|
74
74
|
},
|
|
75
75
|
"peerDependencies": {
|
|
76
76
|
"zod": "3.24.3"
|