@logto/schemas 1.38.0 → 1.40.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.39.0-1774752400-add-delete-account-url.ts +20 -0
- package/alterations/1.39.0-1774770686-add-account-center-custom-css.ts +20 -0
- package/alterations/1.39.0-1776502301-add-sign-up-profile-fields.ts +20 -0
- package/alterations/1.40.0-1776516232-add-account-center-profile-fields.ts +20 -0
- package/alterations/1.40.0-1778318116-add-custom-ui-csp-to-sie.ts +20 -0
- package/alterations/1.40.0-1778500000-add-organization-user-relations-user-id-index.ts +41 -0
- package/alterations/1.40.0-1778500001-add-organization-role-user-relations-org-user-index.ts +43 -0
- package/alterations/1.40.0-1779421396-add-application-access-control-schema.ts +90 -0
- package/alterations-js/1.39.0-1774752400-add-delete-account-url.js +16 -0
- package/alterations-js/1.39.0-1774770686-add-account-center-custom-css.js +16 -0
- package/alterations-js/1.39.0-1776502301-add-sign-up-profile-fields.js +16 -0
- package/alterations-js/1.40.0-1776516232-add-account-center-profile-fields.js +16 -0
- package/alterations-js/1.40.0-1778318116-add-custom-ui-csp-to-sie.js +16 -0
- package/alterations-js/1.40.0-1778500000-add-organization-user-relations-user-id-index.js +37 -0
- package/alterations-js/1.40.0-1778500001-add-organization-role-user-relations-org-user-index.js +39 -0
- package/alterations-js/1.40.0-1779421396-add-application-access-control-schema.js +82 -0
- package/lib/consts/application.d.ts +1 -0
- package/lib/consts/application.js +1 -0
- package/lib/consts/index.d.ts +1 -0
- package/lib/consts/index.js +1 -0
- package/lib/db-entries/account-center.d.ts +14 -2
- package/lib/db-entries/account-center.js +13 -1
- package/lib/db-entries/application-access-control-org-role-relation.d.ts +22 -0
- package/lib/db-entries/application-access-control-org-role-relation.js +33 -0
- package/lib/db-entries/application-access-control-organization-relation.d.ts +20 -0
- package/lib/db-entries/application-access-control-organization-relation.js +29 -0
- package/lib/db-entries/application-access-control-user-relation.d.ts +20 -0
- package/lib/db-entries/application-access-control-user-relation.js +29 -0
- package/lib/db-entries/application-access-control-user-role-relation.d.ts +20 -0
- package/lib/db-entries/application-access-control-user-role-relation.js +29 -0
- package/lib/db-entries/application.d.ts +3 -1
- package/lib/db-entries/application.js +4 -0
- package/lib/db-entries/index.d.ts +4 -0
- package/lib/db-entries/index.js +4 -0
- package/lib/db-entries/sign-in-experience.d.ts +8 -2
- package/lib/db-entries/sign-in-experience.js +9 -1
- package/lib/foundations/jsonb-types/account-centers.d.ts +27 -0
- package/lib/foundations/jsonb-types/account-centers.js +12 -0
- package/lib/foundations/jsonb-types/applications.d.ts +3 -0
- package/lib/foundations/jsonb-types/applications.js +4 -0
- package/lib/foundations/jsonb-types/applications.test.d.ts +1 -0
- package/lib/foundations/jsonb-types/applications.test.js +23 -0
- package/lib/foundations/jsonb-types/sign-in-experience.d.ts +27 -1
- package/lib/foundations/jsonb-types/sign-in-experience.js +5 -0
- package/lib/foundations/jsonb-types/sign-in-experience.test.d.ts +1 -0
- package/lib/foundations/jsonb-types/sign-in-experience.test.js +18 -0
- package/lib/seeds/application.js +2 -0
- package/lib/seeds/sign-in-experience.d.ts +13 -1
- package/lib/seeds/sign-in-experience.js +10 -1
- package/lib/seeds/sign-in-experience.test.d.ts +1 -0
- package/lib/seeds/sign-in-experience.test.js +27 -0
- package/lib/types/alteration.d.ts +5 -0
- package/lib/types/application.d.ts +101 -2
- package/lib/types/application.js +55 -0
- package/lib/types/application.test.d.ts +1 -0
- package/lib/types/application.test.js +120 -0
- package/lib/types/consent.d.ts +6 -0
- package/lib/types/custom-profile-fields.d.ts +7 -13
- package/lib/types/custom-profile-fields.js +6 -13
- package/lib/types/logto-config/index.d.ts +93 -2
- package/lib/types/logto-config/index.js +22 -4
- package/lib/types/logto-config/index.test.d.ts +1 -0
- package/lib/types/logto-config/index.test.js +29 -0
- package/lib/types/logto-config/jwt-customizer.d.ts +74 -0
- package/lib/types/logto-config/jwt-customizer.js +1 -0
- package/lib/types/logto-config/jwt-customizer.test.js +14 -2
- package/lib/types/onboarding.d.ts +93 -1
- package/lib/types/onboarding.js +22 -1
- package/lib/types/saml-application.d.ts +3 -0
- package/lib/types/sign-in-experience.d.ts +23 -2
- package/lib/types/sign-in-experience.js +1 -0
- package/lib/types/system.d.ts +46 -7
- package/lib/types/system.js +9 -0
- package/lib/types/user-assets.d.ts +1 -1
- package/lib/types/user-logto-config.d.ts +11 -0
- package/lib/types/user-logto-config.js +6 -0
- package/lib/types/user-sessions.d.ts +2516 -0
- package/lib/types/user-sessions.js +21 -0
- package/lib/utils/index.d.ts +1 -0
- package/lib/utils/index.js +1 -0
- package/lib/utils/oidc-private-key.d.ts +88 -0
- package/lib/utils/oidc-private-key.js +163 -0
- package/lib/utils/oidc-private-key.test.d.ts +1 -0
- package/lib/utils/oidc-private-key.test.js +128 -0
- package/package.json +6 -6
- package/tables/account_centers.sql +6 -0
- package/tables/application_access_control_org_role_relations.sql +16 -0
- package/tables/application_access_control_organization_relations.sql +12 -0
- package/tables/application_access_control_user_relations.sql +12 -0
- package/tables/application_access_control_user_role_relations.sql +14 -0
- package/tables/applications.sql +1 -0
- package/tables/organization_role_user_relations.sql +3 -0
- package/tables/organization_user_relations.sql +3 -0
- package/tables/sign_in_experiences.sql +3 -0
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { createAdminTenantSignInExperience, createDefaultSignInExperience, } from './sign-in-experience.js';
|
|
3
|
+
import { adminTenantId } from './tenant.js';
|
|
4
|
+
describe('createAdminTenantSignInExperience', () => {
|
|
5
|
+
it('seeds an empty passwordPolicy by default', () => {
|
|
6
|
+
const row = createAdminTenantSignInExperience();
|
|
7
|
+
expect(row.passwordPolicy).toEqual({});
|
|
8
|
+
});
|
|
9
|
+
it('seeds an empty passwordPolicy when option is explicitly false', () => {
|
|
10
|
+
const row = createAdminTenantSignInExperience({ disablePwnedPasswordCheck: false });
|
|
11
|
+
expect(row.passwordPolicy).toEqual({});
|
|
12
|
+
});
|
|
13
|
+
it('seeds rejects.pwned=false when disablePwnedPasswordCheck is true', () => {
|
|
14
|
+
const row = createAdminTenantSignInExperience({ disablePwnedPasswordCheck: true });
|
|
15
|
+
expect(row.passwordPolicy).toEqual({ rejects: { pwned: false } });
|
|
16
|
+
});
|
|
17
|
+
it('still targets the admin tenant id when the option is set', () => {
|
|
18
|
+
const row = createAdminTenantSignInExperience({ disablePwnedPasswordCheck: true });
|
|
19
|
+
expect(row.tenantId).toBe(adminTenantId);
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
describe('createDefaultSignInExperience (unchanged contract)', () => {
|
|
23
|
+
it('still has a two-parameter signature and seeds an empty passwordPolicy', () => {
|
|
24
|
+
const row = createDefaultSignInExperience('some-tenant-id', false);
|
|
25
|
+
expect(row.passwordPolicy).toEqual({});
|
|
26
|
+
});
|
|
27
|
+
});
|
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
import type { CommonQueryMethods, DatabaseTransactionConnection } from '@silverhand/slonik';
|
|
2
|
+
/**
|
|
3
|
+
* IMPORTANT: Logto Cloud has a parallel `AlterationScript` type in `@logto/cloud-alterations`
|
|
4
|
+
* (logto-cloud repo: `packages/cloud-alterations/src/types.ts`).
|
|
5
|
+
* Any changes to this type must be synchronized with the Cloud type definition.
|
|
6
|
+
*/
|
|
2
7
|
export type AlterationScript = {
|
|
3
8
|
/**
|
|
4
9
|
* Optional hook that runs before `up` outside of a transaction.
|
|
@@ -48,6 +48,7 @@ export declare const featuredApplicationGuard: z.ZodObject<Pick<{
|
|
|
48
48
|
pageRules: {
|
|
49
49
|
path: string;
|
|
50
50
|
}[];
|
|
51
|
+
additionalScopes?: (UserScope.CustomData | UserScope.Identities | UserScope.Roles | UserScope.Organizations | UserScope.OrganizationRoles)[] | undefined;
|
|
51
52
|
customDomains?: {
|
|
52
53
|
status: import("../index.js").DomainStatus;
|
|
53
54
|
domain: string;
|
|
@@ -76,6 +77,7 @@ export declare const featuredApplicationGuard: z.ZodObject<Pick<{
|
|
|
76
77
|
pageRules: {
|
|
77
78
|
path: string;
|
|
78
79
|
}[];
|
|
80
|
+
additionalScopes?: (UserScope.CustomData | UserScope.Identities | UserScope.Roles | UserScope.Organizations | UserScope.OrganizationRoles)[] | undefined;
|
|
79
81
|
customDomains?: {
|
|
80
82
|
status: import("../index.js").DomainStatus;
|
|
81
83
|
domain: string;
|
|
@@ -100,6 +102,7 @@ export declare const featuredApplicationGuard: z.ZodObject<Pick<{
|
|
|
100
102
|
} | null>;
|
|
101
103
|
customData: z.ZodType<import("@withtyped/server").JsonObject, z.ZodTypeDef, import("@withtyped/server").JsonObject>;
|
|
102
104
|
isThirdParty: z.ZodType<boolean, z.ZodTypeDef, boolean>;
|
|
105
|
+
appLevelAccessControlEnabled: z.ZodType<boolean, z.ZodTypeDef, boolean>;
|
|
103
106
|
createdAt: z.ZodType<number, z.ZodTypeDef, number>;
|
|
104
107
|
}, "type" | "name" | "id">, "strip", z.ZodTypeAny, {
|
|
105
108
|
type: import("../db-entries/custom-types.js").ApplicationType;
|
|
@@ -144,6 +147,7 @@ export declare const applicationCreateGuard: z.ZodObject<{
|
|
|
144
147
|
pageRules: {
|
|
145
148
|
path: string;
|
|
146
149
|
}[];
|
|
150
|
+
additionalScopes?: (UserScope.CustomData | UserScope.Identities | UserScope.Roles | UserScope.Organizations | UserScope.OrganizationRoles)[] | undefined;
|
|
147
151
|
customDomains?: {
|
|
148
152
|
status: import("../index.js").DomainStatus;
|
|
149
153
|
domain: string;
|
|
@@ -172,6 +176,7 @@ export declare const applicationCreateGuard: z.ZodObject<{
|
|
|
172
176
|
pageRules: {
|
|
173
177
|
path: string;
|
|
174
178
|
}[];
|
|
179
|
+
additionalScopes?: (UserScope.CustomData | UserScope.Identities | UserScope.Roles | UserScope.Organizations | UserScope.OrganizationRoles)[] | undefined;
|
|
175
180
|
customDomains?: {
|
|
176
181
|
status: import("../index.js").DomainStatus;
|
|
177
182
|
domain: string;
|
|
@@ -251,6 +256,7 @@ export declare const applicationPatchGuard: z.ZodObject<Omit<{
|
|
|
251
256
|
pageRules: {
|
|
252
257
|
path: string;
|
|
253
258
|
}[];
|
|
259
|
+
additionalScopes?: (UserScope.CustomData | UserScope.Identities | UserScope.Roles | UserScope.Organizations | UserScope.OrganizationRoles)[] | undefined;
|
|
254
260
|
customDomains?: {
|
|
255
261
|
status: import("../index.js").DomainStatus;
|
|
256
262
|
domain: string;
|
|
@@ -279,6 +285,7 @@ export declare const applicationPatchGuard: z.ZodObject<Omit<{
|
|
|
279
285
|
pageRules: {
|
|
280
286
|
path: string;
|
|
281
287
|
}[];
|
|
288
|
+
additionalScopes?: (UserScope.CustomData | UserScope.Identities | UserScope.Roles | UserScope.Organizations | UserScope.OrganizationRoles)[] | undefined;
|
|
282
289
|
customDomains?: {
|
|
283
290
|
status: import("../index.js").DomainStatus;
|
|
284
291
|
domain: string;
|
|
@@ -319,6 +326,98 @@ export declare const applicationPatchGuard: z.ZodObject<Omit<{
|
|
|
319
326
|
customClientMetadata?: import("../index.js").CustomClientMetadata;
|
|
320
327
|
protectedAppMetadata?: import("../index.js").ProtectedAppMetadata | null;
|
|
321
328
|
}>;
|
|
329
|
+
/** The guard for one organization role access-control rule group. */
|
|
330
|
+
export declare const applicationAccessControlOrganizationRoleRuleGuard: z.ZodObject<{
|
|
331
|
+
organizationId: z.ZodString;
|
|
332
|
+
organizationRoleIds: z.ZodPipeline<z.ZodEffects<z.ZodArray<z.ZodString, "many">, string[], string[]>, z.ZodArray<z.ZodString, "many">>;
|
|
333
|
+
}, "strip", z.ZodTypeAny, {
|
|
334
|
+
organizationId: string;
|
|
335
|
+
organizationRoleIds: string[];
|
|
336
|
+
}, {
|
|
337
|
+
organizationId: string;
|
|
338
|
+
organizationRoleIds: string[];
|
|
339
|
+
}>;
|
|
340
|
+
/** The guard for application-level access control rule payloads. */
|
|
341
|
+
export declare const applicationAccessControlGuard: z.ZodPipeline<z.ZodEffects<z.ZodObject<{
|
|
342
|
+
userIds: z.ZodPipeline<z.ZodEffects<z.ZodArray<z.ZodString, "many">, string[], string[]>, z.ZodArray<z.ZodString, "many">>;
|
|
343
|
+
userRoleIds: z.ZodPipeline<z.ZodEffects<z.ZodArray<z.ZodString, "many">, string[], string[]>, z.ZodArray<z.ZodString, "many">>;
|
|
344
|
+
organizationIds: z.ZodPipeline<z.ZodEffects<z.ZodArray<z.ZodString, "many">, string[], string[]>, z.ZodArray<z.ZodString, "many">>;
|
|
345
|
+
organizationRoleRules: z.ZodArray<z.ZodObject<{
|
|
346
|
+
organizationId: z.ZodString;
|
|
347
|
+
organizationRoleIds: z.ZodPipeline<z.ZodEffects<z.ZodArray<z.ZodString, "many">, string[], string[]>, z.ZodArray<z.ZodString, "many">>;
|
|
348
|
+
}, "strip", z.ZodTypeAny, {
|
|
349
|
+
organizationId: string;
|
|
350
|
+
organizationRoleIds: string[];
|
|
351
|
+
}, {
|
|
352
|
+
organizationId: string;
|
|
353
|
+
organizationRoleIds: string[];
|
|
354
|
+
}>, "many">;
|
|
355
|
+
}, "strip", z.ZodTypeAny, {
|
|
356
|
+
userIds: string[];
|
|
357
|
+
userRoleIds: string[];
|
|
358
|
+
organizationIds: string[];
|
|
359
|
+
organizationRoleRules: {
|
|
360
|
+
organizationId: string;
|
|
361
|
+
organizationRoleIds: string[];
|
|
362
|
+
}[];
|
|
363
|
+
}, {
|
|
364
|
+
userIds: string[];
|
|
365
|
+
userRoleIds: string[];
|
|
366
|
+
organizationIds: string[];
|
|
367
|
+
organizationRoleRules: {
|
|
368
|
+
organizationId: string;
|
|
369
|
+
organizationRoleIds: string[];
|
|
370
|
+
}[];
|
|
371
|
+
}>, {
|
|
372
|
+
organizationRoleRules: {
|
|
373
|
+
organizationId: string;
|
|
374
|
+
organizationRoleIds: string[];
|
|
375
|
+
}[];
|
|
376
|
+
userIds: string[];
|
|
377
|
+
userRoleIds: string[];
|
|
378
|
+
organizationIds: string[];
|
|
379
|
+
}, {
|
|
380
|
+
userIds: string[];
|
|
381
|
+
userRoleIds: string[];
|
|
382
|
+
organizationIds: string[];
|
|
383
|
+
organizationRoleRules: {
|
|
384
|
+
organizationId: string;
|
|
385
|
+
organizationRoleIds: string[];
|
|
386
|
+
}[];
|
|
387
|
+
}>, z.ZodObject<{
|
|
388
|
+
userIds: z.ZodArray<z.ZodString, "many">;
|
|
389
|
+
userRoleIds: z.ZodArray<z.ZodString, "many">;
|
|
390
|
+
organizationIds: z.ZodArray<z.ZodString, "many">;
|
|
391
|
+
organizationRoleRules: z.ZodArray<z.ZodObject<{
|
|
392
|
+
organizationId: z.ZodString;
|
|
393
|
+
organizationRoleIds: z.ZodPipeline<z.ZodEffects<z.ZodArray<z.ZodString, "many">, string[], string[]>, z.ZodArray<z.ZodString, "many">>;
|
|
394
|
+
}, "strip", z.ZodTypeAny, {
|
|
395
|
+
organizationId: string;
|
|
396
|
+
organizationRoleIds: string[];
|
|
397
|
+
}, {
|
|
398
|
+
organizationId: string;
|
|
399
|
+
organizationRoleIds: string[];
|
|
400
|
+
}>, "many">;
|
|
401
|
+
}, "strip", z.ZodTypeAny, {
|
|
402
|
+
userIds: string[];
|
|
403
|
+
userRoleIds: string[];
|
|
404
|
+
organizationIds: string[];
|
|
405
|
+
organizationRoleRules: {
|
|
406
|
+
organizationId: string;
|
|
407
|
+
organizationRoleIds: string[];
|
|
408
|
+
}[];
|
|
409
|
+
}, {
|
|
410
|
+
userIds: string[];
|
|
411
|
+
userRoleIds: string[];
|
|
412
|
+
organizationIds: string[];
|
|
413
|
+
organizationRoleRules: {
|
|
414
|
+
organizationId: string;
|
|
415
|
+
organizationRoleIds: string[];
|
|
416
|
+
}[];
|
|
417
|
+
}>>;
|
|
418
|
+
export type ApplicationAccessControl = z.infer<typeof applicationAccessControlGuard>;
|
|
419
|
+
/** Create an empty application-level access control rule set. */
|
|
420
|
+
export declare const createDefaultApplicationAccessControl: () => ApplicationAccessControl;
|
|
322
421
|
export declare const applicationUserConsentScopesResponseGuard: z.ZodObject<{
|
|
323
422
|
organizationScopes: z.ZodArray<z.ZodObject<Pick<{
|
|
324
423
|
tenantId: z.ZodType<string, z.ZodTypeDef, string>;
|
|
@@ -549,16 +648,16 @@ export declare const applicationSignInExperienceCreateGuard: z.ZodObject<Omit<{
|
|
|
549
648
|
privacyPolicyUrl: z.ZodUnion<[z.ZodNullable<z.ZodOptional<z.ZodString>>, z.ZodLiteral<"">]>;
|
|
550
649
|
}, "strip", z.ZodTypeAny, {
|
|
551
650
|
displayName?: string | null;
|
|
651
|
+
customCss?: string | null;
|
|
552
652
|
color?: import("../index.js").PartialColor;
|
|
553
653
|
branding?: import("../index.js").Branding;
|
|
554
|
-
customCss?: string | null;
|
|
555
654
|
termsOfUseUrl?: string | null | undefined;
|
|
556
655
|
privacyPolicyUrl?: string | null | undefined;
|
|
557
656
|
}, {
|
|
558
657
|
displayName?: string | null;
|
|
658
|
+
customCss?: string | null;
|
|
559
659
|
color?: import("../index.js").PartialColor;
|
|
560
660
|
branding?: import("../index.js").Branding;
|
|
561
|
-
customCss?: string | null;
|
|
562
661
|
termsOfUseUrl?: string | null | undefined;
|
|
563
662
|
privacyPolicyUrl?: string | null | undefined;
|
|
564
663
|
}>;
|
package/lib/types/application.js
CHANGED
|
@@ -9,6 +9,7 @@ export const featuredApplicationGuard = Applications.guard.pick({
|
|
|
9
9
|
});
|
|
10
10
|
export const applicationCreateGuard = Applications.createGuard
|
|
11
11
|
.omit({
|
|
12
|
+
appLevelAccessControlEnabled: true,
|
|
12
13
|
id: true,
|
|
13
14
|
createdAt: true,
|
|
14
15
|
secret: true,
|
|
@@ -20,6 +21,60 @@ export const applicationPatchGuard = applicationCreateGuard.partial().omit({
|
|
|
20
21
|
type: true,
|
|
21
22
|
isThirdParty: true,
|
|
22
23
|
});
|
|
24
|
+
const applicationAccessControlRuleLimit = 1000;
|
|
25
|
+
const applicationAccessControlRawRuleLimit = applicationAccessControlRuleLimit * 2;
|
|
26
|
+
const uniqueStringArrayGuard = z
|
|
27
|
+
.array(z.string())
|
|
28
|
+
.max(applicationAccessControlRawRuleLimit)
|
|
29
|
+
.transform((values) => [...new Set(values)])
|
|
30
|
+
.pipe(z.array(z.string()).max(applicationAccessControlRuleLimit));
|
|
31
|
+
/** The guard for one organization role access-control rule group. */
|
|
32
|
+
export const applicationAccessControlOrganizationRoleRuleGuard = z.object({
|
|
33
|
+
organizationId: z.string(),
|
|
34
|
+
organizationRoleIds: uniqueStringArrayGuard,
|
|
35
|
+
});
|
|
36
|
+
/** The guard for application-level access control rule payloads. */
|
|
37
|
+
export const applicationAccessControlGuard = z
|
|
38
|
+
.object({
|
|
39
|
+
userIds: uniqueStringArrayGuard,
|
|
40
|
+
userRoleIds: uniqueStringArrayGuard,
|
|
41
|
+
organizationIds: uniqueStringArrayGuard,
|
|
42
|
+
organizationRoleRules: z
|
|
43
|
+
.array(applicationAccessControlOrganizationRoleRuleGuard)
|
|
44
|
+
.max(applicationAccessControlRawRuleLimit),
|
|
45
|
+
})
|
|
46
|
+
.transform(({ organizationRoleRules, ...rest }) => {
|
|
47
|
+
const organizationRoleRulesMap = new Map();
|
|
48
|
+
for (const { organizationId, organizationRoleIds } of organizationRoleRules) {
|
|
49
|
+
const roleIds = organizationRoleRulesMap.get(organizationId) ?? new Set();
|
|
50
|
+
for (const roleId of organizationRoleIds) {
|
|
51
|
+
roleIds.add(roleId);
|
|
52
|
+
}
|
|
53
|
+
organizationRoleRulesMap.set(organizationId, roleIds);
|
|
54
|
+
}
|
|
55
|
+
return {
|
|
56
|
+
...rest,
|
|
57
|
+
organizationRoleRules: [...organizationRoleRulesMap.entries()].map(([organizationId, organizationRoleIds]) => ({
|
|
58
|
+
organizationId,
|
|
59
|
+
organizationRoleIds: [...organizationRoleIds],
|
|
60
|
+
})),
|
|
61
|
+
};
|
|
62
|
+
})
|
|
63
|
+
.pipe(z.object({
|
|
64
|
+
userIds: z.array(z.string()).max(applicationAccessControlRuleLimit),
|
|
65
|
+
userRoleIds: z.array(z.string()).max(applicationAccessControlRuleLimit),
|
|
66
|
+
organizationIds: z.array(z.string()).max(applicationAccessControlRuleLimit),
|
|
67
|
+
organizationRoleRules: z
|
|
68
|
+
.array(applicationAccessControlOrganizationRoleRuleGuard)
|
|
69
|
+
.max(applicationAccessControlRuleLimit),
|
|
70
|
+
}));
|
|
71
|
+
/** Create an empty application-level access control rule set. */
|
|
72
|
+
export const createDefaultApplicationAccessControl = () => ({
|
|
73
|
+
userIds: [],
|
|
74
|
+
userRoleIds: [],
|
|
75
|
+
organizationIds: [],
|
|
76
|
+
organizationRoleRules: [],
|
|
77
|
+
});
|
|
23
78
|
const resourceScopesGuard = z.array(z.object({
|
|
24
79
|
resource: Resources.guard.pick({ id: true, name: true, indicator: true }),
|
|
25
80
|
scopes: z.array(Scopes.guard.pick({ id: true, name: true, description: true })),
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { applicationAccessControlGuard, createDefaultApplicationAccessControl, } from './application.js';
|
|
3
|
+
describe('applicationAccessControlGuard', () => {
|
|
4
|
+
it('deduplicates direct user and role rules', () => {
|
|
5
|
+
expect(applicationAccessControlGuard.parse({
|
|
6
|
+
userIds: ['user-1', 'user-2', 'user-1'],
|
|
7
|
+
userRoleIds: ['role-1', 'role-1', 'role-2'],
|
|
8
|
+
organizationIds: ['organization-1', 'organization-1', 'organization-2'],
|
|
9
|
+
organizationRoleRules: [],
|
|
10
|
+
})).toMatchObject({
|
|
11
|
+
userIds: ['user-1', 'user-2'],
|
|
12
|
+
userRoleIds: ['role-1', 'role-2'],
|
|
13
|
+
organizationIds: ['organization-1', 'organization-2'],
|
|
14
|
+
organizationRoleRules: [],
|
|
15
|
+
});
|
|
16
|
+
});
|
|
17
|
+
it('merges organization role rules by organization', () => {
|
|
18
|
+
expect(applicationAccessControlGuard.parse({
|
|
19
|
+
userIds: [],
|
|
20
|
+
userRoleIds: [],
|
|
21
|
+
organizationIds: [],
|
|
22
|
+
organizationRoleRules: [
|
|
23
|
+
{
|
|
24
|
+
organizationId: 'organization-1',
|
|
25
|
+
organizationRoleIds: ['organization-role-1', 'organization-role-2'],
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
organizationId: 'organization-1',
|
|
29
|
+
organizationRoleIds: ['organization-role-1', 'organization-role-3'],
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
organizationId: 'organization-2',
|
|
33
|
+
organizationRoleIds: ['organization-role-1'],
|
|
34
|
+
},
|
|
35
|
+
],
|
|
36
|
+
})).toMatchObject({
|
|
37
|
+
userIds: [],
|
|
38
|
+
userRoleIds: [],
|
|
39
|
+
organizationIds: [],
|
|
40
|
+
organizationRoleRules: [
|
|
41
|
+
{
|
|
42
|
+
organizationId: 'organization-1',
|
|
43
|
+
organizationRoleIds: [
|
|
44
|
+
'organization-role-1',
|
|
45
|
+
'organization-role-2',
|
|
46
|
+
'organization-role-3',
|
|
47
|
+
],
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
organizationId: 'organization-2',
|
|
51
|
+
organizationRoleIds: ['organization-role-1'],
|
|
52
|
+
},
|
|
53
|
+
],
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
it('creates a fresh default rule set', () => {
|
|
57
|
+
const first = createDefaultApplicationAccessControl();
|
|
58
|
+
const second = createDefaultApplicationAccessControl();
|
|
59
|
+
expect(first).not.toBe(second);
|
|
60
|
+
expect(first.userIds).not.toBe(second.userIds);
|
|
61
|
+
expect(first.userRoleIds).not.toBe(second.userRoleIds);
|
|
62
|
+
expect(first.organizationIds).not.toBe(second.organizationIds);
|
|
63
|
+
expect(first.organizationRoleRules).not.toBe(second.organizationRoleRules);
|
|
64
|
+
});
|
|
65
|
+
it('rejects oversized rule lists', () => {
|
|
66
|
+
const oversizedIds = Array.from({ length: 1001 }, (_, index) => `id-${index}`);
|
|
67
|
+
const emptyAccessControl = createDefaultApplicationAccessControl();
|
|
68
|
+
expect(() => applicationAccessControlGuard.parse({ ...emptyAccessControl, userIds: oversizedIds })).toThrow();
|
|
69
|
+
expect(() => applicationAccessControlGuard.parse({
|
|
70
|
+
...emptyAccessControl,
|
|
71
|
+
organizationRoleRules: oversizedIds.map((organizationId) => ({
|
|
72
|
+
organizationId,
|
|
73
|
+
organizationRoleIds: [],
|
|
74
|
+
})),
|
|
75
|
+
})).toThrow();
|
|
76
|
+
expect(() => applicationAccessControlGuard.parse({
|
|
77
|
+
...emptyAccessControl,
|
|
78
|
+
organizationRoleRules: [
|
|
79
|
+
{ organizationId: 'organization-1', organizationRoleIds: oversizedIds },
|
|
80
|
+
],
|
|
81
|
+
})).toThrow();
|
|
82
|
+
});
|
|
83
|
+
it('enforces rule list limits after normalization', () => {
|
|
84
|
+
const emptyAccessControl = createDefaultApplicationAccessControl();
|
|
85
|
+
const duplicateHeavyIds = Array.from({ length: 1001 }, () => 'user-1');
|
|
86
|
+
const mergedOrganizationRoleRules = [
|
|
87
|
+
{
|
|
88
|
+
organizationId: 'organization-1',
|
|
89
|
+
organizationRoleIds: Array.from({ length: 1000 }, (_, index) => `role-${index}`),
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
organizationId: 'organization-1',
|
|
93
|
+
organizationRoleIds: ['role-1000'],
|
|
94
|
+
},
|
|
95
|
+
];
|
|
96
|
+
expect(applicationAccessControlGuard.parse({ ...emptyAccessControl, userIds: duplicateHeavyIds })).toMatchObject({ userIds: ['user-1'] });
|
|
97
|
+
expect(() => applicationAccessControlGuard.parse({
|
|
98
|
+
...emptyAccessControl,
|
|
99
|
+
organizationRoleRules: mergedOrganizationRoleRules,
|
|
100
|
+
})).toThrow();
|
|
101
|
+
});
|
|
102
|
+
it('rejects overly large raw rule inputs before normalization', () => {
|
|
103
|
+
const emptyAccessControl = createDefaultApplicationAccessControl();
|
|
104
|
+
const oversizedDuplicateIds = Array.from({ length: 2001 }, () => 'id-1');
|
|
105
|
+
expect(() => applicationAccessControlGuard.parse({ ...emptyAccessControl, userIds: oversizedDuplicateIds })).toThrow();
|
|
106
|
+
expect(() => applicationAccessControlGuard.parse({
|
|
107
|
+
...emptyAccessControl,
|
|
108
|
+
organizationRoleRules: oversizedDuplicateIds.map(() => ({
|
|
109
|
+
organizationId: 'organization-1',
|
|
110
|
+
organizationRoleIds: [],
|
|
111
|
+
})),
|
|
112
|
+
})).toThrow();
|
|
113
|
+
expect(() => applicationAccessControlGuard.parse({
|
|
114
|
+
...emptyAccessControl,
|
|
115
|
+
organizationRoleRules: [
|
|
116
|
+
{ organizationId: 'organization-1', organizationRoleIds: oversizedDuplicateIds },
|
|
117
|
+
],
|
|
118
|
+
})).toThrow();
|
|
119
|
+
});
|
|
120
|
+
});
|
package/lib/types/consent.d.ts
CHANGED
|
@@ -179,6 +179,7 @@ export declare const publicApplicationGuard: z.ZodObject<Pick<{
|
|
|
179
179
|
pageRules: {
|
|
180
180
|
path: string;
|
|
181
181
|
}[];
|
|
182
|
+
additionalScopes?: (import("@logto/core-kit").UserScope.CustomData | import("@logto/core-kit").UserScope.Identities | import("@logto/core-kit").UserScope.Roles | import("@logto/core-kit").UserScope.Organizations | import("@logto/core-kit").UserScope.OrganizationRoles)[] | undefined;
|
|
182
183
|
customDomains?: {
|
|
183
184
|
status: import("../index.js").DomainStatus;
|
|
184
185
|
domain: string;
|
|
@@ -207,6 +208,7 @@ export declare const publicApplicationGuard: z.ZodObject<Pick<{
|
|
|
207
208
|
pageRules: {
|
|
208
209
|
path: string;
|
|
209
210
|
}[];
|
|
211
|
+
additionalScopes?: (import("@logto/core-kit").UserScope.CustomData | import("@logto/core-kit").UserScope.Identities | import("@logto/core-kit").UserScope.Roles | import("@logto/core-kit").UserScope.Organizations | import("@logto/core-kit").UserScope.OrganizationRoles)[] | undefined;
|
|
210
212
|
customDomains?: {
|
|
211
213
|
status: import("../index.js").DomainStatus;
|
|
212
214
|
domain: string;
|
|
@@ -231,6 +233,7 @@ export declare const publicApplicationGuard: z.ZodObject<Pick<{
|
|
|
231
233
|
} | null>;
|
|
232
234
|
customData: z.ZodType<import("@withtyped/server").JsonObject, z.ZodTypeDef, import("@withtyped/server").JsonObject>;
|
|
233
235
|
isThirdParty: z.ZodType<boolean, z.ZodTypeDef, boolean>;
|
|
236
|
+
appLevelAccessControlEnabled: z.ZodType<boolean, z.ZodTypeDef, boolean>;
|
|
234
237
|
createdAt: z.ZodType<number, z.ZodTypeDef, number>;
|
|
235
238
|
}, "name" | "id">, "strip", z.ZodTypeAny, {
|
|
236
239
|
name: string;
|
|
@@ -502,6 +505,7 @@ export declare const consentInfoResponseGuard: z.ZodObject<{
|
|
|
502
505
|
pageRules: {
|
|
503
506
|
path: string;
|
|
504
507
|
}[];
|
|
508
|
+
additionalScopes?: (import("@logto/core-kit").UserScope.CustomData | import("@logto/core-kit").UserScope.Identities | import("@logto/core-kit").UserScope.Roles | import("@logto/core-kit").UserScope.Organizations | import("@logto/core-kit").UserScope.OrganizationRoles)[] | undefined;
|
|
505
509
|
customDomains?: {
|
|
506
510
|
status: import("../index.js").DomainStatus;
|
|
507
511
|
domain: string;
|
|
@@ -530,6 +534,7 @@ export declare const consentInfoResponseGuard: z.ZodObject<{
|
|
|
530
534
|
pageRules: {
|
|
531
535
|
path: string;
|
|
532
536
|
}[];
|
|
537
|
+
additionalScopes?: (import("@logto/core-kit").UserScope.CustomData | import("@logto/core-kit").UserScope.Identities | import("@logto/core-kit").UserScope.Roles | import("@logto/core-kit").UserScope.Organizations | import("@logto/core-kit").UserScope.OrganizationRoles)[] | undefined;
|
|
533
538
|
customDomains?: {
|
|
534
539
|
status: import("../index.js").DomainStatus;
|
|
535
540
|
domain: string;
|
|
@@ -554,6 +559,7 @@ export declare const consentInfoResponseGuard: z.ZodObject<{
|
|
|
554
559
|
} | null>;
|
|
555
560
|
customData: z.ZodType<import("@withtyped/server").JsonObject, z.ZodTypeDef, import("@withtyped/server").JsonObject>;
|
|
556
561
|
isThirdParty: z.ZodType<boolean, z.ZodTypeDef, boolean>;
|
|
562
|
+
appLevelAccessControlEnabled: z.ZodType<boolean, z.ZodTypeDef, boolean>;
|
|
557
563
|
createdAt: z.ZodType<number, z.ZodTypeDef, number>;
|
|
558
564
|
}, "name" | "id"> & {
|
|
559
565
|
displayName: z.ZodOptional<z.ZodType<string | null, z.ZodTypeDef, string | null>>;
|
|
@@ -2383,11 +2383,10 @@ export declare const updateCustomProfileFieldSieOrderGuard: z.ZodObject<{
|
|
|
2383
2383
|
sieOrder: number;
|
|
2384
2384
|
}>;
|
|
2385
2385
|
export type UpdateCustomProfileFieldSieOrder = z.infer<typeof updateCustomProfileFieldSieOrderGuard>;
|
|
2386
|
-
/**
|
|
2387
|
-
* Reserved custom data keys, which are used by the system and should not be used by custom profile fields.
|
|
2388
|
-
*/
|
|
2386
|
+
/** Reserved custom data keys, which are used by the system and should not be used by custom profile fields. */
|
|
2389
2387
|
export declare const reservedCustomDataKeyGuard: z.ZodObject<{
|
|
2390
2388
|
onboarding: z.ZodOptional<z.ZodString>;
|
|
2389
|
+
ossOnboarding: z.ZodOptional<z.ZodString>;
|
|
2391
2390
|
guideRequests: z.ZodOptional<z.ZodString>;
|
|
2392
2391
|
adminConsolePreferences: z.ZodOptional<z.ZodString>;
|
|
2393
2392
|
defaultTenantId: z.ZodOptional<z.ZodString>;
|
|
@@ -2396,17 +2395,16 @@ export declare const reservedCustomDataKeyGuard: z.ZodObject<{
|
|
|
2396
2395
|
guideRequests?: string | undefined;
|
|
2397
2396
|
defaultTenantId?: string | undefined;
|
|
2398
2397
|
onboarding?: string | undefined;
|
|
2398
|
+
ossOnboarding?: string | undefined;
|
|
2399
2399
|
}, {
|
|
2400
2400
|
adminConsolePreferences?: string | undefined;
|
|
2401
2401
|
guideRequests?: string | undefined;
|
|
2402
2402
|
defaultTenantId?: string | undefined;
|
|
2403
2403
|
onboarding?: string | undefined;
|
|
2404
|
+
ossOnboarding?: string | undefined;
|
|
2404
2405
|
}>;
|
|
2405
|
-
export declare const reservedCustomDataKeys: readonly ["adminConsolePreferences", "guideRequests", "defaultTenantId", "onboarding"];
|
|
2406
|
-
/**
|
|
2407
|
-
* Disallow sign-in identifiers related field keys in custom profile fields, as this is conflicting
|
|
2408
|
-
* with the built-in sign-in/sign-up experience flows.
|
|
2409
|
-
*/
|
|
2406
|
+
export declare const reservedCustomDataKeys: readonly ["adminConsolePreferences", "guideRequests", "defaultTenantId", "onboarding", "ossOnboarding"];
|
|
2407
|
+
/** Disallow sign-in identifier related field keys in custom profile fields to avoid conflicts with built-in sign-in/sign-up flows. */
|
|
2410
2408
|
export declare const signInIdentifierKeyGuard: z.ZodObject<Pick<{
|
|
2411
2409
|
tenantId: z.ZodOptional<z.ZodType<string, z.ZodTypeDef, string>>;
|
|
2412
2410
|
id: z.ZodType<string, z.ZodTypeDef, string>;
|
|
@@ -2544,11 +2542,7 @@ export declare const signInIdentifierKeyGuard: z.ZodObject<Pick<{
|
|
|
2544
2542
|
primaryPhone?: string | null;
|
|
2545
2543
|
}>;
|
|
2546
2544
|
export declare const reservedSignInIdentifierKeys: readonly ["username", "email", "phone", "primaryEmail", "primaryPhone"];
|
|
2547
|
-
/**
|
|
2548
|
-
* Reserved user profile keys.
|
|
2549
|
-
* Currently only `preferredUsername` is reserved since it is the standard username property used
|
|
2550
|
-
* by most identity providers. Should not allow user updating this field via profile related APIs.
|
|
2551
|
-
*/
|
|
2545
|
+
/** Reserved user profile keys. Currently only `preferredUsername` is reserved for standard IdP usage. */
|
|
2552
2546
|
export declare const reservedBuiltInProfileKeyGuard: z.ZodObject<Pick<{
|
|
2553
2547
|
familyName: z.ZodOptional<z.ZodString>;
|
|
2554
2548
|
givenName: z.ZodOptional<z.ZodString>;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { Users } from '../db-entries/user.js';
|
|
3
3
|
import { CustomProfileFieldType, customProfileFieldTypeGuard, fieldPartGuard, userProfileAddressKeys, userProfileGuard, } from '../foundations/index.js';
|
|
4
|
-
import { userOnboardingDataKey } from './onboarding.js';
|
|
4
|
+
import { ossUserOnboardingDataKey, userOnboardingDataKey } from './onboarding.js';
|
|
5
5
|
import { defaultTenantIdKey } from './tenant.js';
|
|
6
6
|
import { consoleUserPreferenceKey, guideRequestsKey } from './user.js';
|
|
7
7
|
const baseProfileFieldGuard = z.object({
|
|
@@ -129,22 +129,18 @@ export const updateCustomProfileFieldSieOrderGuard = z.object({
|
|
|
129
129
|
name: z.string(),
|
|
130
130
|
sieOrder: z.number(),
|
|
131
131
|
});
|
|
132
|
-
/**
|
|
133
|
-
* Reserved custom data keys, which are used by the system and should not be used by custom profile fields.
|
|
134
|
-
*/
|
|
132
|
+
/** Reserved custom data keys, which are used by the system and should not be used by custom profile fields. */
|
|
135
133
|
export const reservedCustomDataKeyGuard = z
|
|
136
134
|
.object({
|
|
137
135
|
[userOnboardingDataKey]: z.string(),
|
|
136
|
+
[ossUserOnboardingDataKey]: z.string(),
|
|
138
137
|
[guideRequestsKey]: z.string(),
|
|
139
138
|
[consoleUserPreferenceKey]: z.string(),
|
|
140
139
|
[defaultTenantIdKey]: z.string(),
|
|
141
140
|
})
|
|
142
141
|
.partial();
|
|
143
142
|
export const reservedCustomDataKeys = Object.freeze(reservedCustomDataKeyGuard.keyof().options);
|
|
144
|
-
/**
|
|
145
|
-
* Disallow sign-in identifiers related field keys in custom profile fields, as this is conflicting
|
|
146
|
-
* with the built-in sign-in/sign-up experience flows.
|
|
147
|
-
*/
|
|
143
|
+
/** Disallow sign-in identifier related field keys in custom profile fields to avoid conflicts with built-in sign-in/sign-up flows. */
|
|
148
144
|
export const signInIdentifierKeyGuard = Users.createGuard
|
|
149
145
|
.pick({
|
|
150
146
|
username: true,
|
|
@@ -156,11 +152,7 @@ export const signInIdentifierKeyGuard = Users.createGuard
|
|
|
156
152
|
phone: z.string().nullable().optional(),
|
|
157
153
|
});
|
|
158
154
|
export const reservedSignInIdentifierKeys = Object.freeze(signInIdentifierKeyGuard.keyof().options);
|
|
159
|
-
/**
|
|
160
|
-
* Reserved user profile keys.
|
|
161
|
-
* Currently only `preferredUsername` is reserved since it is the standard username property used
|
|
162
|
-
* by most identity providers. Should not allow user updating this field via profile related APIs.
|
|
163
|
-
*/
|
|
155
|
+
/** Reserved user profile keys. Currently only `preferredUsername` is reserved for standard IdP usage. */
|
|
164
156
|
export const reservedBuiltInProfileKeyGuard = userProfileGuard.pick({ preferredUsername: true });
|
|
165
157
|
export const reservedBuiltInProfileKeys = Object.freeze(reservedBuiltInProfileKeyGuard.keyof().options);
|
|
166
158
|
export var SupportedDateFormat;
|
|
@@ -176,3 +168,4 @@ export var Gender;
|
|
|
176
168
|
Gender["Male"] = "male";
|
|
177
169
|
Gender["Other"] = "prefer_not_to_say";
|
|
178
170
|
})(Gender || (Gender = {}));
|
|
171
|
+
/* eslint-enable max-lines */
|