@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
package/dist/modules/identity/{identity.lifecycle-service.js → user/user.lifecycle.service.js}
RENAMED
|
@@ -1,36 +1,33 @@
|
|
|
1
1
|
import { randomBytes, randomUUID } from "node:crypto";
|
|
2
|
-
import { digest, hashPassword } from "./
|
|
3
|
-
import { IdentityError } from "
|
|
4
|
-
import {
|
|
5
|
-
import { checkIdentityRequest } from "
|
|
6
|
-
import { invitationSchema, recoverySchema, tokenCompletionSchema
|
|
2
|
+
import { digest, hashPassword } from "./user.password.js";
|
|
3
|
+
import { IdentityError } from "../support/identity.error.js";
|
|
4
|
+
import { IdentityUserRepository } from "./user.repository.js";
|
|
5
|
+
import { checkIdentityRequest } from "../support/identity.request-context.js";
|
|
6
|
+
import { invitationSchema, recoverySchema, tokenCompletionSchema } from "./user.lifecycle.schema.js";
|
|
7
7
|
export class IdentityLifecycleService {
|
|
8
8
|
db;
|
|
9
9
|
config;
|
|
10
10
|
delivery;
|
|
11
|
-
|
|
11
|
+
permissions;
|
|
12
|
+
ownership;
|
|
13
|
+
constructor(db, config, delivery, permissions, ownership) {
|
|
12
14
|
this.db = db;
|
|
13
15
|
this.config = config;
|
|
14
16
|
this.delivery = delivery;
|
|
17
|
+
this.permissions = permissions;
|
|
18
|
+
this.ownership = ownership;
|
|
15
19
|
}
|
|
16
20
|
async invite(actor, raw) {
|
|
17
|
-
if (actor.portal === "user" ||
|
|
18
|
-
!actor.permissions.includes("identity.manage"))
|
|
21
|
+
if (actor.portal === "user" || !actor.permissions.includes("identity.manage"))
|
|
19
22
|
throw new IdentityError(403, "Access denied.");
|
|
20
23
|
const input = invitationSchema.parse(raw);
|
|
21
24
|
const tenantId = input.tenantId ?? actor.tenant.id;
|
|
22
|
-
if (actor.portal !== "super-admin" &&
|
|
23
|
-
(tenantId !== actor.tenant.id || input.roleId !== "user"))
|
|
25
|
+
if (actor.portal !== "super-admin" && (tenantId !== actor.tenant.id || input.roleId !== "user"))
|
|
24
26
|
throw new IdentityError(403, "Access denied.");
|
|
25
27
|
this.requireDelivery();
|
|
26
|
-
if ((await new
|
|
28
|
+
if ((await new IdentityUserRepository(this.db, this.permissions, this.ownership).throttle(digest(`${actor.appId}:invitation:${actor.user.id}`))) > 20)
|
|
27
29
|
throw new IdentityError(429, "Too many invitations. Try again later.");
|
|
28
|
-
const tenant = await this.db
|
|
29
|
-
.selectFrom("identity_tenants")
|
|
30
|
-
.select("id")
|
|
31
|
-
.where("id", "=", tenantId)
|
|
32
|
-
.where("active", "=", 1)
|
|
33
|
-
.executeTakeFirst();
|
|
30
|
+
const tenant = await this.ownership.activeOrganization(this.db, tenantId);
|
|
34
31
|
if (!tenant)
|
|
35
32
|
throw new IdentityError(422, "Organization is inactive or unavailable.");
|
|
36
33
|
if (await this.db
|
|
@@ -44,24 +41,23 @@ export class IdentityLifecycleService {
|
|
|
44
41
|
}
|
|
45
42
|
async requestRecovery(portal, raw, address) {
|
|
46
43
|
const input = recoverySchema
|
|
47
|
-
.refine((value) => this.config.mode !== "multi-tenant" || Boolean(value.tenantId), {
|
|
44
|
+
.refine((value) => this.config.mode !== "multi-tenant" || Boolean(value.tenantId), {
|
|
45
|
+
path: ["tenantId"],
|
|
46
|
+
message: "Enter an organization ID."
|
|
47
|
+
})
|
|
48
48
|
.parse(raw);
|
|
49
49
|
this.requireDelivery();
|
|
50
|
-
const repository = new
|
|
50
|
+
const repository = new IdentityUserRepository(this.db, this.permissions, this.ownership);
|
|
51
51
|
if ((await repository.throttle(digest(`${this.config.appId}:recovery-ip:${address}`))) > 20)
|
|
52
52
|
throw new IdentityError(429, "Too many recovery requests. Try again later.");
|
|
53
53
|
if ((await repository.throttle(digest(`${this.config.appId}:recovery:${address}:${input.email}`))) > 5)
|
|
54
54
|
throw new IdentityError(429, "Too many recovery requests. Try again later.");
|
|
55
|
-
const tenantId = this.config.mode === "single-client"
|
|
56
|
-
? this.config.tenantId
|
|
57
|
-
: input.tenantId;
|
|
55
|
+
const tenantId = this.config.mode === "single-client" ? this.config.tenantId : input.tenantId;
|
|
58
56
|
const user = await repository.user(input.email);
|
|
59
57
|
const principal = user?.active && tenantId
|
|
60
58
|
? await repository.principal(user.id, tenantId, portal, this.config.appId)
|
|
61
59
|
: null;
|
|
62
|
-
if (user?.active &&
|
|
63
|
-
tenantId &&
|
|
64
|
-
principal?.permissions.includes("identity.password")) {
|
|
60
|
+
if (user?.active && tenantId && principal?.permissions.includes("identity.password")) {
|
|
65
61
|
await this.db
|
|
66
62
|
.updateTable("identity_tokens")
|
|
67
63
|
.set({ consumed_at: new Date().toISOString() })
|
|
@@ -72,12 +68,12 @@ export class IdentityLifecycleService {
|
|
|
72
68
|
await this.issue("recovery", user.email, user.name, portal, tenantId, user.id);
|
|
73
69
|
}
|
|
74
70
|
return {
|
|
75
|
-
message: "If this account can recover access, an email will arrive."
|
|
71
|
+
message: "If this account can recover access, an email will arrive."
|
|
76
72
|
};
|
|
77
73
|
}
|
|
78
74
|
async complete(portal, kind, raw, address) {
|
|
79
75
|
const input = tokenCompletionSchema.parse(raw);
|
|
80
|
-
const repository = new
|
|
76
|
+
const repository = new IdentityUserRepository(this.db, this.permissions, this.ownership);
|
|
81
77
|
if ((await repository.throttle(digest(`${this.config.appId}:complete:${address}`))) > 10)
|
|
82
78
|
throw new IdentityError(429, "Too many account requests. Try again later.");
|
|
83
79
|
const available = await this.db
|
|
@@ -109,15 +105,9 @@ export class IdentityLifecycleService {
|
|
|
109
105
|
.where("expires_at", ">", new Date().toISOString())
|
|
110
106
|
.executeTakeFirst();
|
|
111
107
|
if (!token ||
|
|
112
|
-
(this.config.mode === "single-client" &&
|
|
113
|
-
token.tenant_id !== this.config.tenantId))
|
|
108
|
+
(this.config.mode === "single-client" && token.tenant_id !== this.config.tenantId))
|
|
114
109
|
throw new IdentityError(422, "This link is invalid or expired.");
|
|
115
|
-
const activeTenant = await trx
|
|
116
|
-
.selectFrom("identity_tenants")
|
|
117
|
-
.select("id")
|
|
118
|
-
.where("id", "=", token.tenant_id)
|
|
119
|
-
.where("active", "=", 1)
|
|
120
|
-
.executeTakeFirst();
|
|
110
|
+
const activeTenant = await this.ownership.activeOrganization(trx, token.tenant_id);
|
|
121
111
|
if (!activeTenant)
|
|
122
112
|
throw new IdentityError(422, "This link is invalid or expired.");
|
|
123
113
|
const claimed = await trx
|
|
@@ -143,20 +133,13 @@ export class IdentityLifecycleService {
|
|
|
143
133
|
email: token.email,
|
|
144
134
|
name: token.name,
|
|
145
135
|
password_hash: passwordHash,
|
|
146
|
-
active: 1
|
|
147
|
-
})
|
|
148
|
-
.execute();
|
|
149
|
-
await trx
|
|
150
|
-
.insertInto("identity_memberships")
|
|
151
|
-
.values({
|
|
152
|
-
user_id: userId,
|
|
153
|
-
tenant_id: token.tenant_id,
|
|
154
|
-
role_id: token.role_id,
|
|
136
|
+
active: 1
|
|
155
137
|
})
|
|
156
138
|
.execute();
|
|
139
|
+
await this.ownership.assignInvitedMembership(trx, userId, token.tenant_id, token.role_id);
|
|
157
140
|
}
|
|
158
141
|
else {
|
|
159
|
-
const principal = await new
|
|
142
|
+
const principal = await new IdentityUserRepository(trx, this.permissions, this.ownership).principal(userId, token.tenant_id, portal, this.config.appId);
|
|
160
143
|
if (!principal?.permissions.includes("identity.password"))
|
|
161
144
|
throw new IdentityError(422, "This link is invalid or expired.");
|
|
162
145
|
await trx
|
|
@@ -164,10 +147,7 @@ export class IdentityLifecycleService {
|
|
|
164
147
|
.set({ password_hash: passwordHash })
|
|
165
148
|
.where("id", "=", userId)
|
|
166
149
|
.execute();
|
|
167
|
-
await trx
|
|
168
|
-
.deleteFrom("identity_sessions")
|
|
169
|
-
.where("user_id", "=", userId)
|
|
170
|
-
.execute();
|
|
150
|
+
await this.ownership.revokeUserSessions(trx, userId);
|
|
171
151
|
await trx
|
|
172
152
|
.updateTable("identity_tokens")
|
|
173
153
|
.set({ consumed_at: new Date().toISOString() })
|
|
@@ -175,25 +155,19 @@ export class IdentityLifecycleService {
|
|
|
175
155
|
.where("kind", "=", "recovery")
|
|
176
156
|
.execute();
|
|
177
157
|
}
|
|
178
|
-
await trx
|
|
179
|
-
.
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
app_id: this.config.appId,
|
|
183
|
-
actor_id: userId,
|
|
184
|
-
tenant_id: token.tenant_id,
|
|
158
|
+
await this.ownership.recordEvent(trx, {
|
|
159
|
+
appId: this.config.appId,
|
|
160
|
+
actorId: userId,
|
|
161
|
+
tenantId: token.tenant_id,
|
|
185
162
|
action: `identity.${kind}.completed`,
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
})
|
|
189
|
-
.execute();
|
|
163
|
+
resourceId: token.id
|
|
164
|
+
});
|
|
190
165
|
checkIdentityRequest();
|
|
191
166
|
return { message: "Account access updated. Sign in to continue." };
|
|
192
167
|
});
|
|
193
168
|
}
|
|
194
169
|
async list(actor, query) {
|
|
195
|
-
if (actor.portal === "user" ||
|
|
196
|
-
!actor.permissions.includes("identity.manage"))
|
|
170
|
+
if (actor.portal === "user" || !actor.permissions.includes("identity.manage"))
|
|
197
171
|
throw new IdentityError(403, "Access denied.");
|
|
198
172
|
let select = this.db
|
|
199
173
|
.selectFrom("identity_tokens")
|
|
@@ -202,10 +176,7 @@ export class IdentityLifecycleService {
|
|
|
202
176
|
if (actor.portal !== "super-admin")
|
|
203
177
|
select = select.where("tenant_id", "=", actor.tenant.id);
|
|
204
178
|
if (query.search)
|
|
205
|
-
select = select.where((eb) => eb.or([
|
|
206
|
-
eb("email", "like", `%${query.search}%`),
|
|
207
|
-
eb("name", "like", `%${query.search}%`),
|
|
208
|
-
]));
|
|
179
|
+
select = select.where((eb) => eb.or([eb("email", "like", `%${query.search}%`), eb("name", "like", `%${query.search}%`)]));
|
|
209
180
|
const count = await select
|
|
210
181
|
.select((eb) => eb.fn.countAll().as("total"))
|
|
211
182
|
.executeTakeFirstOrThrow();
|
|
@@ -223,13 +194,12 @@ export class IdentityLifecycleService {
|
|
|
223
194
|
page: query.page,
|
|
224
195
|
perPage: query.perPage,
|
|
225
196
|
total,
|
|
226
|
-
lastPage: Math.max(1, Math.ceil(total / query.perPage))
|
|
227
|
-
}
|
|
197
|
+
lastPage: Math.max(1, Math.ceil(total / query.perPage))
|
|
198
|
+
}
|
|
228
199
|
};
|
|
229
200
|
}
|
|
230
201
|
async show(actor, id) {
|
|
231
|
-
if (actor.portal === "user" ||
|
|
232
|
-
!actor.permissions.includes("identity.manage"))
|
|
202
|
+
if (actor.portal === "user" || !actor.permissions.includes("identity.manage"))
|
|
233
203
|
throw new IdentityError(403, "Access denied.");
|
|
234
204
|
let query = this.db
|
|
235
205
|
.selectFrom("identity_tokens")
|
|
@@ -252,14 +222,13 @@ export class IdentityLifecycleService {
|
|
|
252
222
|
email: old.email,
|
|
253
223
|
name: old.name,
|
|
254
224
|
roleId: old.roleId,
|
|
255
|
-
tenantId: old.tenantId
|
|
225
|
+
tenantId: old.tenantId
|
|
256
226
|
});
|
|
257
227
|
await this.revoke(actor, id);
|
|
258
228
|
return result;
|
|
259
229
|
}
|
|
260
230
|
async revoke(actor, id) {
|
|
261
|
-
if (actor.portal === "user" ||
|
|
262
|
-
!actor.permissions.includes("identity.manage"))
|
|
231
|
+
if (actor.portal === "user" || !actor.permissions.includes("identity.manage"))
|
|
263
232
|
throw new IdentityError(403, "Access denied.");
|
|
264
233
|
const token = await this.db
|
|
265
234
|
.selectFrom("identity_tokens")
|
|
@@ -268,8 +237,7 @@ export class IdentityLifecycleService {
|
|
|
268
237
|
.where("app_id", "=", actor.appId)
|
|
269
238
|
.where("kind", "=", "invitation")
|
|
270
239
|
.executeTakeFirst();
|
|
271
|
-
if (!token ||
|
|
272
|
-
(actor.portal !== "super-admin" && token.tenant_id !== actor.tenant.id))
|
|
240
|
+
if (!token || (actor.portal !== "super-admin" && token.tenant_id !== actor.tenant.id))
|
|
273
241
|
throw new IdentityError(404, "Invitation not found.");
|
|
274
242
|
await this.db.transaction().execute(async (trx) => {
|
|
275
243
|
checkIdentityRequest();
|
|
@@ -283,18 +251,13 @@ export class IdentityLifecycleService {
|
|
|
283
251
|
query = query.where("tenant_id", "=", actor.tenant.id);
|
|
284
252
|
if ((await query.executeTakeFirst()).numUpdatedRows !== 1n)
|
|
285
253
|
throw new IdentityError(404, "Invitation not found.");
|
|
286
|
-
await trx
|
|
287
|
-
.
|
|
288
|
-
.
|
|
289
|
-
|
|
290
|
-
app_id: actor.appId,
|
|
291
|
-
actor_id: actor.user.id,
|
|
292
|
-
tenant_id: token.tenant_id,
|
|
254
|
+
await this.ownership.recordEvent(trx, {
|
|
255
|
+
appId: actor.appId,
|
|
256
|
+
actorId: actor.user.id,
|
|
257
|
+
tenantId: token.tenant_id,
|
|
293
258
|
action: "identity.invitation.revoked",
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
})
|
|
297
|
-
.execute();
|
|
259
|
+
resourceId: id
|
|
260
|
+
});
|
|
298
261
|
checkIdentityRequest();
|
|
299
262
|
});
|
|
300
263
|
}
|
|
@@ -316,7 +279,7 @@ export class IdentityLifecycleService {
|
|
|
316
279
|
user_id: userId,
|
|
317
280
|
expires_at: new Date(Date.now() + (kind === "recovery" ? 1800 : 86400) * 1000).toISOString(),
|
|
318
281
|
consumed_at: null,
|
|
319
|
-
delivered: 0
|
|
282
|
+
delivered: 0
|
|
320
283
|
};
|
|
321
284
|
checkIdentityRequest();
|
|
322
285
|
await this.db.insertInto("identity_tokens").values(row).execute();
|
|
@@ -325,10 +288,8 @@ export class IdentityLifecycleService {
|
|
|
325
288
|
try {
|
|
326
289
|
const delivered = await this.delivery.send({
|
|
327
290
|
to: email,
|
|
328
|
-
subject: kind === "invitation"
|
|
329
|
-
|
|
330
|
-
: "Recover account access",
|
|
331
|
-
text: `${name}, open this link to ${kind === "invitation" ? "set up your account" : "reset your password"}: ${link}\nThis link expires at ${row.expires_at}.`,
|
|
291
|
+
subject: kind === "invitation" ? "Account invitation" : "Recover account access",
|
|
292
|
+
text: `${name}, open this link to ${kind === "invitation" ? "set up your account" : "reset your password"}: ${link}\nThis link expires at ${row.expires_at}.`
|
|
332
293
|
});
|
|
333
294
|
if (!delivered.messageId)
|
|
334
295
|
throw new Error("Missing delivery receipt.");
|
|
@@ -339,27 +300,19 @@ export class IdentityLifecycleService {
|
|
|
339
300
|
.set({ delivered: 1 })
|
|
340
301
|
.where("id", "=", row.id)
|
|
341
302
|
.execute();
|
|
342
|
-
await trx
|
|
343
|
-
.
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
app_id: this.config.appId,
|
|
347
|
-
actor_id: actorId,
|
|
348
|
-
tenant_id: tenantId,
|
|
303
|
+
await this.ownership.recordEvent(trx, {
|
|
304
|
+
appId: this.config.appId,
|
|
305
|
+
actorId,
|
|
306
|
+
tenantId,
|
|
349
307
|
action: `identity.${kind}.issued`,
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
})
|
|
353
|
-
.execute();
|
|
308
|
+
resourceId: row.id
|
|
309
|
+
});
|
|
354
310
|
checkIdentityRequest();
|
|
355
311
|
});
|
|
356
312
|
return { ...row, delivered: 1 };
|
|
357
313
|
}
|
|
358
314
|
catch {
|
|
359
|
-
await this.db
|
|
360
|
-
.deleteFrom("identity_tokens")
|
|
361
|
-
.where("id", "=", row.id)
|
|
362
|
-
.execute();
|
|
315
|
+
await this.db.deleteFrom("identity_tokens").where("id", "=", row.id).execute();
|
|
363
316
|
throw new IdentityError(503, "Account email is unavailable. Contact your administrator.");
|
|
364
317
|
}
|
|
365
318
|
}
|
|
@@ -375,21 +328,16 @@ export class IdentityLifecycleService {
|
|
|
375
328
|
? "closed"
|
|
376
329
|
: row.expires_at <= new Date().toISOString()
|
|
377
330
|
? "expired"
|
|
378
|
-
: "pending"
|
|
331
|
+
: "pending"
|
|
379
332
|
};
|
|
380
333
|
}
|
|
381
334
|
async audit(actor, action, resourceId, tenantId = actor.tenant.id) {
|
|
382
|
-
await this.db
|
|
383
|
-
.
|
|
384
|
-
.
|
|
385
|
-
|
|
386
|
-
app_id: actor.appId,
|
|
387
|
-
actor_id: actor.user.id,
|
|
388
|
-
tenant_id: tenantId,
|
|
335
|
+
await this.ownership.recordEvent(this.db, {
|
|
336
|
+
appId: actor.appId,
|
|
337
|
+
actorId: actor.user.id,
|
|
338
|
+
tenantId,
|
|
389
339
|
action,
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
})
|
|
393
|
-
.execute();
|
|
340
|
+
resourceId
|
|
341
|
+
});
|
|
394
342
|
}
|
|
395
343
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -17,5 +17,5 @@ export function digest(value) {
|
|
|
17
17
|
return createHash("sha256").update(value).digest("hex");
|
|
18
18
|
}
|
|
19
19
|
function derive(password, salt) {
|
|
20
|
-
return new Promise((resolve, reject) => scrypt(password, salt, 64, options, (error, key) => error ? reject(error) : resolve(key)));
|
|
20
|
+
return new Promise((resolve, reject) => scrypt(password, salt, 64, options, (error, key) => (error ? reject(error) : resolve(key))));
|
|
21
21
|
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import type { Kysely, RawBuilder, Transaction } from "kysely";
|
|
2
|
+
import type { IdentityConfig, IdentityProviderOptions, IdentitySchema } from "../identity.types.js";
|
|
3
|
+
import type { IdentityPermissionProvider } from "../permission/index.js";
|
|
4
|
+
import { IdentityService } from "./user.service.js";
|
|
5
|
+
import { IdentityLifecycleService } from "./user.lifecycle.service.js";
|
|
6
|
+
import type { IdentityRoleResolver } from "../role/index.js";
|
|
7
|
+
import type { IdentityMutation } from "../identity.types.js";
|
|
8
|
+
import type { IdentityUserRoleProvider } from "../user-role/index.js";
|
|
9
|
+
import type { Portal } from "../identity.types.js";
|
|
10
|
+
export interface IdentityUserOwnership {
|
|
11
|
+
activeOrganization(db: Kysely<IdentitySchema>, id: string): Promise<unknown>;
|
|
12
|
+
assignInvitedMembership(trx: Transaction<IdentitySchema>, userId: string, tenantId: string, portal: Portal): Promise<void>;
|
|
13
|
+
revokeUserSessions(trx: Transaction<IdentitySchema>, userId: string): Promise<void>;
|
|
14
|
+
recordEvent(db: Kysely<IdentitySchema>, input: {
|
|
15
|
+
appId: string;
|
|
16
|
+
actorId: string;
|
|
17
|
+
tenantId: string;
|
|
18
|
+
action: string;
|
|
19
|
+
resourceId: string;
|
|
20
|
+
}): Promise<void>;
|
|
21
|
+
sessionSeconds(appId: string, fallback: number): Promise<number>;
|
|
22
|
+
activeSession(tokenHash: string, appId: string, portal: Portal, now: string): Promise<IdentitySchema["identity_sessions"] | undefined>;
|
|
23
|
+
createSession(session: IdentitySchema["identity_sessions"], previous: string | undefined, eligible: (trx: Transaction<IdentitySchema>) => Promise<boolean>): Promise<boolean>;
|
|
24
|
+
revokeSession(tokenHash: string, appId: string, portal: Portal): Promise<void>;
|
|
25
|
+
throttle(key: string): Promise<number>;
|
|
26
|
+
clearThrottle(key: string): Promise<void>;
|
|
27
|
+
activeMembership(db: Kysely<IdentitySchema>, userId: string, tenantId: string, portal: Portal): Promise<{
|
|
28
|
+
custom_role_id: string | null;
|
|
29
|
+
} | undefined>;
|
|
30
|
+
activeOrganizationDetail(db: Kysely<IdentitySchema>, id: string): Promise<{
|
|
31
|
+
id: string;
|
|
32
|
+
name: string;
|
|
33
|
+
} | undefined>;
|
|
34
|
+
permissionIds(db: Kysely<IdentitySchema>, portal: Portal, customRoleId: string | null, appId: string, tenantId: string): Promise<string[] | null>;
|
|
35
|
+
}
|
|
36
|
+
export declare function createIdentityUserProvider(database: Kysely<IdentitySchema>, config: IdentityConfig, permissions: IdentityPermissionProvider, ownership: IdentityUserOwnership, options?: IdentityProviderOptions): Readonly<{
|
|
37
|
+
verify: () => Promise<void>;
|
|
38
|
+
identity: IdentityService;
|
|
39
|
+
lifecycle: IdentityLifecycleService;
|
|
40
|
+
}>;
|
|
41
|
+
export type IdentityUserProvider = ReturnType<typeof createIdentityUserProvider>;
|
|
42
|
+
export declare function createIdentityUserAdministrationProvider(database: Kysely<IdentitySchema>, visibility: (actor: import("../identity.types.js").Principal, userId: RawBuilder<unknown>) => RawBuilder<unknown>, resolveRole: IdentityRoleResolver, userRoles: IdentityUserRoleProvider, mutate: IdentityMutation, activeOrganization: (trx: Transaction<IdentitySchema>, id: string) => Promise<unknown>, revokeUserSessions: (trx: Transaction<IdentitySchema>, userId: string) => Promise<void>): Readonly<{
|
|
43
|
+
list: (actor: import("../identity.types.js").Principal, query: import("../index.js").IdentityListQuery) => Promise<{
|
|
44
|
+
data: {
|
|
45
|
+
active: boolean;
|
|
46
|
+
version: number;
|
|
47
|
+
}[];
|
|
48
|
+
meta: {
|
|
49
|
+
page: number;
|
|
50
|
+
perPage: number;
|
|
51
|
+
total: number;
|
|
52
|
+
lastPage: number;
|
|
53
|
+
};
|
|
54
|
+
}>;
|
|
55
|
+
show: (actor: import("../identity.types.js").Principal, id: string) => Promise<{
|
|
56
|
+
active: boolean;
|
|
57
|
+
version: number;
|
|
58
|
+
} | undefined>;
|
|
59
|
+
create: (actor: import("../identity.types.js").Principal, raw: unknown) => Promise<{
|
|
60
|
+
id: string;
|
|
61
|
+
email: string;
|
|
62
|
+
name: string;
|
|
63
|
+
active: boolean;
|
|
64
|
+
version: number;
|
|
65
|
+
}>;
|
|
66
|
+
update: (actor: import("../identity.types.js").Principal, id: string, raw: unknown) => Promise<{
|
|
67
|
+
id: string;
|
|
68
|
+
name: string;
|
|
69
|
+
email: string;
|
|
70
|
+
active: boolean;
|
|
71
|
+
version: number;
|
|
72
|
+
}>;
|
|
73
|
+
profile: (actor: import("../identity.types.js").Principal, raw?: unknown) => Promise<{
|
|
74
|
+
active: boolean;
|
|
75
|
+
version: number;
|
|
76
|
+
email: string;
|
|
77
|
+
id: string;
|
|
78
|
+
name: string;
|
|
79
|
+
}>;
|
|
80
|
+
}>;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { IdentityUserRepository } from "./user.repository.js";
|
|
2
|
+
import { IdentityService } from "./user.service.js";
|
|
3
|
+
import { IdentityLifecycleService } from "./user.lifecycle.service.js";
|
|
4
|
+
import { IdentityUserAdministrationService } from "./user.administration.service.js";
|
|
5
|
+
export function createIdentityUserProvider(database, config, permissions, ownership, options = {}) {
|
|
6
|
+
const repository = new IdentityUserRepository(database, permissions, ownership);
|
|
7
|
+
return Object.freeze({
|
|
8
|
+
verify: repository.verifySchema.bind(repository),
|
|
9
|
+
identity: new IdentityService(repository, config),
|
|
10
|
+
lifecycle: new IdentityLifecycleService(database, config, options.delivery, permissions, ownership)
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
export function createIdentityUserAdministrationProvider(database, visibility, resolveRole, userRoles, mutate, activeOrganization, revokeUserSessions) {
|
|
14
|
+
const service = new IdentityUserAdministrationService(database, visibility, resolveRole, userRoles.assignInitialMembership, mutate, activeOrganization, userRoles.isManageableUser, userRoles.protectAdministrators, revokeUserSessions);
|
|
15
|
+
return Object.freeze({
|
|
16
|
+
list: service.list.bind(service),
|
|
17
|
+
show: service.show.bind(service),
|
|
18
|
+
create: service.create.bind(service),
|
|
19
|
+
update: service.update.bind(service),
|
|
20
|
+
profile: service.profile.bind(service)
|
|
21
|
+
});
|
|
22
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import type { IdentityPermissionProvider } from "../permission/index.js";
|
|
2
|
+
import { type Kysely, type RawBuilder } from "kysely";
|
|
3
|
+
import type { IdentitySchema, Portal } from "../identity.types.js";
|
|
4
|
+
import type { IdentityListQuery } from "../support/pagination.schema.js";
|
|
5
|
+
import type { IdentityUserOwnership } from "./user.provider.js";
|
|
6
|
+
export declare const userNameSource: () => RawBuilder<unknown>;
|
|
7
|
+
export declare function activeUserIds(db: Kysely<IdentitySchema>, ids: string[]): Promise<string[]>;
|
|
8
|
+
export declare function listUsers(db: Kysely<IdentitySchema>, query: IdentityListQuery, visibility: RawBuilder<unknown>): Promise<{
|
|
9
|
+
data: {
|
|
10
|
+
active: boolean;
|
|
11
|
+
version: number;
|
|
12
|
+
}[];
|
|
13
|
+
meta: {
|
|
14
|
+
page: number;
|
|
15
|
+
perPage: number;
|
|
16
|
+
total: number;
|
|
17
|
+
lastPage: number;
|
|
18
|
+
};
|
|
19
|
+
}>;
|
|
20
|
+
export declare function showUser(db: Kysely<IdentitySchema>, id: string, visibility: RawBuilder<unknown>): Promise<{
|
|
21
|
+
active: boolean;
|
|
22
|
+
version: number;
|
|
23
|
+
} | undefined>;
|
|
24
|
+
export declare class IdentityUserRepository {
|
|
25
|
+
private readonly db;
|
|
26
|
+
private readonly permissions;
|
|
27
|
+
private readonly ownership;
|
|
28
|
+
constructor(db: Kysely<IdentitySchema>, permissions: IdentityPermissionProvider, ownership: IdentityUserOwnership);
|
|
29
|
+
verifySchema(): Promise<void>;
|
|
30
|
+
user(email: string): Promise<{
|
|
31
|
+
active: number;
|
|
32
|
+
email: string;
|
|
33
|
+
id: string;
|
|
34
|
+
name: string;
|
|
35
|
+
password_hash: string;
|
|
36
|
+
version: number;
|
|
37
|
+
} | undefined>;
|
|
38
|
+
sessionSeconds(appId: string, fallback: number): Promise<number>;
|
|
39
|
+
principal(userId: string, tenantId: string, portal: Portal, appId: string): Promise<{
|
|
40
|
+
user: {
|
|
41
|
+
id: string;
|
|
42
|
+
email: string;
|
|
43
|
+
name: string;
|
|
44
|
+
};
|
|
45
|
+
tenant: {
|
|
46
|
+
id: string;
|
|
47
|
+
name: string;
|
|
48
|
+
};
|
|
49
|
+
permissions: string[];
|
|
50
|
+
} | null>;
|
|
51
|
+
session(tokenHash: string, appId: string, portal: Portal, now: string): Promise<{
|
|
52
|
+
token_hash: string;
|
|
53
|
+
user_id: string;
|
|
54
|
+
tenant_id: string;
|
|
55
|
+
app_id: string;
|
|
56
|
+
portal: Portal;
|
|
57
|
+
expires_at: string;
|
|
58
|
+
} | undefined>;
|
|
59
|
+
createSession(session: IdentitySchema["identity_sessions"], expectedHash: string, previous?: string): Promise<boolean>;
|
|
60
|
+
revoke(tokenHash: string, appId: string, portal: Portal): Promise<void>;
|
|
61
|
+
throttle(key: string): Promise<number>;
|
|
62
|
+
clearThrottle(key: string): Promise<void>;
|
|
63
|
+
changePassword(userId: string, expectedHash: string, passwordHash: string): Promise<boolean>;
|
|
64
|
+
}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { sql } from "kysely";
|
|
2
|
+
import { checkIdentityRequest } from "../support/identity.request-context.js";
|
|
3
|
+
import { listRows, showRow } from "../support/pagination.js";
|
|
4
|
+
const presentUser = (row) => ({
|
|
5
|
+
...row,
|
|
6
|
+
active: Boolean(row.active),
|
|
7
|
+
version: Number(row.version)
|
|
8
|
+
});
|
|
9
|
+
export const userNameSource = () => sql `select id,name,active from identity_users`;
|
|
10
|
+
export async function activeUserIds(db, ids) {
|
|
11
|
+
if (ids.length === 0)
|
|
12
|
+
return [];
|
|
13
|
+
const rows = await db
|
|
14
|
+
.selectFrom("identity_users")
|
|
15
|
+
.select("id")
|
|
16
|
+
.where("id", "in", ids)
|
|
17
|
+
.where("active", "=", 1)
|
|
18
|
+
.execute();
|
|
19
|
+
return rows.map((row) => row.id);
|
|
20
|
+
}
|
|
21
|
+
export function listUsers(db, query, visibility) {
|
|
22
|
+
return listRows(db, userListSource(visibility), ["id", "name", "email"], query, presentUser);
|
|
23
|
+
}
|
|
24
|
+
export function showUser(db, id, visibility) {
|
|
25
|
+
return showRow(db, userListSource(visibility), id, presentUser);
|
|
26
|
+
}
|
|
27
|
+
function userListSource(visibility) {
|
|
28
|
+
return sql `select u.id,u.name,u.email,u.active,u.version from identity_users u
|
|
29
|
+
where ${visibility}`;
|
|
30
|
+
}
|
|
31
|
+
export class IdentityUserRepository {
|
|
32
|
+
db;
|
|
33
|
+
permissions;
|
|
34
|
+
ownership;
|
|
35
|
+
constructor(db, permissions, ownership) {
|
|
36
|
+
this.db = db;
|
|
37
|
+
this.permissions = permissions;
|
|
38
|
+
this.ownership = ownership;
|
|
39
|
+
}
|
|
40
|
+
async verifySchema() {
|
|
41
|
+
await this.db.selectFrom("identity_users").select("version").limit(1).execute();
|
|
42
|
+
await this.db
|
|
43
|
+
.selectFrom("identity_tokens")
|
|
44
|
+
.select(["delivered", "consumed_at"])
|
|
45
|
+
.limit(1)
|
|
46
|
+
.execute();
|
|
47
|
+
}
|
|
48
|
+
user(email) {
|
|
49
|
+
return this.db
|
|
50
|
+
.selectFrom("identity_users")
|
|
51
|
+
.selectAll()
|
|
52
|
+
.where("email", "=", email)
|
|
53
|
+
.executeTakeFirst();
|
|
54
|
+
}
|
|
55
|
+
async sessionSeconds(appId, fallback) {
|
|
56
|
+
return this.ownership.sessionSeconds(appId, fallback);
|
|
57
|
+
}
|
|
58
|
+
async principal(userId, tenantId, portal, appId) {
|
|
59
|
+
const user = await this.db
|
|
60
|
+
.selectFrom("identity_users")
|
|
61
|
+
.select(["id", "email", "name"])
|
|
62
|
+
.where("id", "=", userId)
|
|
63
|
+
.where("active", "=", 1)
|
|
64
|
+
.executeTakeFirst();
|
|
65
|
+
if (!user)
|
|
66
|
+
return null;
|
|
67
|
+
const membership = await this.ownership.activeMembership(this.db, userId, tenantId, portal);
|
|
68
|
+
if (!membership)
|
|
69
|
+
return null;
|
|
70
|
+
const tenant = await this.ownership.activeOrganizationDetail(this.db, tenantId);
|
|
71
|
+
if (!tenant)
|
|
72
|
+
return null;
|
|
73
|
+
const ids = await this.ownership.permissionIds(this.db, portal, membership.custom_role_id, appId, tenantId);
|
|
74
|
+
if (!ids)
|
|
75
|
+
return null;
|
|
76
|
+
return {
|
|
77
|
+
user: { id: user.id, email: user.email, name: user.name },
|
|
78
|
+
tenant,
|
|
79
|
+
permissions: await this.permissions.filterDeclared(this.db, appId, portal, ids)
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
session(tokenHash, appId, portal, now) {
|
|
83
|
+
return this.ownership.activeSession(tokenHash, appId, portal, now);
|
|
84
|
+
}
|
|
85
|
+
createSession(session, expectedHash, previous) {
|
|
86
|
+
return this.ownership.createSession(session, previous, async (trx) => {
|
|
87
|
+
const current = await trx
|
|
88
|
+
.selectFrom("identity_users")
|
|
89
|
+
.select("id")
|
|
90
|
+
.where("id", "=", session.user_id)
|
|
91
|
+
.where("password_hash", "=", expectedHash)
|
|
92
|
+
.where("active", "=", 1)
|
|
93
|
+
.executeTakeFirst();
|
|
94
|
+
if (!current)
|
|
95
|
+
return false;
|
|
96
|
+
const membership = await this.ownership.activeMembership(trx, session.user_id, session.tenant_id, session.portal);
|
|
97
|
+
if (!membership)
|
|
98
|
+
return false;
|
|
99
|
+
if (!(await this.ownership.activeOrganizationDetail(trx, session.tenant_id)))
|
|
100
|
+
return false;
|
|
101
|
+
const permissions = await this.ownership.permissionIds(trx, session.portal, membership.custom_role_id, session.app_id, session.tenant_id);
|
|
102
|
+
return permissions !== null;
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
revoke(tokenHash, appId, portal) {
|
|
106
|
+
return this.ownership.revokeSession(tokenHash, appId, portal);
|
|
107
|
+
}
|
|
108
|
+
throttle(key) {
|
|
109
|
+
return this.ownership.throttle(key);
|
|
110
|
+
}
|
|
111
|
+
clearThrottle(key) {
|
|
112
|
+
return this.ownership.clearThrottle(key);
|
|
113
|
+
}
|
|
114
|
+
async changePassword(userId, expectedHash, passwordHash) {
|
|
115
|
+
return this.db.transaction().execute(async (trx) => {
|
|
116
|
+
checkIdentityRequest();
|
|
117
|
+
const result = await trx
|
|
118
|
+
.updateTable("identity_users")
|
|
119
|
+
.set({ password_hash: passwordHash })
|
|
120
|
+
.where("id", "=", userId)
|
|
121
|
+
.where("password_hash", "=", expectedHash)
|
|
122
|
+
.executeTakeFirst();
|
|
123
|
+
if (result.numUpdatedRows !== 1n)
|
|
124
|
+
return false;
|
|
125
|
+
await this.ownership.revokeUserSessions(trx, userId);
|
|
126
|
+
return true;
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
}
|