@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,517 +0,0 @@
1
- import { validateDeclaredPermissions } from "./identity.permission-declarations.js";
2
- import { randomUUID } from "node:crypto";
3
- import { IdentityAdministrationRepository } from "./identity.administration-repository.js";
4
- import { IdentityRolesService } from "./identity.roles-service.js";
5
- import { protectAdministrators } from "./identity.membership-policy.js";
6
- import { IdentityPresentationService } from "./identity.presentation-service.js";
7
- import { checkIdentityRequest } from "./identity.request-context.js";
8
- import { sql } from "kysely";
9
- import { hashPassword } from "./identity.password.js";
10
- import { IdentityError } from "./identity.error.js";
11
- import { userCreateSchema, userUpdateSchema, organizationCreateSchema, organizationUpdateSchema, membershipSchema, roleUpdateSchema, profileSchema, settingsSchema, securitySettingsSchema, } from "./identity.administration-schema.js";
12
- export class IdentityAdministrationService {
13
- db;
14
- appName;
15
- repository;
16
- roles;
17
- presentationService;
18
- constructor(db, appName) {
19
- this.db = db;
20
- this.appName = appName;
21
- this.repository = new IdentityAdministrationRepository(db);
22
- this.roles = new IdentityRolesService(db);
23
- this.presentationService = new IdentityPresentationService(db, appName);
24
- }
25
- async list(actor, resource, query) {
26
- this.allow(actor, resource, false);
27
- return this.repository.list(actor, resource, query);
28
- }
29
- async show(actor, resource, id) {
30
- this.allow(actor, resource, false);
31
- const row = await this.repository.show(actor, resource, id);
32
- if (!row)
33
- throw new IdentityError(404, "Resource not found.");
34
- return row;
35
- }
36
- async create(actor, resource, raw) {
37
- if (resource === "roles")
38
- return this.roles.create(actor, raw);
39
- this.allow(actor, resource, true);
40
- if (resource === "users") {
41
- const input = userCreateSchema.parse(raw);
42
- const tenantId = this.tenant(actor, input.tenantId);
43
- await this.roles.resolve(this.db, actor, input.roleId, tenantId);
44
- const passwordHash = await hashPassword(input.password);
45
- return this.mutate(actor, resource, randomUUID(), async (trx, id) => {
46
- await this.activeTenant(trx, tenantId);
47
- const selected = await this.roles.resolve(trx, actor, input.roleId, tenantId);
48
- if (await trx
49
- .selectFrom("identity_users")
50
- .select("id")
51
- .where("email", "=", input.email)
52
- .executeTakeFirst())
53
- throw new IdentityError(409, "Email already exists.");
54
- await trx
55
- .insertInto("identity_users")
56
- .values({
57
- id,
58
- email: input.email,
59
- name: input.name,
60
- active: 1,
61
- password_hash: passwordHash,
62
- })
63
- .execute();
64
- await trx
65
- .insertInto("identity_memberships")
66
- .values({
67
- user_id: id,
68
- tenant_id: tenantId,
69
- role_id: selected.portal,
70
- custom_role_id: selected.customRoleId,
71
- })
72
- .execute();
73
- return {
74
- id,
75
- email: input.email,
76
- name: input.name,
77
- active: true,
78
- version: 0,
79
- };
80
- }, tenantId);
81
- }
82
- if (resource === "organizations") {
83
- const input = organizationCreateSchema.parse(raw);
84
- return this.mutate(actor, resource, input.id, async (trx) => {
85
- if (await trx
86
- .selectFrom("identity_tenants")
87
- .select("id")
88
- .where("id", "=", input.id)
89
- .executeTakeFirst())
90
- throw new IdentityError(409, "Organization already exists.");
91
- await trx
92
- .insertInto("identity_tenants")
93
- .values({
94
- id: input.id,
95
- name: input.name,
96
- active: Number(input.active),
97
- })
98
- .execute();
99
- return { ...input, version: 0 };
100
- });
101
- }
102
- if (resource === "memberships") {
103
- const input = membershipSchema.parse(raw);
104
- const tenantId = this.tenant(actor, input.tenantId);
105
- const selected = await this.roles.resolve(this.db, actor, input.roleId, tenantId);
106
- await this.show(actor, "users", input.userId);
107
- const id = `${input.userId}~${tenantId}~${selected.portal}`;
108
- return this.mutate(actor, resource, id, async (trx) => {
109
- await this.activeTenant(trx, tenantId);
110
- const currentRole = await this.roles.resolve(trx, actor, input.roleId, tenantId);
111
- if (await trx
112
- .selectFrom("identity_memberships")
113
- .select("user_id")
114
- .where("user_id", "=", input.userId)
115
- .where("tenant_id", "=", tenantId)
116
- .where("role_id", "=", currentRole.portal)
117
- .executeTakeFirst())
118
- throw new IdentityError(409, "Membership already exists.");
119
- await trx
120
- .insertInto("identity_memberships")
121
- .values({
122
- user_id: input.userId,
123
- tenant_id: tenantId,
124
- role_id: currentRole.portal,
125
- custom_role_id: currentRole.customRoleId,
126
- })
127
- .execute();
128
- return {
129
- id,
130
- userId: input.userId,
131
- tenantId,
132
- roleId: input.roleId,
133
- portal: currentRole.portal,
134
- active: true,
135
- version: 0,
136
- };
137
- });
138
- }
139
- throw new IdentityError(405, "Creation is not supported for this resource.");
140
- }
141
- async update(actor, resource, id, raw) {
142
- if (resource === "memberships")
143
- return this.roles.updateMembership(actor, id, raw);
144
- if (resource === "roles" && !["user", "admin", "super-admin"].includes(id))
145
- return this.roles.update(actor, id, raw);
146
- this.allow(actor, resource, true);
147
- await this.show(actor, resource, id);
148
- if (resource === "users") {
149
- const input = userUpdateSchema.parse(raw);
150
- return this.mutate(actor, resource, id, async (trx) => {
151
- const current = await trx
152
- .selectFrom("identity_users")
153
- .selectAll()
154
- .where("id", "=", id)
155
- .executeTakeFirstOrThrow();
156
- this.revision(current.version, input.expectedVersion);
157
- if (actor.portal !== "super-admin")
158
- await this.unprivilegedUser(trx, id);
159
- if (input.active === false) {
160
- await protectAdministrators(trx, id);
161
- await trx
162
- .deleteFrom("identity_sessions")
163
- .where("user_id", "=", id)
164
- .execute();
165
- }
166
- if (input.email &&
167
- (await trx
168
- .selectFrom("identity_users")
169
- .select("id")
170
- .where("email", "=", input.email)
171
- .where("id", "!=", id)
172
- .executeTakeFirst()))
173
- throw new IdentityError(409, "Email already exists.");
174
- const { expectedVersion, ...fields } = input;
175
- await trx
176
- .updateTable("identity_users")
177
- .set({
178
- ...fields,
179
- version: sql `version + 1`,
180
- active: input.active === undefined
181
- ? current.active
182
- : Number(input.active),
183
- })
184
- .where("id", "=", id)
185
- .execute();
186
- return {
187
- id,
188
- name: input.name ?? current.name,
189
- email: input.email ?? current.email,
190
- active: input.active ?? Boolean(current.active),
191
- version: expectedVersion + 1,
192
- };
193
- });
194
- }
195
- if (resource === "organizations") {
196
- const input = organizationUpdateSchema.parse(raw);
197
- return this.mutate(actor, resource, id, async (trx) => {
198
- const current = await trx
199
- .selectFrom("identity_tenants")
200
- .select("version")
201
- .where("id", "=", id)
202
- .executeTakeFirstOrThrow();
203
- this.revision(current.version, input.expectedVersion);
204
- if (input.active === false && id === actor.tenant.id)
205
- throw new IdentityError(409, "Cannot deactivate your current organization.");
206
- const { expectedVersion, ...fields } = input;
207
- await trx
208
- .updateTable("identity_tenants")
209
- .set({
210
- ...fields,
211
- version: sql `version + 1`,
212
- active: input.active === undefined ? undefined : Number(input.active),
213
- })
214
- .where("id", "=", id)
215
- .execute();
216
- if (input.active === false)
217
- await trx
218
- .deleteFrom("identity_sessions")
219
- .where("tenant_id", "=", id)
220
- .execute();
221
- const row = await trx
222
- .selectFrom("identity_tenants")
223
- .selectAll()
224
- .where("id", "=", id)
225
- .executeTakeFirstOrThrow();
226
- return {
227
- ...row,
228
- active: Boolean(row.active),
229
- version: Number(row.version),
230
- };
231
- });
232
- }
233
- if (resource === "roles") {
234
- const input = roleUpdateSchema.parse(raw);
235
- if (!input.permissionIds.includes(`desk.${id}`) ||
236
- !input.permissionIds.includes("identity.password"))
237
- throw new IdentityError(409, "Keep required portal and password permissions.");
238
- if (id !== "user" && !input.permissionIds.includes("identity.manage"))
239
- throw new IdentityError(409, "Keep administration permission on administrator roles.");
240
- return this.mutate(actor, resource, id, async (trx) => {
241
- const current = await trx
242
- .selectFrom("identity_roles")
243
- .select("version")
244
- .where("id", "=", id)
245
- .executeTakeFirstOrThrow();
246
- this.revision(current.version, input.expectedVersion);
247
- await validateDeclaredPermissions(trx, actor.appId, id, input.permissionIds);
248
- const known = await trx
249
- .selectFrom("identity_permissions")
250
- .select("id")
251
- .execute();
252
- if (input.permissionIds.some((permission) => !known.some((row) => row.id === permission)))
253
- throw new IdentityError(422, "Unknown permission.");
254
- if (id === "user" &&
255
- input.permissionIds.some((permission) => permission.startsWith("identity.manage")))
256
- throw new IdentityError(403, "User roles cannot receive administration permissions.");
257
- await trx
258
- .deleteFrom("identity_role_permissions")
259
- .where("role_id", "=", id)
260
- .execute();
261
- for (const permission of new Set(input.permissionIds))
262
- await trx
263
- .insertInto("identity_role_permissions")
264
- .values({ role_id: id, permission_id: permission })
265
- .execute();
266
- await trx
267
- .updateTable("identity_roles")
268
- .set({ version: sql `version + 1` })
269
- .where("id", "=", id)
270
- .execute();
271
- return {
272
- id,
273
- permissionIds: [...new Set(input.permissionIds)],
274
- version: input.expectedVersion + 1,
275
- };
276
- });
277
- }
278
- throw new IdentityError(405, "Update is not supported for this resource.");
279
- }
280
- async remove(actor, resource, id, expectedVersion) {
281
- if (resource === "memberships")
282
- return this.roles.removeMembership(actor, id, expectedVersion);
283
- this.allow(actor, resource, true);
284
- const row = await this.show(actor, resource, id);
285
- if (resource === "sessions") {
286
- await this.mutate(actor, resource, id, async (trx) => {
287
- await trx
288
- .deleteFrom("identity_sessions")
289
- .where("token_hash", "=", id)
290
- .where("app_id", "=", actor.appId)
291
- .execute();
292
- });
293
- return;
294
- }
295
- throw new IdentityError(405, "Deletion is not supported for this resource.");
296
- }
297
- async profile(actor, raw) {
298
- if (!actor.permissions.includes("identity.self"))
299
- throw new IdentityError(403, "Access denied.");
300
- if (raw !== undefined) {
301
- const input = profileSchema.parse(raw);
302
- await this.mutate(actor, "profile", actor.user.id, async (trx) => {
303
- const changed = await trx
304
- .updateTable("identity_users")
305
- .set({ name: input.name, version: sql `version + 1` })
306
- .where("id", "=", actor.user.id)
307
- .where("version", "=", input.expectedVersion)
308
- .executeTakeFirst();
309
- if (changed.numUpdatedRows !== 1n)
310
- throw new IdentityError(409, "This record changed. Reload before saving.");
311
- });
312
- }
313
- const user = await this.db
314
- .selectFrom("identity_users")
315
- .select(["id", "name", "email", "active", "version"])
316
- .where("id", "=", actor.user.id)
317
- .executeTakeFirstOrThrow();
318
- return {
319
- ...user,
320
- active: Boolean(user.active),
321
- version: Number(user.version),
322
- };
323
- }
324
- async settings(actor, raw) {
325
- if (raw !== undefined) {
326
- this.allow(actor, "settings", true);
327
- const input = settingsSchema.parse(raw);
328
- await this.mutate(actor, "settings", actor.tenant.id, async (trx) => {
329
- const current = await trx
330
- .selectFrom("identity_settings")
331
- .select("version")
332
- .where("tenant_id", "=", actor.tenant.id)
333
- .executeTakeFirst();
334
- this.revision(current?.version ?? 0, input.expectedVersion);
335
- await trx
336
- .insertInto("identity_settings")
337
- .values({
338
- tenant_id: actor.tenant.id,
339
- display_name: input.displayName,
340
- locale: input.locale,
341
- time_zone: input.timeZone,
342
- version: input.expectedVersion + 1,
343
- })
344
- .onConflict((c) => c.column("tenant_id").doUpdateSet({
345
- display_name: input.displayName,
346
- locale: input.locale,
347
- time_zone: input.timeZone,
348
- version: input.expectedVersion + 1,
349
- }))
350
- .execute();
351
- });
352
- }
353
- return this.presentationService.organizationSettings(actor);
354
- }
355
- async applicationSettings(actor, security, raw, fallbackSeconds) {
356
- if (actor.portal === "user")
357
- throw new IdentityError(403, "Access denied.");
358
- if (raw !== undefined) {
359
- if (actor.portal !== "super-admin" ||
360
- !actor.permissions.includes("identity.manage"))
361
- throw new IdentityError(403, "Access denied.");
362
- const input = security
363
- ? securitySettingsSchema.parse(raw)
364
- : settingsSchema.parse(raw);
365
- await this.mutate(actor, security ? "security-settings" : "application-settings", actor.appId, async (trx) => {
366
- const current = await trx
367
- .selectFrom("identity_app_settings")
368
- .selectAll()
369
- .where("app_id", "=", actor.appId)
370
- .executeTakeFirst();
371
- this.revision(current?.version ?? 0, input.expectedVersion);
372
- const row = {
373
- app_id: actor.appId,
374
- display_name: current?.display_name ?? this.appName,
375
- locale: current?.locale ?? "en",
376
- time_zone: current?.time_zone ?? "UTC",
377
- session_seconds: current?.session_seconds ?? fallbackSeconds,
378
- version: input.expectedVersion + 1,
379
- };
380
- if ("sessionSeconds" in input)
381
- row.session_seconds = input.sessionSeconds;
382
- else {
383
- row.display_name = input.displayName;
384
- row.locale = input.locale;
385
- row.time_zone = input.timeZone;
386
- }
387
- await trx
388
- .insertInto("identity_app_settings")
389
- .values(row)
390
- .onConflict((c) => c.column("app_id").doUpdateSet(row))
391
- .execute();
392
- if (security)
393
- await trx
394
- .deleteFrom("identity_sessions")
395
- .where("app_id", "=", actor.appId)
396
- .execute();
397
- });
398
- }
399
- const row = await this.db
400
- .selectFrom("identity_app_settings")
401
- .selectAll()
402
- .where("app_id", "=", actor.appId)
403
- .executeTakeFirst();
404
- return security
405
- ? {
406
- sessionSeconds: Number(row?.session_seconds ?? fallbackSeconds),
407
- version: Number(row?.version ?? 0),
408
- }
409
- : {
410
- displayName: row?.display_name ?? this.appName,
411
- locale: row?.locale ?? "en",
412
- timeZone: row?.time_zone ?? "UTC",
413
- version: Number(row?.version ?? 0),
414
- };
415
- }
416
- revision(current, expected) {
417
- if (Number(current) !== expected)
418
- throw new IdentityError(409, "This record changed. Reload before saving.");
419
- }
420
- async presentation(actor) {
421
- return this.presentationService.presentation(actor);
422
- }
423
- async configuration(appId, requiresOrganizationId) {
424
- return this.presentationService.configuration(appId, requiresOrganizationId);
425
- }
426
- allow(actor, resource, write) {
427
- if (!write &&
428
- ["roles", "permissions", "organizations"].includes(resource) &&
429
- actor.portal !== "user")
430
- return;
431
- if (resource === "sessions" && actor.portal === "user")
432
- return;
433
- if (actor.portal === "user" ||
434
- !actor.permissions.includes("identity.manage"))
435
- throw new IdentityError(403, "Access denied.");
436
- if (write &&
437
- ["roles", "permissions", "organizations"].includes(resource) &&
438
- actor.portal !== "super-admin")
439
- throw new IdentityError(403, "Only super administrators can change this resource.");
440
- }
441
- tenant(actor, requested) {
442
- if (requested &&
443
- requested !== actor.tenant.id &&
444
- actor.portal !== "super-admin")
445
- throw new IdentityError(403, "Access denied.");
446
- return requested ?? actor.tenant.id;
447
- }
448
- assignableRole(actor, role) {
449
- if (actor.portal !== "super-admin" && role !== "user")
450
- throw new IdentityError(403, "Only super administrators can assign privileged roles.");
451
- }
452
- async activeTenant(trx, id) {
453
- if (!(await trx
454
- .selectFrom("identity_tenants")
455
- .select("id")
456
- .where("id", "=", id)
457
- .where("active", "=", 1)
458
- .executeTakeFirst()))
459
- throw new IdentityError(422, "Organization is inactive or unavailable.");
460
- }
461
- async unprivilegedUser(trx, id) {
462
- const privileged = await trx
463
- .selectFrom("identity_memberships")
464
- .select("role_id")
465
- .where("user_id", "=", id)
466
- .where("role_id", "in", ["admin", "super-admin"])
467
- .executeTakeFirst();
468
- if (privileged)
469
- throw new IdentityError(403, "Only super administrators can change privileged accounts.");
470
- const tenants = await trx
471
- .selectFrom("identity_memberships")
472
- .select("tenant_id")
473
- .where("user_id", "=", id)
474
- .distinct()
475
- .execute();
476
- if (tenants.length > 1)
477
- throw new IdentityError(403, "Only super administrators can change shared accounts.");
478
- }
479
- async mutate(actor, resource, id, action, affectedTenant) {
480
- return this.db.transaction().execute(async (trx) => {
481
- checkIdentityRequest();
482
- const result = await action(trx, id);
483
- checkIdentityRequest();
484
- const targetTenant = affectedTenant ??
485
- (resource === "organizations"
486
- ? id
487
- : resource === "memberships"
488
- ? id.split("~")[1]
489
- : actor.tenant.id);
490
- let targets = [targetTenant];
491
- if (!affectedTenant && ["users", "profile"].includes(resource))
492
- targets = (await trx
493
- .selectFrom("identity_memberships")
494
- .select("tenant_id")
495
- .where("user_id", "=", id)
496
- .distinct()
497
- .execute()).map((row) => row.tenant_id);
498
- if (["roles", "application-settings", "security-settings"].includes(resource))
499
- targets = (await trx.selectFrom("identity_tenants").select("id").execute()).map((row) => row.id);
500
- for (const tenant of new Set(targets))
501
- await trx
502
- .insertInto("identity_audit_events")
503
- .values({
504
- id: randomUUID(),
505
- app_id: actor.appId,
506
- actor_id: actor.user.id,
507
- tenant_id: tenant,
508
- action: `identity.${resource}.changed`,
509
- resource_id: id,
510
- created_at: new Date().toISOString(),
511
- })
512
- .execute();
513
- checkIdentityRequest();
514
- return result;
515
- });
516
- }
517
- }
@@ -1,3 +0,0 @@
1
- import type { Transaction } from "kysely";
2
- import type { IdentitySchema } from "./identity.types.js";
3
- export declare function protectAdministrators(trx: Transaction<IdentitySchema>, userId: string, tenantId?: string, roleId?: string): Promise<void>;
@@ -1,27 +0,0 @@
1
- import { IdentityError } from "./identity.error.js";
2
- export async function protectAdministrators(trx, userId, tenantId, roleId) {
3
- let query = trx
4
- .selectFrom("identity_memberships")
5
- .selectAll()
6
- .where("user_id", "=", userId)
7
- .where("active", "=", 1)
8
- .where("role_id", "in", ["admin", "super-admin"]);
9
- if (tenantId)
10
- query = query.where("tenant_id", "=", tenantId);
11
- if (roleId)
12
- query = query.where("role_id", "=", roleId);
13
- for (const member of await query.execute()) {
14
- const peer = await trx
15
- .selectFrom("identity_memberships as m")
16
- .innerJoin("identity_users as u", "u.id", "m.user_id")
17
- .select("u.id")
18
- .where("m.tenant_id", "=", member.tenant_id)
19
- .where("m.role_id", "=", member.role_id)
20
- .where("m.active", "=", 1)
21
- .where("u.active", "=", 1)
22
- .where("u.id", "!=", userId)
23
- .executeTakeFirst();
24
- if (!peer)
25
- throw new IdentityError(409, "Keep at least one active administrator for this role and organization.");
26
- }
27
- }
@@ -1,23 +0,0 @@
1
- import { z } from "zod";
2
- import type { Kysely, Transaction } from "kysely";
3
- import type { IdentitySchema, Portal } from "./identity.types.js";
4
- declare const declarationSchema: z.ZodObject<{
5
- owner: z.ZodString;
6
- permissions: z.ZodArray<z.ZodObject<{
7
- id: z.ZodString;
8
- portals: z.ZodArray<z.ZodEnum<{
9
- admin: "admin";
10
- "super-admin": "super-admin";
11
- user: "user";
12
- }>>;
13
- label: z.ZodOptional<z.ZodString>;
14
- }, z.core.$strict>>;
15
- }, z.core.$strict>;
16
- export type IdentityPermissionDeclaration = z.input<typeof declarationSchema>;
17
- export declare function registerIdentityPermissions(db: Kysely<IdentitySchema>, appId: string, raw: unknown): Promise<{
18
- owner: string;
19
- permissionIds: string[];
20
- }>;
21
- export declare function validateDeclaredPermissions(db: Kysely<IdentitySchema> | Transaction<IdentitySchema>, appId: string, portal: Portal, ids: string[]): Promise<void>;
22
- export declare function filterDeclaredPermissions(db: Kysely<IdentitySchema>, appId: string, portal: Portal, ids: string[]): Promise<string[]>;
23
- export {};
@@ -1,57 +0,0 @@
1
- import { z } from "zod";
2
- import { IdentityError } from "./identity.error.js";
3
- import { portalSchema } from "./identity.schema.js";
4
- const declarationSchema = z.object({
5
- owner: z.string().regex(/^[a-z][a-z0-9-]{0,63}$/).refine((value) => !["identity", "desk"].includes(value)),
6
- permissions: z.array(z.object({
7
- id: z.string().regex(/^[a-z][a-z0-9-]*(?:\.[a-z][a-z0-9-]*){2,8}$/).max(250),
8
- portals: z.array(portalSchema).min(1).max(3),
9
- label: z.string().trim().min(1).max(100).regex(/^[^\x00-\x1f\x7f]+$/).optional(),
10
- }).strict()).min(1).max(100),
11
- }).strict();
12
- export async function registerIdentityPermissions(db, appId, raw) {
13
- const declaration = declarationSchema.parse(raw);
14
- const ids = new Set();
15
- for (const permission of declaration.permissions) {
16
- if (!permission.id.startsWith(`${appId}.${declaration.owner}.`) || ids.has(permission.id))
17
- throw new IdentityError(422, "Use unique permission IDs in this app and owner namespace.");
18
- ids.add(permission.id);
19
- }
20
- return db.transaction().execute(async (trx) => {
21
- for (const permission of declaration.permissions) {
22
- const portals = JSON.stringify([...new Set(permission.portals)].sort());
23
- const previous = await trx.selectFrom("identity_permission_declarations").selectAll()
24
- .where("permission_id", "=", permission.id).executeTakeFirst();
25
- if (previous) {
26
- if (previous.app_id !== appId || previous.owner !== declaration.owner || previous.portals !== portals)
27
- throw new IdentityError(409, "Permission declaration conflicts with its registered owner or portal policy.");
28
- if (permission.label !== undefined && permission.label !== previous.label)
29
- await trx.updateTable("identity_permission_declarations").set({ label: permission.label })
30
- .where("permission_id", "=", permission.id).execute();
31
- continue;
32
- }
33
- if (await trx.selectFrom("identity_permissions").select("id").where("id", "=", permission.id).executeTakeFirst())
34
- throw new IdentityError(409, "Permission already exists without this owner declaration.");
35
- await trx.insertInto("identity_permissions").values({ id: permission.id }).execute();
36
- await trx.insertInto("identity_permission_declarations").values({
37
- permission_id: permission.id, app_id: appId, owner: declaration.owner, portals, label: permission.label ?? null,
38
- }).execute();
39
- }
40
- return { owner: declaration.owner, permissionIds: [...ids] };
41
- });
42
- }
43
- export async function validateDeclaredPermissions(db, appId, portal, ids) {
44
- const declarations = await db.selectFrom("identity_permission_declarations").selectAll().execute();
45
- for (const id of ids) {
46
- const declaration = declarations.find((row) => row.permission_id === id);
47
- if (declaration && (declaration.app_id !== appId || !JSON.parse(declaration.portals).includes(portal)))
48
- throw new IdentityError(403, "Permission is unavailable in this app or portal.");
49
- }
50
- }
51
- export async function filterDeclaredPermissions(db, appId, portal, ids) {
52
- const declarations = await db.selectFrom("identity_permission_declarations").selectAll().execute();
53
- return ids.filter((id) => {
54
- const declaration = declarations.find((row) => row.permission_id === id);
55
- return !declaration || (declaration.app_id === appId && JSON.parse(declaration.portals).includes(portal));
56
- });
57
- }
@@ -1,23 +0,0 @@
1
- import type { Kysely } from "kysely";
2
- import type { IdentitySchema, Principal } from "./identity.types.js";
3
- export declare class IdentityPresentationService {
4
- private readonly db;
5
- private readonly appName;
6
- constructor(db: Kysely<IdentitySchema>, appName: string);
7
- configuration(appId: string, requiresOrganizationId: boolean): Promise<{
8
- displayName: string;
9
- requiresOrganizationId: boolean;
10
- }>;
11
- presentation(actor: Principal): Promise<{
12
- displayName: string;
13
- organizationDisplayName: string;
14
- locale: string;
15
- timeZone: string;
16
- }>;
17
- organizationSettings(actor: Principal): Promise<{
18
- displayName: string;
19
- locale: string;
20
- timeZone: string;
21
- version: number;
22
- }>;
23
- }