@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.
Files changed (280) hide show
  1. package/README.md +7 -1
  2. package/dist/index.d.ts +6 -4
  3. package/dist/index.js +4 -2
  4. package/dist/modules/identity/audit/audit.controller.d.ts +17 -0
  5. package/dist/modules/identity/audit/audit.controller.js +12 -0
  6. package/dist/modules/identity/audit/audit.migration.d.ts +1 -0
  7. package/dist/modules/identity/audit/audit.migration.js +1 -0
  8. package/dist/modules/identity/audit/audit.provider.d.ts +24 -0
  9. package/dist/modules/identity/audit/audit.provider.js +14 -0
  10. package/dist/modules/identity/audit/audit.repository.d.ts +26 -0
  11. package/dist/modules/identity/audit/audit.repository.js +46 -0
  12. package/dist/modules/identity/audit/audit.routes.d.ts +5 -0
  13. package/dist/modules/identity/audit/audit.routes.js +2 -0
  14. package/dist/modules/identity/audit/audit.schema.d.ts +1 -0
  15. package/dist/modules/identity/audit/audit.schema.js +1 -0
  16. package/dist/modules/identity/audit/audit.seed.d.ts +1 -0
  17. package/dist/modules/identity/audit/audit.seed.js +1 -0
  18. package/dist/modules/identity/audit/audit.service.d.ts +23 -0
  19. package/dist/modules/identity/audit/audit.service.js +46 -0
  20. package/dist/modules/identity/audit/audit.types.d.ts +12 -0
  21. package/dist/modules/identity/audit/audit.types.js +1 -0
  22. package/dist/modules/identity/audit/index.d.ts +4 -0
  23. package/dist/modules/identity/audit/index.js +3 -0
  24. package/dist/modules/identity/{identity.administration-service.d.ts → composition/identity.composition.d.ts} +59 -27
  25. package/dist/modules/identity/composition/identity.composition.js +171 -0
  26. package/dist/modules/identity/composition/identity.seed.d.ts +3 -0
  27. package/dist/modules/identity/composition/identity.seed.js +16 -0
  28. package/dist/modules/identity/identity.controller.d.ts +8 -6
  29. package/dist/modules/identity/identity.controller.js +90 -70
  30. package/dist/modules/identity/identity.migration.d.ts +1 -2
  31. package/dist/modules/identity/identity.migration.js +1 -79
  32. package/dist/modules/identity/identity.provider.d.ts +39 -13
  33. package/dist/modules/identity/identity.provider.js +19 -73
  34. package/dist/modules/identity/identity.public-schemas.d.ts +9 -3
  35. package/dist/modules/identity/identity.public-schemas.js +9 -3
  36. package/dist/modules/identity/identity.repository.d.ts +1 -40
  37. package/dist/modules/identity/identity.repository.js +1 -196
  38. package/dist/modules/identity/identity.routes.d.ts +2 -5
  39. package/dist/modules/identity/identity.routes.js +3 -11
  40. package/dist/modules/identity/identity.schema.d.ts +1 -27
  41. package/dist/modules/identity/identity.schema.js +1 -34
  42. package/dist/modules/identity/identity.seed.d.ts +1 -3
  43. package/dist/modules/identity/identity.seed.js +1 -81
  44. package/dist/modules/identity/identity.service.d.ts +1 -32
  45. package/dist/modules/identity/identity.service.js +1 -83
  46. package/dist/modules/identity/identity.types.d.ts +13 -106
  47. package/dist/modules/identity/index.d.ts +18 -11
  48. package/dist/modules/identity/index.js +14 -8
  49. package/dist/modules/identity/{identity.administration-migration.js → legacy/identity.administration-migration.js} +3 -11
  50. package/dist/modules/identity/legacy/identity.migration.d.ts +2 -0
  51. package/dist/modules/identity/legacy/identity.migration.js +72 -0
  52. package/dist/modules/identity/organization/index.d.ts +6 -0
  53. package/dist/modules/identity/organization/index.js +5 -0
  54. package/dist/modules/identity/organization/organization.controller.d.ts +35 -0
  55. package/dist/modules/identity/organization/organization.controller.js +18 -0
  56. package/dist/modules/identity/organization/organization.migration.d.ts +1 -0
  57. package/dist/modules/identity/organization/organization.migration.js +1 -0
  58. package/dist/modules/identity/organization/organization.provider.d.ts +46 -0
  59. package/dist/modules/identity/organization/organization.provider.js +33 -0
  60. package/dist/modules/identity/organization/organization.repository.d.ts +58 -0
  61. package/dist/modules/identity/organization/organization.repository.js +101 -0
  62. package/dist/modules/identity/organization/organization.routes.d.ts +5 -0
  63. package/dist/modules/identity/organization/organization.routes.js +2 -0
  64. package/dist/modules/identity/organization/organization.schema.d.ts +11 -0
  65. package/dist/modules/identity/organization/organization.schema.js +18 -0
  66. package/dist/modules/identity/organization/organization.seed.d.ts +3 -0
  67. package/dist/modules/identity/organization/organization.seed.js +11 -0
  68. package/dist/modules/identity/organization/organization.service.d.ts +39 -0
  69. package/dist/modules/identity/organization/organization.service.js +55 -0
  70. package/dist/modules/identity/organization/organization.types.d.ts +19 -0
  71. package/dist/modules/identity/organization/organization.types.js +1 -0
  72. package/dist/modules/identity/permission/index.d.ts +9 -0
  73. package/dist/modules/identity/permission/index.js +6 -0
  74. package/dist/modules/identity/permission/permission.controller.d.ts +21 -0
  75. package/dist/modules/identity/permission/permission.controller.js +12 -0
  76. package/dist/modules/identity/{identity.permission-declarations-migration.js → permission/permission.declarations.migration.js} +9 -4
  77. package/dist/modules/identity/{identity.permission-labels-migration.js → permission/permission.labels.migration.js} +5 -2
  78. package/dist/modules/identity/permission/permission.migration.d.ts +2 -0
  79. package/dist/modules/identity/permission/permission.migration.js +2 -0
  80. package/dist/modules/identity/permission/permission.provider.d.ts +19 -0
  81. package/dist/modules/identity/permission/permission.provider.js +14 -0
  82. package/dist/modules/identity/permission/permission.repository.d.ts +51 -0
  83. package/dist/modules/identity/permission/permission.repository.js +84 -0
  84. package/dist/modules/identity/permission/permission.routes.d.ts +5 -0
  85. package/dist/modules/identity/permission/permission.routes.js +2 -0
  86. package/dist/modules/identity/permission/permission.schema.d.ts +14 -0
  87. package/dist/modules/identity/permission/permission.schema.js +29 -0
  88. package/dist/modules/identity/permission/permission.seed.d.ts +4 -0
  89. package/dist/modules/identity/permission/permission.seed.js +17 -0
  90. package/dist/modules/identity/permission/permission.service.d.ts +31 -0
  91. package/dist/modules/identity/permission/permission.service.js +77 -0
  92. package/dist/modules/identity/permission/permission.types.d.ts +15 -0
  93. package/dist/modules/identity/permission/permission.types.js +1 -0
  94. package/dist/modules/identity/role/index.d.ts +10 -0
  95. package/dist/modules/identity/role/index.js +6 -0
  96. package/dist/modules/identity/role/role.controller.d.ts +42 -0
  97. package/dist/modules/identity/role/role.controller.js +25 -0
  98. package/dist/modules/identity/{identity.roles-migration.js → role/role.migration.js} +5 -18
  99. package/dist/modules/identity/role/role.provider.d.ts +14 -0
  100. package/dist/modules/identity/role/role.provider.js +30 -0
  101. package/dist/modules/identity/role/role.repository.d.ts +38 -0
  102. package/dist/modules/identity/role/role.repository.js +46 -0
  103. package/dist/modules/identity/role/role.routes.d.ts +5 -0
  104. package/dist/modules/identity/role/role.routes.js +2 -0
  105. package/dist/modules/identity/role/role.schema.d.ts +16 -0
  106. package/dist/modules/identity/role/role.schema.js +27 -0
  107. package/dist/modules/identity/role/role.seed.d.ts +3 -0
  108. package/dist/modules/identity/role/role.seed.js +8 -0
  109. package/dist/modules/identity/role/role.service.d.ts +69 -0
  110. package/dist/modules/identity/role/role.service.js +192 -0
  111. package/dist/modules/identity/role/role.types.d.ts +17 -0
  112. package/dist/modules/identity/role/role.types.js +1 -0
  113. package/dist/modules/identity/role-permission/index.d.ts +6 -0
  114. package/dist/modules/identity/role-permission/index.js +4 -0
  115. package/dist/modules/identity/role-permission/role-permission.controller.d.ts +1 -0
  116. package/dist/modules/identity/role-permission/role-permission.controller.js +1 -0
  117. package/dist/modules/identity/role-permission/role-permission.migration.d.ts +1 -0
  118. package/dist/modules/identity/role-permission/role-permission.migration.js +1 -0
  119. package/dist/modules/identity/role-permission/role-permission.provider.d.ts +14 -0
  120. package/dist/modules/identity/role-permission/role-permission.provider.js +15 -0
  121. package/dist/modules/identity/role-permission/role-permission.repository.d.ts +12 -0
  122. package/dist/modules/identity/role-permission/role-permission.repository.js +51 -0
  123. package/dist/modules/identity/role-permission/role-permission.routes.d.ts +1 -0
  124. package/dist/modules/identity/role-permission/role-permission.routes.js +2 -0
  125. package/dist/modules/identity/role-permission/role-permission.schema.d.ts +2 -0
  126. package/dist/modules/identity/role-permission/role-permission.schema.js +3 -0
  127. package/dist/modules/identity/role-permission/role-permission.seed.d.ts +3 -0
  128. package/dist/modules/identity/role-permission/role-permission.seed.js +10 -0
  129. package/dist/modules/identity/role-permission/role-permission.service.d.ts +13 -0
  130. package/dist/modules/identity/role-permission/role-permission.service.js +35 -0
  131. package/dist/modules/identity/role-permission/role-permission.types.d.ts +12 -0
  132. package/dist/modules/identity/role-permission/role-permission.types.js +1 -0
  133. package/dist/modules/identity/session/index.d.ts +6 -0
  134. package/dist/modules/identity/session/index.js +5 -0
  135. package/dist/modules/identity/session/session.authentication.controller.d.ts +30 -0
  136. package/dist/modules/identity/session/session.authentication.controller.js +37 -0
  137. package/dist/modules/identity/session/session.controller.d.ts +18 -0
  138. package/dist/modules/identity/session/session.controller.js +15 -0
  139. package/dist/modules/identity/session/session.migration.d.ts +1 -0
  140. package/dist/modules/identity/session/session.migration.js +1 -0
  141. package/dist/modules/identity/session/session.provider.d.ts +34 -0
  142. package/dist/modules/identity/session/session.provider.js +23 -0
  143. package/dist/modules/identity/session/session.repository.d.ts +36 -0
  144. package/dist/modules/identity/session/session.repository.js +128 -0
  145. package/dist/modules/identity/session/session.routes.d.ts +27 -0
  146. package/dist/modules/identity/session/session.routes.js +13 -0
  147. package/dist/modules/identity/session/session.schema.d.ts +6 -0
  148. package/dist/modules/identity/session/session.schema.js +12 -0
  149. package/dist/modules/identity/session/session.seed.d.ts +1 -0
  150. package/dist/modules/identity/session/session.seed.js +1 -0
  151. package/dist/modules/identity/session/session.service.d.ts +20 -0
  152. package/dist/modules/identity/session/session.service.js +27 -0
  153. package/dist/modules/identity/session/session.types.d.ts +17 -0
  154. package/dist/modules/identity/session/session.types.js +1 -0
  155. package/dist/modules/identity/settings/index.d.ts +5 -0
  156. package/dist/modules/identity/settings/index.js +4 -0
  157. package/dist/modules/identity/settings/settings.controller.d.ts +35 -0
  158. package/dist/modules/identity/settings/settings.controller.js +18 -0
  159. package/dist/modules/identity/settings/settings.migration.d.ts +1 -0
  160. package/dist/modules/identity/settings/settings.migration.js +1 -0
  161. package/dist/modules/identity/settings/settings.provider.d.ts +35 -0
  162. package/dist/modules/identity/settings/settings.provider.js +14 -0
  163. package/dist/modules/identity/settings/settings.repository.d.ts +49 -0
  164. package/dist/modules/identity/settings/settings.repository.js +75 -0
  165. package/dist/modules/identity/settings/settings.routes.d.ts +1 -0
  166. package/dist/modules/identity/settings/settings.routes.js +7 -0
  167. package/dist/modules/identity/settings/settings.schema.d.ts +15 -0
  168. package/dist/modules/identity/settings/settings.schema.js +27 -0
  169. package/dist/modules/identity/settings/settings.seed.d.ts +1 -0
  170. package/dist/modules/identity/settings/settings.seed.js +1 -0
  171. package/dist/modules/identity/settings/settings.service.d.ts +40 -0
  172. package/dist/modules/identity/settings/settings.service.js +101 -0
  173. package/dist/modules/identity/settings/settings.types.d.ts +20 -0
  174. package/dist/modules/identity/settings/settings.types.js +1 -0
  175. package/dist/modules/identity/support/identity.schema.d.ts +8 -0
  176. package/dist/modules/identity/support/identity.schema.js +10 -0
  177. package/dist/modules/identity/support/pagination.d.ts +13 -0
  178. package/dist/modules/identity/support/pagination.js +27 -0
  179. package/dist/modules/identity/support/pagination.schema.d.ts +43 -0
  180. package/dist/modules/identity/support/pagination.schema.js +15 -0
  181. package/dist/modules/identity/support/resource-controller.d.ts +14 -0
  182. package/dist/modules/identity/support/resource-controller.js +1 -0
  183. package/dist/modules/identity/user/index.d.ts +13 -0
  184. package/dist/modules/identity/user/index.js +11 -0
  185. package/dist/modules/identity/user/user.administration.service.d.ts +59 -0
  186. package/dist/modules/identity/user/user.administration.service.js +159 -0
  187. package/dist/modules/identity/user/user.controller.d.ts +44 -0
  188. package/dist/modules/identity/user/user.controller.js +21 -0
  189. package/dist/modules/identity/user/user.lifecycle.controller.d.ts +58 -0
  190. package/dist/modules/identity/user/user.lifecycle.controller.js +27 -0
  191. package/dist/modules/identity/{identity.lifecycle-schema.js → user/user.lifecycle.schema.js} +5 -5
  192. package/dist/modules/identity/{identity.lifecycle-service.d.ts → user/user.lifecycle.service.d.ts} +8 -4
  193. package/dist/modules/identity/{identity.lifecycle-service.js → user/user.lifecycle.service.js} +66 -118
  194. package/dist/modules/identity/user/user.migration.d.ts +1 -0
  195. package/dist/modules/identity/user/user.migration.js +1 -0
  196. package/dist/modules/identity/{identity.password.js → user/user.password.js} +1 -1
  197. package/dist/modules/identity/user/user.provider.d.ts +80 -0
  198. package/dist/modules/identity/user/user.provider.js +22 -0
  199. package/dist/modules/identity/user/user.repository.d.ts +64 -0
  200. package/dist/modules/identity/user/user.repository.js +129 -0
  201. package/dist/modules/identity/user/user.routes.d.ts +12 -0
  202. package/dist/modules/identity/user/user.routes.js +9 -0
  203. package/dist/modules/identity/user/user.schema.d.ts +34 -0
  204. package/dist/modules/identity/user/user.schema.js +46 -0
  205. package/dist/modules/identity/user/user.seed.d.ts +14 -0
  206. package/dist/modules/identity/user/user.seed.js +47 -0
  207. package/dist/modules/identity/user/user.service.d.ts +33 -0
  208. package/dist/modules/identity/user/user.service.js +76 -0
  209. package/dist/modules/identity/user/user.types.d.ts +27 -0
  210. package/dist/modules/identity/user/user.types.js +1 -0
  211. package/dist/modules/identity/user-role/index.d.ts +8 -0
  212. package/dist/modules/identity/user-role/index.js +6 -0
  213. package/dist/modules/identity/user-role/user-role.controller.d.ts +42 -0
  214. package/dist/modules/identity/user-role/user-role.controller.js +21 -0
  215. package/dist/modules/identity/user-role/user-role.migration.d.ts +1 -0
  216. package/dist/modules/identity/user-role/user-role.migration.js +1 -0
  217. package/dist/modules/identity/user-role/user-role.policy.d.ts +3 -0
  218. package/dist/modules/identity/user-role/user-role.policy.js +25 -0
  219. package/dist/modules/identity/user-role/user-role.provider.d.ts +18 -0
  220. package/dist/modules/identity/user-role/user-role.provider.js +23 -0
  221. package/dist/modules/identity/user-role/user-role.repository.d.ts +34 -0
  222. package/dist/modules/identity/user-role/user-role.repository.js +87 -0
  223. package/dist/modules/identity/user-role/user-role.routes.d.ts +5 -0
  224. package/dist/modules/identity/user-role/user-role.routes.js +2 -0
  225. package/dist/modules/identity/user-role/user-role.schema.d.ts +14 -0
  226. package/dist/modules/identity/user-role/user-role.schema.js +25 -0
  227. package/dist/modules/identity/user-role/user-role.seed.d.ts +7 -0
  228. package/dist/modules/identity/user-role/user-role.seed.js +11 -0
  229. package/dist/modules/identity/user-role/user-role.service.d.ts +60 -0
  230. package/dist/modules/identity/user-role/user-role.service.js +216 -0
  231. package/dist/modules/identity/user-role/user-role.types.d.ts +12 -0
  232. package/dist/modules/identity/user-role/user-role.types.js +1 -0
  233. package/dist/modules/tenant/index.d.ts +6 -0
  234. package/dist/modules/tenant/index.js +5 -0
  235. package/dist/modules/tenant/tenant.command.d.ts +3 -0
  236. package/dist/modules/tenant/tenant.command.js +43 -0
  237. package/dist/modules/tenant/tenant.controller.d.ts +4 -0
  238. package/dist/modules/tenant/tenant.controller.js +12 -0
  239. package/dist/modules/tenant/tenant.migration.d.ts +5 -0
  240. package/dist/modules/tenant/tenant.migration.js +46 -0
  241. package/dist/modules/tenant/tenant.provider.d.ts +3 -0
  242. package/dist/modules/tenant/tenant.provider.js +44 -0
  243. package/dist/modules/tenant/tenant.readiness.d.ts +6 -0
  244. package/dist/modules/tenant/tenant.readiness.js +45 -0
  245. package/dist/modules/tenant/tenant.repository.d.ts +15 -0
  246. package/dist/modules/tenant/tenant.repository.js +23 -0
  247. package/dist/modules/tenant/tenant.routes.d.ts +4 -0
  248. package/dist/modules/tenant/tenant.routes.js +7 -0
  249. package/dist/modules/tenant/tenant.schema.d.ts +20 -0
  250. package/dist/modules/tenant/tenant.schema.js +31 -0
  251. package/dist/modules/tenant/tenant.seed.d.ts +8 -0
  252. package/dist/modules/tenant/tenant.seed.js +40 -0
  253. package/dist/modules/tenant/tenant.service.d.ts +16 -0
  254. package/dist/modules/tenant/tenant.service.js +72 -0
  255. package/dist/modules/tenant/tenant.types.d.ts +39 -0
  256. package/dist/modules/tenant/tenant.types.js +1 -0
  257. package/package.json +9 -5
  258. package/dist/modules/identity/identity.administration-repository.d.ts +0 -19
  259. package/dist/modules/identity/identity.administration-repository.js +0 -102
  260. package/dist/modules/identity/identity.administration-schema.d.ts +0 -113
  261. package/dist/modules/identity/identity.administration-schema.js +0 -121
  262. package/dist/modules/identity/identity.administration-service.js +0 -517
  263. package/dist/modules/identity/identity.membership-policy.d.ts +0 -3
  264. package/dist/modules/identity/identity.membership-policy.js +0 -27
  265. package/dist/modules/identity/identity.permission-declarations.d.ts +0 -23
  266. package/dist/modules/identity/identity.permission-declarations.js +0 -57
  267. package/dist/modules/identity/identity.presentation-service.d.ts +0 -23
  268. package/dist/modules/identity/identity.presentation-service.js +0 -51
  269. package/dist/modules/identity/identity.roles-service.d.ts +0 -49
  270. package/dist/modules/identity/identity.roles-service.js +0 -327
  271. /package/dist/modules/identity/{identity.administration-migration.d.ts → legacy/identity.administration-migration.d.ts} +0 -0
  272. /package/dist/modules/identity/{identity.permission-declarations-migration.d.ts → permission/permission.declarations.migration.d.ts} +0 -0
  273. /package/dist/modules/identity/{identity.permission-labels-migration.d.ts → permission/permission.labels.migration.d.ts} +0 -0
  274. /package/dist/modules/identity/{identity.roles-migration.d.ts → role/role.migration.d.ts} +0 -0
  275. /package/dist/modules/identity/{identity.error.d.ts → support/identity.error.d.ts} +0 -0
  276. /package/dist/modules/identity/{identity.error.js → support/identity.error.js} +0 -0
  277. /package/dist/modules/identity/{identity.request-context.d.ts → support/identity.request-context.d.ts} +0 -0
  278. /package/dist/modules/identity/{identity.request-context.js → support/identity.request-context.js} +0 -0
  279. /package/dist/modules/identity/{identity.lifecycle-schema.d.ts → user/user.lifecycle.schema.d.ts} +0 -0
  280. /package/dist/modules/identity/{identity.password.d.ts → user/user.password.d.ts} +0 -0
@@ -1,36 +1,33 @@
1
1
  import { randomBytes, randomUUID } from "node:crypto";
2
- import { digest, hashPassword } from "./identity.password.js";
3
- import { IdentityError } from "./identity.error.js";
4
- import { IdentityRepository } from "./identity.repository.js";
5
- import { checkIdentityRequest } from "./identity.request-context.js";
6
- import { invitationSchema, recoverySchema, tokenCompletionSchema, } from "./identity.lifecycle-schema.js";
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
- constructor(db, config, delivery) {
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 IdentityRepository(this.db).throttle(digest(`${actor.appId}:invitation:${actor.user.id}`))) > 20)
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), { path: ["tenantId"], message: "Enter an organization ID." })
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 IdentityRepository(this.db);
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 IdentityRepository(this.db);
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 IdentityRepository(trx).principal(userId, token.tenant_id, portal, this.config.appId);
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
- .insertInto("identity_audit_events")
180
- .values({
181
- id: randomUUID(),
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
- resource_id: token.id,
187
- created_at: new Date().toISOString(),
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
- .insertInto("identity_audit_events")
288
- .values({
289
- id: randomUUID(),
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
- resource_id: id,
295
- created_at: new Date().toISOString(),
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
- ? "Account invitation"
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
- .insertInto("identity_audit_events")
344
- .values({
345
- id: randomUUID(),
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
- resource_id: row.id,
351
- created_at: new Date().toISOString(),
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
- .insertInto("identity_audit_events")
384
- .values({
385
- id: randomUUID(),
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
- resource_id: resourceId,
391
- created_at: new Date().toISOString(),
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
+ }