@devxcrew/platform 0.1.2 → 0.1.6
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/README.md +7 -1
- package/dist/index.d.ts +6 -4
- package/dist/index.js +4 -2
- package/dist/modules/identity/audit/audit.controller.d.ts +17 -0
- package/dist/modules/identity/audit/audit.controller.js +12 -0
- package/dist/modules/identity/audit/audit.migration.d.ts +1 -0
- package/dist/modules/identity/audit/audit.migration.js +1 -0
- package/dist/modules/identity/audit/audit.provider.d.ts +24 -0
- package/dist/modules/identity/audit/audit.provider.js +14 -0
- package/dist/modules/identity/audit/audit.repository.d.ts +26 -0
- package/dist/modules/identity/audit/audit.repository.js +46 -0
- package/dist/modules/identity/audit/audit.routes.d.ts +5 -0
- package/dist/modules/identity/audit/audit.routes.js +2 -0
- package/dist/modules/identity/audit/audit.schema.d.ts +1 -0
- package/dist/modules/identity/audit/audit.schema.js +1 -0
- package/dist/modules/identity/audit/audit.seed.d.ts +1 -0
- package/dist/modules/identity/audit/audit.seed.js +1 -0
- package/dist/modules/identity/audit/audit.service.d.ts +23 -0
- package/dist/modules/identity/audit/audit.service.js +46 -0
- package/dist/modules/identity/audit/audit.types.d.ts +12 -0
- package/dist/modules/identity/audit/audit.types.js +1 -0
- package/dist/modules/identity/audit/index.d.ts +4 -0
- package/dist/modules/identity/audit/index.js +3 -0
- package/dist/modules/identity/{identity.administration-service.d.ts → composition/identity.composition.d.ts} +59 -27
- package/dist/modules/identity/composition/identity.composition.js +171 -0
- package/dist/modules/identity/composition/identity.seed.d.ts +3 -0
- package/dist/modules/identity/composition/identity.seed.js +16 -0
- package/dist/modules/identity/identity.controller.d.ts +8 -6
- package/dist/modules/identity/identity.controller.js +90 -70
- package/dist/modules/identity/identity.migration.d.ts +1 -2
- package/dist/modules/identity/identity.migration.js +1 -79
- package/dist/modules/identity/identity.provider.d.ts +39 -13
- package/dist/modules/identity/identity.provider.js +19 -73
- package/dist/modules/identity/identity.public-schemas.d.ts +9 -3
- package/dist/modules/identity/identity.public-schemas.js +9 -3
- package/dist/modules/identity/identity.repository.d.ts +1 -40
- package/dist/modules/identity/identity.repository.js +1 -196
- package/dist/modules/identity/identity.routes.d.ts +2 -5
- package/dist/modules/identity/identity.routes.js +3 -11
- package/dist/modules/identity/identity.schema.d.ts +1 -27
- package/dist/modules/identity/identity.schema.js +1 -34
- package/dist/modules/identity/identity.seed.d.ts +1 -3
- package/dist/modules/identity/identity.seed.js +1 -81
- package/dist/modules/identity/identity.service.d.ts +1 -32
- package/dist/modules/identity/identity.service.js +1 -83
- package/dist/modules/identity/identity.types.d.ts +13 -106
- package/dist/modules/identity/index.d.ts +18 -11
- package/dist/modules/identity/index.js +14 -8
- package/dist/modules/identity/{identity.administration-migration.js → legacy/identity.administration-migration.js} +3 -11
- package/dist/modules/identity/legacy/identity.migration.d.ts +2 -0
- package/dist/modules/identity/legacy/identity.migration.js +72 -0
- package/dist/modules/identity/organization/index.d.ts +6 -0
- package/dist/modules/identity/organization/index.js +5 -0
- package/dist/modules/identity/organization/organization.controller.d.ts +35 -0
- package/dist/modules/identity/organization/organization.controller.js +18 -0
- package/dist/modules/identity/organization/organization.migration.d.ts +1 -0
- package/dist/modules/identity/organization/organization.migration.js +1 -0
- package/dist/modules/identity/organization/organization.provider.d.ts +46 -0
- package/dist/modules/identity/organization/organization.provider.js +33 -0
- package/dist/modules/identity/organization/organization.repository.d.ts +58 -0
- package/dist/modules/identity/organization/organization.repository.js +101 -0
- package/dist/modules/identity/organization/organization.routes.d.ts +5 -0
- package/dist/modules/identity/organization/organization.routes.js +2 -0
- package/dist/modules/identity/organization/organization.schema.d.ts +11 -0
- package/dist/modules/identity/organization/organization.schema.js +18 -0
- package/dist/modules/identity/organization/organization.seed.d.ts +3 -0
- package/dist/modules/identity/organization/organization.seed.js +11 -0
- package/dist/modules/identity/organization/organization.service.d.ts +39 -0
- package/dist/modules/identity/organization/organization.service.js +55 -0
- package/dist/modules/identity/organization/organization.types.d.ts +19 -0
- package/dist/modules/identity/organization/organization.types.js +1 -0
- package/dist/modules/identity/permission/index.d.ts +9 -0
- package/dist/modules/identity/permission/index.js +6 -0
- package/dist/modules/identity/permission/permission.controller.d.ts +21 -0
- package/dist/modules/identity/permission/permission.controller.js +12 -0
- package/dist/modules/identity/{identity.permission-declarations-migration.js → permission/permission.declarations.migration.js} +9 -4
- package/dist/modules/identity/{identity.permission-labels-migration.js → permission/permission.labels.migration.js} +5 -2
- package/dist/modules/identity/permission/permission.migration.d.ts +2 -0
- package/dist/modules/identity/permission/permission.migration.js +2 -0
- package/dist/modules/identity/permission/permission.provider.d.ts +19 -0
- package/dist/modules/identity/permission/permission.provider.js +14 -0
- package/dist/modules/identity/permission/permission.repository.d.ts +51 -0
- package/dist/modules/identity/permission/permission.repository.js +84 -0
- package/dist/modules/identity/permission/permission.routes.d.ts +5 -0
- package/dist/modules/identity/permission/permission.routes.js +2 -0
- package/dist/modules/identity/permission/permission.schema.d.ts +14 -0
- package/dist/modules/identity/permission/permission.schema.js +29 -0
- package/dist/modules/identity/permission/permission.seed.d.ts +4 -0
- package/dist/modules/identity/permission/permission.seed.js +17 -0
- package/dist/modules/identity/permission/permission.service.d.ts +31 -0
- package/dist/modules/identity/permission/permission.service.js +77 -0
- package/dist/modules/identity/permission/permission.types.d.ts +15 -0
- package/dist/modules/identity/permission/permission.types.js +1 -0
- package/dist/modules/identity/role/index.d.ts +10 -0
- package/dist/modules/identity/role/index.js +6 -0
- package/dist/modules/identity/role/role.controller.d.ts +42 -0
- package/dist/modules/identity/role/role.controller.js +25 -0
- package/dist/modules/identity/{identity.roles-migration.js → role/role.migration.js} +5 -18
- package/dist/modules/identity/role/role.provider.d.ts +14 -0
- package/dist/modules/identity/role/role.provider.js +30 -0
- package/dist/modules/identity/role/role.repository.d.ts +38 -0
- package/dist/modules/identity/role/role.repository.js +46 -0
- package/dist/modules/identity/role/role.routes.d.ts +5 -0
- package/dist/modules/identity/role/role.routes.js +2 -0
- package/dist/modules/identity/role/role.schema.d.ts +16 -0
- package/dist/modules/identity/role/role.schema.js +27 -0
- package/dist/modules/identity/role/role.seed.d.ts +3 -0
- package/dist/modules/identity/role/role.seed.js +8 -0
- package/dist/modules/identity/role/role.service.d.ts +69 -0
- package/dist/modules/identity/role/role.service.js +192 -0
- package/dist/modules/identity/role/role.types.d.ts +17 -0
- package/dist/modules/identity/role/role.types.js +1 -0
- package/dist/modules/identity/role-permission/index.d.ts +6 -0
- package/dist/modules/identity/role-permission/index.js +4 -0
- package/dist/modules/identity/role-permission/role-permission.controller.d.ts +1 -0
- package/dist/modules/identity/role-permission/role-permission.controller.js +1 -0
- package/dist/modules/identity/role-permission/role-permission.migration.d.ts +1 -0
- package/dist/modules/identity/role-permission/role-permission.migration.js +1 -0
- package/dist/modules/identity/role-permission/role-permission.provider.d.ts +14 -0
- package/dist/modules/identity/role-permission/role-permission.provider.js +15 -0
- package/dist/modules/identity/role-permission/role-permission.repository.d.ts +12 -0
- package/dist/modules/identity/role-permission/role-permission.repository.js +51 -0
- package/dist/modules/identity/role-permission/role-permission.routes.d.ts +1 -0
- package/dist/modules/identity/role-permission/role-permission.routes.js +2 -0
- package/dist/modules/identity/role-permission/role-permission.schema.d.ts +2 -0
- package/dist/modules/identity/role-permission/role-permission.schema.js +3 -0
- package/dist/modules/identity/role-permission/role-permission.seed.d.ts +3 -0
- package/dist/modules/identity/role-permission/role-permission.seed.js +10 -0
- package/dist/modules/identity/role-permission/role-permission.service.d.ts +13 -0
- package/dist/modules/identity/role-permission/role-permission.service.js +35 -0
- package/dist/modules/identity/role-permission/role-permission.types.d.ts +12 -0
- package/dist/modules/identity/role-permission/role-permission.types.js +1 -0
- package/dist/modules/identity/session/index.d.ts +6 -0
- package/dist/modules/identity/session/index.js +5 -0
- package/dist/modules/identity/session/session.authentication.controller.d.ts +30 -0
- package/dist/modules/identity/session/session.authentication.controller.js +37 -0
- package/dist/modules/identity/session/session.controller.d.ts +18 -0
- package/dist/modules/identity/session/session.controller.js +15 -0
- package/dist/modules/identity/session/session.migration.d.ts +1 -0
- package/dist/modules/identity/session/session.migration.js +1 -0
- package/dist/modules/identity/session/session.provider.d.ts +34 -0
- package/dist/modules/identity/session/session.provider.js +23 -0
- package/dist/modules/identity/session/session.repository.d.ts +36 -0
- package/dist/modules/identity/session/session.repository.js +128 -0
- package/dist/modules/identity/session/session.routes.d.ts +27 -0
- package/dist/modules/identity/session/session.routes.js +13 -0
- package/dist/modules/identity/session/session.schema.d.ts +6 -0
- package/dist/modules/identity/session/session.schema.js +12 -0
- package/dist/modules/identity/session/session.seed.d.ts +1 -0
- package/dist/modules/identity/session/session.seed.js +1 -0
- package/dist/modules/identity/session/session.service.d.ts +20 -0
- package/dist/modules/identity/session/session.service.js +27 -0
- package/dist/modules/identity/session/session.types.d.ts +17 -0
- package/dist/modules/identity/session/session.types.js +1 -0
- package/dist/modules/identity/settings/index.d.ts +5 -0
- package/dist/modules/identity/settings/index.js +4 -0
- package/dist/modules/identity/settings/settings.controller.d.ts +35 -0
- package/dist/modules/identity/settings/settings.controller.js +18 -0
- package/dist/modules/identity/settings/settings.migration.d.ts +1 -0
- package/dist/modules/identity/settings/settings.migration.js +1 -0
- package/dist/modules/identity/settings/settings.provider.d.ts +35 -0
- package/dist/modules/identity/settings/settings.provider.js +14 -0
- package/dist/modules/identity/settings/settings.repository.d.ts +49 -0
- package/dist/modules/identity/settings/settings.repository.js +75 -0
- package/dist/modules/identity/settings/settings.routes.d.ts +1 -0
- package/dist/modules/identity/settings/settings.routes.js +7 -0
- package/dist/modules/identity/settings/settings.schema.d.ts +15 -0
- package/dist/modules/identity/settings/settings.schema.js +27 -0
- package/dist/modules/identity/settings/settings.seed.d.ts +1 -0
- package/dist/modules/identity/settings/settings.seed.js +1 -0
- package/dist/modules/identity/settings/settings.service.d.ts +40 -0
- package/dist/modules/identity/settings/settings.service.js +101 -0
- package/dist/modules/identity/settings/settings.types.d.ts +20 -0
- package/dist/modules/identity/settings/settings.types.js +1 -0
- package/dist/modules/identity/support/identity.schema.d.ts +8 -0
- package/dist/modules/identity/support/identity.schema.js +10 -0
- package/dist/modules/identity/support/pagination.d.ts +13 -0
- package/dist/modules/identity/support/pagination.js +27 -0
- package/dist/modules/identity/support/pagination.schema.d.ts +43 -0
- package/dist/modules/identity/support/pagination.schema.js +15 -0
- package/dist/modules/identity/support/resource-controller.d.ts +14 -0
- package/dist/modules/identity/support/resource-controller.js +1 -0
- package/dist/modules/identity/user/index.d.ts +13 -0
- package/dist/modules/identity/user/index.js +11 -0
- package/dist/modules/identity/user/user.administration.service.d.ts +59 -0
- package/dist/modules/identity/user/user.administration.service.js +159 -0
- package/dist/modules/identity/user/user.controller.d.ts +44 -0
- package/dist/modules/identity/user/user.controller.js +21 -0
- package/dist/modules/identity/user/user.lifecycle.controller.d.ts +58 -0
- package/dist/modules/identity/user/user.lifecycle.controller.js +27 -0
- package/dist/modules/identity/{identity.lifecycle-schema.js → user/user.lifecycle.schema.js} +5 -5
- package/dist/modules/identity/{identity.lifecycle-service.d.ts → user/user.lifecycle.service.d.ts} +8 -4
- package/dist/modules/identity/{identity.lifecycle-service.js → user/user.lifecycle.service.js} +66 -118
- package/dist/modules/identity/user/user.migration.d.ts +1 -0
- package/dist/modules/identity/user/user.migration.js +1 -0
- package/dist/modules/identity/{identity.password.js → user/user.password.js} +1 -1
- package/dist/modules/identity/user/user.provider.d.ts +80 -0
- package/dist/modules/identity/user/user.provider.js +22 -0
- package/dist/modules/identity/user/user.repository.d.ts +64 -0
- package/dist/modules/identity/user/user.repository.js +129 -0
- package/dist/modules/identity/user/user.routes.d.ts +12 -0
- package/dist/modules/identity/user/user.routes.js +9 -0
- package/dist/modules/identity/user/user.schema.d.ts +34 -0
- package/dist/modules/identity/user/user.schema.js +46 -0
- package/dist/modules/identity/user/user.seed.d.ts +14 -0
- package/dist/modules/identity/user/user.seed.js +47 -0
- package/dist/modules/identity/user/user.service.d.ts +33 -0
- package/dist/modules/identity/user/user.service.js +76 -0
- package/dist/modules/identity/user/user.types.d.ts +27 -0
- package/dist/modules/identity/user/user.types.js +1 -0
- package/dist/modules/identity/user-role/index.d.ts +8 -0
- package/dist/modules/identity/user-role/index.js +6 -0
- package/dist/modules/identity/user-role/user-role.controller.d.ts +42 -0
- package/dist/modules/identity/user-role/user-role.controller.js +21 -0
- package/dist/modules/identity/user-role/user-role.migration.d.ts +1 -0
- package/dist/modules/identity/user-role/user-role.migration.js +1 -0
- package/dist/modules/identity/user-role/user-role.policy.d.ts +3 -0
- package/dist/modules/identity/user-role/user-role.policy.js +25 -0
- package/dist/modules/identity/user-role/user-role.provider.d.ts +18 -0
- package/dist/modules/identity/user-role/user-role.provider.js +23 -0
- package/dist/modules/identity/user-role/user-role.repository.d.ts +34 -0
- package/dist/modules/identity/user-role/user-role.repository.js +87 -0
- package/dist/modules/identity/user-role/user-role.routes.d.ts +5 -0
- package/dist/modules/identity/user-role/user-role.routes.js +2 -0
- package/dist/modules/identity/user-role/user-role.schema.d.ts +14 -0
- package/dist/modules/identity/user-role/user-role.schema.js +25 -0
- package/dist/modules/identity/user-role/user-role.seed.d.ts +7 -0
- package/dist/modules/identity/user-role/user-role.seed.js +11 -0
- package/dist/modules/identity/user-role/user-role.service.d.ts +60 -0
- package/dist/modules/identity/user-role/user-role.service.js +216 -0
- package/dist/modules/identity/user-role/user-role.types.d.ts +12 -0
- package/dist/modules/identity/user-role/user-role.types.js +1 -0
- package/dist/modules/tenant/index.d.ts +6 -0
- package/dist/modules/tenant/index.js +5 -0
- package/dist/modules/tenant/tenant.command.d.ts +3 -0
- package/dist/modules/tenant/tenant.command.js +43 -0
- package/dist/modules/tenant/tenant.controller.d.ts +4 -0
- package/dist/modules/tenant/tenant.controller.js +12 -0
- package/dist/modules/tenant/tenant.migration.d.ts +5 -0
- package/dist/modules/tenant/tenant.migration.js +46 -0
- package/dist/modules/tenant/tenant.provider.d.ts +3 -0
- package/dist/modules/tenant/tenant.provider.js +44 -0
- package/dist/modules/tenant/tenant.readiness.d.ts +6 -0
- package/dist/modules/tenant/tenant.readiness.js +45 -0
- package/dist/modules/tenant/tenant.repository.d.ts +15 -0
- package/dist/modules/tenant/tenant.repository.js +23 -0
- package/dist/modules/tenant/tenant.routes.d.ts +4 -0
- package/dist/modules/tenant/tenant.routes.js +7 -0
- package/dist/modules/tenant/tenant.schema.d.ts +20 -0
- package/dist/modules/tenant/tenant.schema.js +31 -0
- package/dist/modules/tenant/tenant.seed.d.ts +8 -0
- package/dist/modules/tenant/tenant.seed.js +40 -0
- package/dist/modules/tenant/tenant.service.d.ts +16 -0
- package/dist/modules/tenant/tenant.service.js +72 -0
- package/dist/modules/tenant/tenant.types.d.ts +39 -0
- package/dist/modules/tenant/tenant.types.js +1 -0
- package/package.json +9 -5
- package/dist/modules/identity/identity.administration-repository.d.ts +0 -19
- package/dist/modules/identity/identity.administration-repository.js +0 -102
- package/dist/modules/identity/identity.administration-schema.d.ts +0 -113
- package/dist/modules/identity/identity.administration-schema.js +0 -121
- package/dist/modules/identity/identity.administration-service.js +0 -517
- package/dist/modules/identity/identity.membership-policy.d.ts +0 -3
- package/dist/modules/identity/identity.membership-policy.js +0 -27
- package/dist/modules/identity/identity.permission-declarations.d.ts +0 -23
- package/dist/modules/identity/identity.permission-declarations.js +0 -57
- package/dist/modules/identity/identity.presentation-service.d.ts +0 -23
- package/dist/modules/identity/identity.presentation-service.js +0 -51
- package/dist/modules/identity/identity.roles-service.d.ts +0 -49
- package/dist/modules/identity/identity.roles-service.js +0 -327
- /package/dist/modules/identity/{identity.administration-migration.d.ts → legacy/identity.administration-migration.d.ts} +0 -0
- /package/dist/modules/identity/{identity.permission-declarations-migration.d.ts → permission/permission.declarations.migration.d.ts} +0 -0
- /package/dist/modules/identity/{identity.permission-labels-migration.d.ts → permission/permission.labels.migration.d.ts} +0 -0
- /package/dist/modules/identity/{identity.roles-migration.d.ts → role/role.migration.d.ts} +0 -0
- /package/dist/modules/identity/{identity.error.d.ts → support/identity.error.d.ts} +0 -0
- /package/dist/modules/identity/{identity.error.js → support/identity.error.js} +0 -0
- /package/dist/modules/identity/{identity.request-context.d.ts → support/identity.request-context.d.ts} +0 -0
- /package/dist/modules/identity/{identity.request-context.js → support/identity.request-context.js} +0 -0
- /package/dist/modules/identity/{identity.lifecycle-schema.d.ts → user/user.lifecycle.schema.d.ts} +0 -0
- /package/dist/modules/identity/{identity.password.d.ts → user/user.password.d.ts} +0 -0
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export declare const userResource: "users";
|
|
2
|
+
export declare const userResourceRoute: (controller: import("./user.controller.js").IdentityUserController) => {
|
|
3
|
+
resource: "users";
|
|
4
|
+
controller: import("./user.controller.js").IdentityUserController;
|
|
5
|
+
};
|
|
6
|
+
export declare const userLifecyclePaths: {
|
|
7
|
+
readonly recovery: "recovery";
|
|
8
|
+
readonly recoveryComplete: "recovery/complete";
|
|
9
|
+
readonly invitationAccept: "invitations/accept";
|
|
10
|
+
readonly invitations: "invitations";
|
|
11
|
+
readonly profile: "profile";
|
|
12
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export const userResource = "users";
|
|
2
|
+
export const userResourceRoute = (controller) => ({ resource: userResource, controller });
|
|
3
|
+
export const userLifecyclePaths = {
|
|
4
|
+
recovery: "recovery",
|
|
5
|
+
recoveryComplete: "recovery/complete",
|
|
6
|
+
invitationAccept: "invitations/accept",
|
|
7
|
+
invitations: "invitations",
|
|
8
|
+
profile: "profile"
|
|
9
|
+
};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export { identityKeySchema, portalSchema } from "../support/identity.schema.js";
|
|
3
|
+
export declare const passwordSchema: z.ZodObject<{
|
|
4
|
+
currentPassword: z.ZodString;
|
|
5
|
+
password: z.ZodString;
|
|
6
|
+
}, z.core.$strict>;
|
|
7
|
+
export declare const accountSchema: z.ZodObject<{
|
|
8
|
+
email: z.ZodPipe<z.ZodEmail, z.ZodTransform<string, string>>;
|
|
9
|
+
name: z.ZodString;
|
|
10
|
+
password: z.ZodString;
|
|
11
|
+
portal: z.ZodEnum<{
|
|
12
|
+
admin: "admin";
|
|
13
|
+
"super-admin": "super-admin";
|
|
14
|
+
user: "user";
|
|
15
|
+
}>;
|
|
16
|
+
tenantId: z.ZodString;
|
|
17
|
+
}, z.core.$strict>;
|
|
18
|
+
export declare const userCreateSchema: z.ZodObject<{
|
|
19
|
+
name: z.ZodString;
|
|
20
|
+
email: z.ZodPipe<z.ZodEmail, z.ZodTransform<string, string>>;
|
|
21
|
+
password: z.ZodString;
|
|
22
|
+
roleId: z.ZodDefault<z.ZodString>;
|
|
23
|
+
tenantId: z.ZodOptional<z.ZodString>;
|
|
24
|
+
}, z.core.$strict>;
|
|
25
|
+
export declare const userUpdateSchema: z.ZodObject<{
|
|
26
|
+
name: z.ZodOptional<z.ZodString>;
|
|
27
|
+
email: z.ZodOptional<z.ZodPipe<z.ZodEmail, z.ZodTransform<string, string>>>;
|
|
28
|
+
active: z.ZodOptional<z.ZodBoolean>;
|
|
29
|
+
expectedVersion: z.ZodNumber;
|
|
30
|
+
}, z.core.$strict>;
|
|
31
|
+
export declare const profileSchema: z.ZodObject<{
|
|
32
|
+
name: z.ZodString;
|
|
33
|
+
expectedVersion: z.ZodNumber;
|
|
34
|
+
}, z.core.$strict>;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { identityKeySchema, portalSchema, resourceIdSchema } from "../support/identity.schema.js";
|
|
3
|
+
export { identityKeySchema, portalSchema } from "../support/identity.schema.js";
|
|
4
|
+
export const passwordSchema = z
|
|
5
|
+
.object({
|
|
6
|
+
currentPassword: z.string().min(1).max(256),
|
|
7
|
+
password: z.string().min(12).max(256)
|
|
8
|
+
})
|
|
9
|
+
.strict();
|
|
10
|
+
export const accountSchema = z
|
|
11
|
+
.object({
|
|
12
|
+
email: z
|
|
13
|
+
.email()
|
|
14
|
+
.max(254)
|
|
15
|
+
.transform((value) => value.toLowerCase()),
|
|
16
|
+
name: z.string().trim().min(1).max(100),
|
|
17
|
+
password: z.string().min(12).max(256),
|
|
18
|
+
portal: portalSchema,
|
|
19
|
+
tenantId: identityKeySchema
|
|
20
|
+
})
|
|
21
|
+
.strict();
|
|
22
|
+
const userName = z.string().trim().min(1).max(100);
|
|
23
|
+
const userEmail = z
|
|
24
|
+
.email()
|
|
25
|
+
.max(254)
|
|
26
|
+
.transform((value) => value.trim().toLowerCase());
|
|
27
|
+
const expectedVersion = z.number().int().min(0);
|
|
28
|
+
export const userCreateSchema = z
|
|
29
|
+
.object({
|
|
30
|
+
name: userName,
|
|
31
|
+
email: userEmail,
|
|
32
|
+
password: z.string().min(12).max(256),
|
|
33
|
+
roleId: resourceIdSchema.default("user"),
|
|
34
|
+
tenantId: identityKeySchema.optional()
|
|
35
|
+
})
|
|
36
|
+
.strict();
|
|
37
|
+
export const userUpdateSchema = z
|
|
38
|
+
.object({
|
|
39
|
+
name: userName.optional(),
|
|
40
|
+
email: userEmail.optional(),
|
|
41
|
+
active: z.boolean().optional(),
|
|
42
|
+
expectedVersion
|
|
43
|
+
})
|
|
44
|
+
.strict()
|
|
45
|
+
.refine((value) => Object.keys(value).length > 1, "Provide a changed field.");
|
|
46
|
+
export const profileSchema = z.object({ name: userName, expectedVersion }).strict();
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Transaction } from "kysely";
|
|
2
|
+
import type { IdentitySchema, Portal } from "../identity.types.js";
|
|
3
|
+
export declare function seedAccounts(env: NodeJS.ProcessEnv): {
|
|
4
|
+
email: string;
|
|
5
|
+
name: string;
|
|
6
|
+
password: string;
|
|
7
|
+
portal: "admin" | "super-admin" | "user";
|
|
8
|
+
tenantId: string;
|
|
9
|
+
}[];
|
|
10
|
+
export declare function seedUsers(trx: Transaction<IdentitySchema>, accounts: ReturnType<typeof seedAccounts>): Promise<{
|
|
11
|
+
userId: string;
|
|
12
|
+
tenantId: string;
|
|
13
|
+
portal: Portal;
|
|
14
|
+
}[]>;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { accountSchema } from "./user.schema.js";
|
|
3
|
+
import { hashPassword } from "./user.password.js";
|
|
4
|
+
export function seedAccounts(env) {
|
|
5
|
+
const tenantId = env.IDENTITY_TENANT_ID ?? "default";
|
|
6
|
+
return ["user", "admin", "super-admin"].flatMap((portal) => {
|
|
7
|
+
const prefix = `IDENTITY_SEED_${portal.toUpperCase().replace("-", "_")}`;
|
|
8
|
+
const email = env[`${prefix}_EMAIL`];
|
|
9
|
+
const password = env[`${prefix}_PASSWORD`];
|
|
10
|
+
if (!email && !password)
|
|
11
|
+
return [];
|
|
12
|
+
return [
|
|
13
|
+
accountSchema.parse({
|
|
14
|
+
email,
|
|
15
|
+
password,
|
|
16
|
+
name: env[`${prefix}_NAME`]?.trim() || portal,
|
|
17
|
+
portal,
|
|
18
|
+
tenantId
|
|
19
|
+
})
|
|
20
|
+
];
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
export async function seedUsers(trx, accounts) {
|
|
24
|
+
const created = [];
|
|
25
|
+
for (const account of accounts) {
|
|
26
|
+
const existing = await trx
|
|
27
|
+
.selectFrom("identity_users")
|
|
28
|
+
.select("id")
|
|
29
|
+
.where("email", "=", account.email)
|
|
30
|
+
.executeTakeFirst();
|
|
31
|
+
if (existing)
|
|
32
|
+
continue;
|
|
33
|
+
const userId = randomUUID();
|
|
34
|
+
await trx
|
|
35
|
+
.insertInto("identity_users")
|
|
36
|
+
.values({
|
|
37
|
+
id: userId,
|
|
38
|
+
email: account.email,
|
|
39
|
+
name: account.name,
|
|
40
|
+
password_hash: await hashPassword(account.password),
|
|
41
|
+
active: 1
|
|
42
|
+
})
|
|
43
|
+
.execute();
|
|
44
|
+
created.push({ userId, tenantId: account.tenantId, portal: account.portal });
|
|
45
|
+
}
|
|
46
|
+
return created;
|
|
47
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { IdentityUserRepository } from "./user.repository.js";
|
|
2
|
+
import type { IdentityConfig, Portal, Principal } from "../identity.types.js";
|
|
3
|
+
import type { z } from "zod";
|
|
4
|
+
import type { passwordSchema } from "./user.schema.js";
|
|
5
|
+
import type { loginSchema } from "../session/index.js";
|
|
6
|
+
export declare class IdentityService {
|
|
7
|
+
private readonly repository;
|
|
8
|
+
private readonly config;
|
|
9
|
+
private readonly dummyHash;
|
|
10
|
+
constructor(repository: IdentityUserRepository, config: IdentityConfig);
|
|
11
|
+
login(portal: Portal, input: z.infer<typeof loginSchema>, address: string, previous?: string): Promise<{
|
|
12
|
+
token: string;
|
|
13
|
+
sessionSeconds: number;
|
|
14
|
+
principal: {
|
|
15
|
+
user: {
|
|
16
|
+
id: string;
|
|
17
|
+
email: string;
|
|
18
|
+
name: string;
|
|
19
|
+
};
|
|
20
|
+
tenant: {
|
|
21
|
+
id: string;
|
|
22
|
+
name: string;
|
|
23
|
+
};
|
|
24
|
+
permissions: string[];
|
|
25
|
+
appId: string;
|
|
26
|
+
portal: Portal;
|
|
27
|
+
};
|
|
28
|
+
}>;
|
|
29
|
+
authenticate(portal: Portal, token?: string): Promise<Principal>;
|
|
30
|
+
requirePermission(principal: Principal, permission: string): void;
|
|
31
|
+
logout(portal: Portal, token?: string): Promise<void>;
|
|
32
|
+
changePassword(principal: Principal, input: z.infer<typeof passwordSchema>): Promise<void>;
|
|
33
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { randomBytes } from "node:crypto";
|
|
2
|
+
import { digest, hashPassword, verifyPassword } from "./user.password.js";
|
|
3
|
+
import { IdentityError } from "../support/identity.error.js";
|
|
4
|
+
export class IdentityService {
|
|
5
|
+
repository;
|
|
6
|
+
config;
|
|
7
|
+
dummyHash = hashPassword(randomBytes(32).toString("hex"));
|
|
8
|
+
constructor(repository, config) {
|
|
9
|
+
this.repository = repository;
|
|
10
|
+
this.config = config;
|
|
11
|
+
}
|
|
12
|
+
async login(portal, input, address, previous) {
|
|
13
|
+
const key = digest(`${this.config.appId}:${portal}:${address}:${input.email}`);
|
|
14
|
+
const ipKey = digest(`${this.config.appId}:ip:${address}`);
|
|
15
|
+
if ((await this.repository.throttle(ipKey)) > 100 || (await this.repository.throttle(key)) > 10)
|
|
16
|
+
throw new IdentityError(429, "Too many sign-in attempts. Try again later.");
|
|
17
|
+
const user = await this.repository.user(input.email);
|
|
18
|
+
const valid = await verifyPassword(input.password, user?.password_hash ?? (await this.dummyHash));
|
|
19
|
+
const tenantId = this.config.mode === "single-client" ? this.config.tenantId : input.tenantId;
|
|
20
|
+
if (!valid ||
|
|
21
|
+
!user ||
|
|
22
|
+
!Number(user.active) ||
|
|
23
|
+
!tenantId ||
|
|
24
|
+
(this.config.mode === "single-client" && input.tenantId && input.tenantId !== tenantId))
|
|
25
|
+
throw new IdentityError(401, "Invalid credentials or portal access.");
|
|
26
|
+
const principal = await this.repository.principal(user.id, tenantId, portal, this.config.appId);
|
|
27
|
+
if (!principal?.permissions.includes(`desk.${portal}`))
|
|
28
|
+
throw new IdentityError(401, "Invalid credentials or portal access.");
|
|
29
|
+
const token = randomBytes(32).toString("base64url");
|
|
30
|
+
const sessionSeconds = await this.repository.sessionSeconds(this.config.appId, this.config.sessionSeconds);
|
|
31
|
+
const created = await this.repository.createSession({
|
|
32
|
+
token_hash: digest(token),
|
|
33
|
+
user_id: user.id,
|
|
34
|
+
tenant_id: tenantId,
|
|
35
|
+
app_id: this.config.appId,
|
|
36
|
+
portal,
|
|
37
|
+
expires_at: new Date(Date.now() + sessionSeconds * 1000).toISOString()
|
|
38
|
+
}, user.password_hash, previous ? digest(previous) : undefined);
|
|
39
|
+
if (!created)
|
|
40
|
+
throw new IdentityError(401, "Invalid credentials or portal access.");
|
|
41
|
+
await this.repository.clearThrottle(key);
|
|
42
|
+
return {
|
|
43
|
+
token,
|
|
44
|
+
sessionSeconds,
|
|
45
|
+
principal: { ...principal, appId: this.config.appId, portal }
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
async authenticate(portal, token) {
|
|
49
|
+
if (!token || !/^[A-Za-z0-9_-]{43}$/.test(token))
|
|
50
|
+
throw new IdentityError(401, "Sign in to continue.");
|
|
51
|
+
const session = await this.repository.session(digest(token), this.config.appId, portal, new Date().toISOString());
|
|
52
|
+
if (!session ||
|
|
53
|
+
(this.config.mode === "single-client" && session.tenant_id !== this.config.tenantId))
|
|
54
|
+
throw new IdentityError(401, "Sign in to continue.");
|
|
55
|
+
const principal = await this.repository.principal(session.user_id, session.tenant_id, portal, this.config.appId);
|
|
56
|
+
if (!principal)
|
|
57
|
+
throw new IdentityError(401, "Sign in to continue.");
|
|
58
|
+
return { ...principal, appId: this.config.appId, portal };
|
|
59
|
+
}
|
|
60
|
+
requirePermission(principal, permission) {
|
|
61
|
+
if (!principal.permissions.includes(permission))
|
|
62
|
+
throw new IdentityError(403, "Access denied.");
|
|
63
|
+
}
|
|
64
|
+
async logout(portal, token) {
|
|
65
|
+
if (token)
|
|
66
|
+
await this.repository.revoke(digest(token), this.config.appId, portal);
|
|
67
|
+
}
|
|
68
|
+
async changePassword(principal, input) {
|
|
69
|
+
this.requirePermission(principal, "identity.password");
|
|
70
|
+
const user = await this.repository.user(principal.user.email);
|
|
71
|
+
if (!user || !(await verifyPassword(input.currentPassword, user.password_hash)))
|
|
72
|
+
throw new IdentityError(422, "Current password is incorrect.");
|
|
73
|
+
if (!(await this.repository.changePassword(user.id, user.password_hash, await hashPassword(input.password))))
|
|
74
|
+
throw new IdentityError(409, "Account changed. Sign in again.");
|
|
75
|
+
}
|
|
76
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { Generated } from "kysely";
|
|
2
|
+
import type { Portal } from "../identity.types.js";
|
|
3
|
+
export interface IdentityUserTables {
|
|
4
|
+
identity_users: {
|
|
5
|
+
id: string;
|
|
6
|
+
email: string;
|
|
7
|
+
name: string;
|
|
8
|
+
password_hash: string;
|
|
9
|
+
active: number;
|
|
10
|
+
version: Generated<number>;
|
|
11
|
+
};
|
|
12
|
+
identity_tokens: {
|
|
13
|
+
id: string;
|
|
14
|
+
token_hash: string;
|
|
15
|
+
app_id: string;
|
|
16
|
+
tenant_id: string;
|
|
17
|
+
kind: "invitation" | "recovery";
|
|
18
|
+
email: string;
|
|
19
|
+
name: string;
|
|
20
|
+
role_id: Portal;
|
|
21
|
+
user_id: string | null;
|
|
22
|
+
expires_at: string;
|
|
23
|
+
consumed_at: string | null;
|
|
24
|
+
delivered: number;
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
export type IdentityUserRow = IdentityUserTables["identity_users"];
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { createIdentityUserRoleProvider } from "./user-role.provider.js";
|
|
2
|
+
export type { IdentityUserRoleProvider } from "./user-role.provider.js";
|
|
3
|
+
export { protectAdministrators } from "./user-role.policy.js";
|
|
4
|
+
export { membershipDeleteQuerySchema, membershipSchema, membershipUpdateSchema } from "./user-role.schema.js";
|
|
5
|
+
export { seedMemberships } from "./user-role.seed.js";
|
|
6
|
+
export { userRoleResource, userRoleResourceRoute } from "./user-role.routes.js";
|
|
7
|
+
export type { IdentityMembershipRow } from "./user-role.types.js";
|
|
8
|
+
export { IdentityUserRoleController } from "./user-role.controller.js";
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { createIdentityUserRoleProvider } from "./user-role.provider.js";
|
|
2
|
+
export { protectAdministrators } from "./user-role.policy.js";
|
|
3
|
+
export { membershipDeleteQuerySchema, membershipSchema, membershipUpdateSchema } from "./user-role.schema.js";
|
|
4
|
+
export { seedMemberships } from "./user-role.seed.js";
|
|
5
|
+
export { userRoleResource, userRoleResourceRoute } from "./user-role.routes.js";
|
|
6
|
+
export { IdentityUserRoleController } from "./user-role.controller.js";
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { Principal } from "../identity.types.js";
|
|
2
|
+
import type { IdentityListQuery } from "../support/pagination.schema.js";
|
|
3
|
+
import type { IdentityUserRoleProvider } from "./user-role.provider.js";
|
|
4
|
+
export declare class IdentityUserRoleController {
|
|
5
|
+
private readonly service;
|
|
6
|
+
constructor(service: IdentityUserRoleProvider);
|
|
7
|
+
index(actor: Principal, query: IdentityListQuery): Promise<{
|
|
8
|
+
data: {
|
|
9
|
+
active: boolean;
|
|
10
|
+
version: number;
|
|
11
|
+
}[];
|
|
12
|
+
meta: {
|
|
13
|
+
page: number;
|
|
14
|
+
perPage: number;
|
|
15
|
+
total: number;
|
|
16
|
+
lastPage: number;
|
|
17
|
+
};
|
|
18
|
+
}>;
|
|
19
|
+
show(actor: Principal, id: string): Promise<{
|
|
20
|
+
active: boolean;
|
|
21
|
+
version: number;
|
|
22
|
+
} | undefined>;
|
|
23
|
+
store(actor: Principal, raw: unknown): Promise<{
|
|
24
|
+
id: string;
|
|
25
|
+
userId: string;
|
|
26
|
+
tenantId: string;
|
|
27
|
+
roleId: string;
|
|
28
|
+
portal: import("../identity.types.js").Portal;
|
|
29
|
+
active: boolean;
|
|
30
|
+
version: number;
|
|
31
|
+
}>;
|
|
32
|
+
update(actor: Principal, id: string, raw: unknown): Promise<{
|
|
33
|
+
id: string;
|
|
34
|
+
userId: string;
|
|
35
|
+
tenantId: string;
|
|
36
|
+
roleId: string;
|
|
37
|
+
portal: import("../identity.types.js").Portal;
|
|
38
|
+
active: boolean;
|
|
39
|
+
version: number;
|
|
40
|
+
}>;
|
|
41
|
+
destroy(actor: Principal, id: string, expectedVersion?: number): Promise<void>;
|
|
42
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export class IdentityUserRoleController {
|
|
2
|
+
service;
|
|
3
|
+
constructor(service) {
|
|
4
|
+
this.service = service;
|
|
5
|
+
}
|
|
6
|
+
index(actor, query) {
|
|
7
|
+
return this.service.list(actor, query);
|
|
8
|
+
}
|
|
9
|
+
show(actor, id) {
|
|
10
|
+
return this.service.show(actor, id);
|
|
11
|
+
}
|
|
12
|
+
store(actor, raw) {
|
|
13
|
+
return this.service.createMembership(actor, raw);
|
|
14
|
+
}
|
|
15
|
+
update(actor, id, raw) {
|
|
16
|
+
return this.service.updateMembership(actor, id, raw);
|
|
17
|
+
}
|
|
18
|
+
destroy(actor, id, expectedVersion) {
|
|
19
|
+
return this.service.removeMembership(actor, id, expectedVersion);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { Kysely, Transaction } from "kysely";
|
|
2
|
+
import type { IdentitySchema } from "../identity.types.js";
|
|
3
|
+
export declare function protectAdministrators(trx: Transaction<IdentitySchema>, userId: string, activeUsers: (db: Kysely<IdentitySchema>, ids: string[]) => Promise<string[]>, tenantId?: string, roleId?: string): Promise<void>;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { IdentityError } from "../support/identity.error.js";
|
|
2
|
+
export async function protectAdministrators(trx, userId, activeUsers, tenantId, roleId) {
|
|
3
|
+
let query = trx
|
|
4
|
+
.selectFrom("identity_memberships")
|
|
5
|
+
.selectAll()
|
|
6
|
+
.where("user_id", "=", userId)
|
|
7
|
+
.where("active", "=", 1)
|
|
8
|
+
.where("role_id", "in", ["admin", "super-admin"]);
|
|
9
|
+
if (tenantId)
|
|
10
|
+
query = query.where("tenant_id", "=", tenantId);
|
|
11
|
+
if (roleId)
|
|
12
|
+
query = query.where("role_id", "=", roleId);
|
|
13
|
+
for (const member of await query.execute()) {
|
|
14
|
+
const peers = await trx
|
|
15
|
+
.selectFrom("identity_memberships")
|
|
16
|
+
.select("user_id")
|
|
17
|
+
.where("tenant_id", "=", member.tenant_id)
|
|
18
|
+
.where("role_id", "=", member.role_id)
|
|
19
|
+
.where("active", "=", 1)
|
|
20
|
+
.where("user_id", "!=", userId)
|
|
21
|
+
.execute();
|
|
22
|
+
if ((await activeUsers(trx, peers.map((peer) => peer.user_id))).length === 0)
|
|
23
|
+
throw new IdentityError(409, "Keep at least one active administrator for this role and organization.");
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { Kysely, Transaction } from "kysely";
|
|
2
|
+
import type { IdentitySchema, Portal, Principal } from "../identity.types.js";
|
|
3
|
+
import type { IdentityRoleResolver } from "../role/index.js";
|
|
4
|
+
import type { IdentityMutation } from "../identity.types.js";
|
|
5
|
+
import { IdentityUserRoleService } from "./user-role.service.js";
|
|
6
|
+
import { customRoleUserIds, assertManageableUser, assignInvitedMembership, activeMembership, membershipTenantIds } from "./user-role.repository.js";
|
|
7
|
+
import { userVisibility, type MembershipReadSources } from "./user-role.repository.js";
|
|
8
|
+
export type IdentityUserRoleProvider = Pick<IdentityUserRoleService, "list" | "show" | "createMembership" | "updateMembership" | "removeMembership" | "assignInitialMembership"> & {
|
|
9
|
+
verify(): Promise<void>;
|
|
10
|
+
tenantIdsForUser: typeof membershipTenantIds;
|
|
11
|
+
userIdsForCustomRole: typeof customRoleUserIds;
|
|
12
|
+
isManageableUser: typeof assertManageableUser;
|
|
13
|
+
assignInvitedMembership: typeof assignInvitedMembership;
|
|
14
|
+
activeMembership: typeof activeMembership;
|
|
15
|
+
userVisibility: typeof userVisibility;
|
|
16
|
+
protectAdministrators(trx: Transaction<IdentitySchema>, userId: string): Promise<void>;
|
|
17
|
+
};
|
|
18
|
+
export declare function createIdentityUserRoleProvider(database: Kysely<IdentitySchema>, readSources: MembershipReadSources, activeUsers: (db: Kysely<IdentitySchema>, ids: string[]) => Promise<string[]>, resolveRole: IdentityRoleResolver, customRoleInApp: (db: Kysely<IdentitySchema>, id: string, appId: string) => Promise<unknown>, mutate: IdentityMutation, assertUserVisible: (actor: Principal, userId: string) => Promise<unknown>, activeOrganization: (trx: Transaction<IdentitySchema>, id: string) => Promise<unknown>, recordAudit: (trx: Transaction<IdentitySchema>, actor: Principal, tenantId: string, action: string, id: string) => Promise<void>, revokeMembershipSessions: (trx: Transaction<IdentitySchema>, appId: string, userId: string, tenantId: string, portal: Portal) => Promise<void>): IdentityUserRoleProvider;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { IdentityUserRoleService } from "./user-role.service.js";
|
|
2
|
+
import { customRoleUserIds, assertManageableUser, assignInvitedMembership, activeMembership, membershipTenantIds, verifyMembershipSchema } from "./user-role.repository.js";
|
|
3
|
+
import { userVisibility } from "./user-role.repository.js";
|
|
4
|
+
import { protectAdministrators } from "./user-role.policy.js";
|
|
5
|
+
export function createIdentityUserRoleProvider(database, readSources, activeUsers, resolveRole, customRoleInApp, mutate, assertUserVisible, activeOrganization, recordAudit, revokeMembershipSessions) {
|
|
6
|
+
const service = new IdentityUserRoleService(database, readSources, activeUsers, resolveRole, customRoleInApp, mutate, assertUserVisible, activeOrganization, recordAudit, revokeMembershipSessions);
|
|
7
|
+
return Object.freeze({
|
|
8
|
+
verify: () => verifyMembershipSchema(database),
|
|
9
|
+
tenantIdsForUser: membershipTenantIds,
|
|
10
|
+
userIdsForCustomRole: customRoleUserIds,
|
|
11
|
+
isManageableUser: assertManageableUser,
|
|
12
|
+
assignInvitedMembership,
|
|
13
|
+
activeMembership,
|
|
14
|
+
userVisibility,
|
|
15
|
+
protectAdministrators: (trx, userId) => protectAdministrators(trx, userId, activeUsers),
|
|
16
|
+
list: service.list.bind(service),
|
|
17
|
+
show: service.show.bind(service),
|
|
18
|
+
createMembership: service.createMembership.bind(service),
|
|
19
|
+
updateMembership: service.updateMembership.bind(service),
|
|
20
|
+
removeMembership: service.removeMembership.bind(service),
|
|
21
|
+
assignInitialMembership: service.assignInitialMembership.bind(service)
|
|
22
|
+
});
|
|
23
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { type Kysely, type RawBuilder, type Transaction } from "kysely";
|
|
2
|
+
import type { IdentitySchema, Principal } from "../identity.types.js";
|
|
3
|
+
import type { IdentityListQuery } from "../support/pagination.schema.js";
|
|
4
|
+
export interface MembershipReadSources {
|
|
5
|
+
users(): RawBuilder<unknown>;
|
|
6
|
+
organizations(): RawBuilder<unknown>;
|
|
7
|
+
roles(): RawBuilder<unknown>;
|
|
8
|
+
customRoleIds(appId: string): RawBuilder<unknown>;
|
|
9
|
+
}
|
|
10
|
+
export declare function listMemberships(db: Kysely<IdentitySchema>, actor: Principal, query: IdentityListQuery, sources: MembershipReadSources): Promise<{
|
|
11
|
+
data: {
|
|
12
|
+
active: boolean;
|
|
13
|
+
version: number;
|
|
14
|
+
}[];
|
|
15
|
+
meta: {
|
|
16
|
+
page: number;
|
|
17
|
+
perPage: number;
|
|
18
|
+
total: number;
|
|
19
|
+
lastPage: number;
|
|
20
|
+
};
|
|
21
|
+
}>;
|
|
22
|
+
export declare function showMembership(db: Kysely<IdentitySchema>, actor: Principal, id: string, sources: MembershipReadSources): Promise<{
|
|
23
|
+
active: boolean;
|
|
24
|
+
version: number;
|
|
25
|
+
} | undefined>;
|
|
26
|
+
export declare function userVisibility(actor: Principal, customRoleIds: (appId: string) => RawBuilder<unknown>, userId: RawBuilder<unknown>): RawBuilder<unknown>;
|
|
27
|
+
export declare function verifyMembershipSchema(db: Kysely<IdentitySchema>): Promise<void>;
|
|
28
|
+
export declare function membershipTenantIds(trx: Transaction<IdentitySchema>, userId: string): Promise<string[]>;
|
|
29
|
+
export declare function customRoleUserIds(trx: Transaction<IdentitySchema>, roleId: string): Promise<string[]>;
|
|
30
|
+
export declare function assertManageableUser(trx: Transaction<IdentitySchema>, userId: string): Promise<"manageable" | "privileged" | "shared">;
|
|
31
|
+
export declare function assignInvitedMembership(trx: Transaction<IdentitySchema>, userId: string, tenantId: string, roleId: "user" | "admin" | "super-admin"): Promise<void>;
|
|
32
|
+
export declare function activeMembership(db: Kysely<IdentitySchema>, userId: string, tenantId: string, portal: "user" | "admin" | "super-admin"): Promise<{
|
|
33
|
+
custom_role_id: string | null;
|
|
34
|
+
} | undefined>;
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { sql } from "kysely";
|
|
2
|
+
import { listRows, showRow } from "../support/pagination.js";
|
|
3
|
+
const presentMembership = (row) => ({
|
|
4
|
+
...row,
|
|
5
|
+
active: Boolean(row.active),
|
|
6
|
+
version: Number(row.version)
|
|
7
|
+
});
|
|
8
|
+
export function listMemberships(db, actor, query, sources) {
|
|
9
|
+
return listRows(db, membershipListSource(actor, sources), ["id", "userName", "organizationName", "roleName"], query, presentMembership);
|
|
10
|
+
}
|
|
11
|
+
export function showMembership(db, actor, id, sources) {
|
|
12
|
+
return showRow(db, membershipListSource(actor, sources), id, presentMembership);
|
|
13
|
+
}
|
|
14
|
+
export function userVisibility(actor, customRoleIds, userId) {
|
|
15
|
+
const eligible = sql `select m.user_id from identity_memberships m
|
|
16
|
+
where m.user_id=${userId} and (m.custom_role_id is null or m.custom_role_id in (${customRoleIds(actor.appId)}))
|
|
17
|
+
${actor.portal === "super-admin" ? sql `` : sql `and m.tenant_id=${actor.tenant.id}`}`;
|
|
18
|
+
return actor.portal === "super-admin"
|
|
19
|
+
? sql `exists (${eligible}) or not exists (select 1 from identity_memberships m where m.user_id=${userId})`
|
|
20
|
+
: sql `exists (${eligible})`;
|
|
21
|
+
}
|
|
22
|
+
export async function verifyMembershipSchema(db) {
|
|
23
|
+
await db
|
|
24
|
+
.selectFrom("identity_memberships")
|
|
25
|
+
.select(["custom_role_id", "active", "version"])
|
|
26
|
+
.limit(1)
|
|
27
|
+
.execute();
|
|
28
|
+
}
|
|
29
|
+
export async function membershipTenantIds(trx, userId) {
|
|
30
|
+
const rows = await trx
|
|
31
|
+
.selectFrom("identity_memberships")
|
|
32
|
+
.select("tenant_id")
|
|
33
|
+
.where("user_id", "=", userId)
|
|
34
|
+
.distinct()
|
|
35
|
+
.execute();
|
|
36
|
+
return rows.map((row) => row.tenant_id);
|
|
37
|
+
}
|
|
38
|
+
export async function customRoleUserIds(trx, roleId) {
|
|
39
|
+
const rows = await trx
|
|
40
|
+
.selectFrom("identity_memberships")
|
|
41
|
+
.select("user_id")
|
|
42
|
+
.where("custom_role_id", "=", roleId)
|
|
43
|
+
.execute();
|
|
44
|
+
return rows.map((row) => row.user_id);
|
|
45
|
+
}
|
|
46
|
+
export async function assertManageableUser(trx, userId) {
|
|
47
|
+
const privileged = await trx
|
|
48
|
+
.selectFrom("identity_memberships")
|
|
49
|
+
.select("role_id")
|
|
50
|
+
.where("user_id", "=", userId)
|
|
51
|
+
.where("role_id", "in", ["admin", "super-admin"])
|
|
52
|
+
.executeTakeFirst();
|
|
53
|
+
if (privileged)
|
|
54
|
+
return "privileged";
|
|
55
|
+
const tenants = await trx
|
|
56
|
+
.selectFrom("identity_memberships")
|
|
57
|
+
.select("tenant_id")
|
|
58
|
+
.where("user_id", "=", userId)
|
|
59
|
+
.distinct()
|
|
60
|
+
.execute();
|
|
61
|
+
return tenants.length > 1 ? "shared" : "manageable";
|
|
62
|
+
}
|
|
63
|
+
export async function assignInvitedMembership(trx, userId, tenantId, roleId) {
|
|
64
|
+
await trx
|
|
65
|
+
.insertInto("identity_memberships")
|
|
66
|
+
.values({ user_id: userId, tenant_id: tenantId, role_id: roleId })
|
|
67
|
+
.execute();
|
|
68
|
+
}
|
|
69
|
+
export function activeMembership(db, userId, tenantId, portal) {
|
|
70
|
+
return db
|
|
71
|
+
.selectFrom("identity_memberships")
|
|
72
|
+
.select("custom_role_id")
|
|
73
|
+
.where("user_id", "=", userId)
|
|
74
|
+
.where("tenant_id", "=", tenantId)
|
|
75
|
+
.where("role_id", "=", portal)
|
|
76
|
+
.where("active", "=", 1)
|
|
77
|
+
.executeTakeFirst();
|
|
78
|
+
}
|
|
79
|
+
function membershipListSource(actor, sources) {
|
|
80
|
+
const global = actor.portal === "super-admin";
|
|
81
|
+
return sql `select m.user_id||'~'||m.tenant_id||'~'||m.role_id as id,m.user_id as userId,m.tenant_id as tenantId,
|
|
82
|
+
coalesce(m.custom_role_id,m.role_id) as roleId,m.role_id as portal,m.active,m.version,u.name as userName,t.name as organizationName,
|
|
83
|
+
coalesce(c.name,m.role_id) as roleName from identity_memberships m
|
|
84
|
+
join (${sources.users()}) u on u.id=m.user_id join (${sources.organizations()}) t on t.id=m.tenant_id
|
|
85
|
+
left join (${sources.roles()}) c on c.id=m.custom_role_id
|
|
86
|
+
where (m.custom_role_id is null or c.app_id=${actor.appId}) ${global ? sql `` : sql `and m.tenant_id=${actor.tenant.id}`}`;
|
|
87
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export declare const userRoleResource: "memberships";
|
|
2
|
+
export declare const userRoleResourceRoute: (controller: import("./user-role.controller.js").IdentityUserRoleController) => {
|
|
3
|
+
resource: "memberships";
|
|
4
|
+
controller: import("./user-role.controller.js").IdentityUserRoleController;
|
|
5
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export declare const membershipDeleteQuerySchema: z.ZodObject<{
|
|
3
|
+
expectedVersion: z.ZodPipe<z.ZodString, z.ZodTransform<number, string>>;
|
|
4
|
+
}, z.core.$strict>;
|
|
5
|
+
export declare const membershipSchema: z.ZodObject<{
|
|
6
|
+
userId: z.ZodString;
|
|
7
|
+
tenantId: z.ZodOptional<z.ZodString>;
|
|
8
|
+
roleId: z.ZodString;
|
|
9
|
+
}, z.core.$strict>;
|
|
10
|
+
export declare const membershipUpdateSchema: z.ZodObject<{
|
|
11
|
+
roleId: z.ZodString;
|
|
12
|
+
active: z.ZodBoolean;
|
|
13
|
+
expectedVersion: z.ZodNumber;
|
|
14
|
+
}, z.core.$strict>;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { identityKeySchema, resourceIdSchema } from "../support/identity.schema.js";
|
|
3
|
+
const expectedVersion = z.number().int().min(0);
|
|
4
|
+
export const membershipDeleteQuerySchema = z
|
|
5
|
+
.object({
|
|
6
|
+
expectedVersion: z
|
|
7
|
+
.string()
|
|
8
|
+
.regex(/^(0|[1-9][0-9]{0,9})$/)
|
|
9
|
+
.transform(Number)
|
|
10
|
+
})
|
|
11
|
+
.strict();
|
|
12
|
+
export const membershipSchema = z
|
|
13
|
+
.object({
|
|
14
|
+
userId: identityKeySchema,
|
|
15
|
+
tenantId: identityKeySchema.optional(),
|
|
16
|
+
roleId: resourceIdSchema
|
|
17
|
+
})
|
|
18
|
+
.strict();
|
|
19
|
+
export const membershipUpdateSchema = z
|
|
20
|
+
.object({
|
|
21
|
+
roleId: resourceIdSchema,
|
|
22
|
+
active: z.boolean(),
|
|
23
|
+
expectedVersion
|
|
24
|
+
})
|
|
25
|
+
.strict();
|