@wildo-ai/saas-backend-lib 1.1.0 → 1.1.2

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 (265) hide show
  1. package/dist/esm/base-classes/services/controllers/controller-resource.backend.service.d.ts +14 -3
  2. package/dist/esm/base-classes/services/controllers/controller-resource.backend.service.d.ts.map +1 -1
  3. package/dist/esm/base-classes/services/controllers/controller-resource.backend.service.js +115 -22
  4. package/dist/esm/base-classes/services/controllers/controller-resource.backend.service.js.map +1 -1
  5. package/dist/esm/base-classes/services/controllers/controllers-registry-handler.backend.service.d.ts +3 -1
  6. package/dist/esm/base-classes/services/controllers/controllers-registry-handler.backend.service.d.ts.map +1 -1
  7. package/dist/esm/base-classes/services/controllers/controllers-registry-handler.backend.service.js +8 -3
  8. package/dist/esm/base-classes/services/controllers/controllers-registry-handler.backend.service.js.map +1 -1
  9. package/dist/esm/base-classes/services/repositories/field-clearing-partition.backend.d.ts +11 -0
  10. package/dist/esm/base-classes/services/repositories/field-clearing-partition.backend.d.ts.map +1 -1
  11. package/dist/esm/base-classes/services/repositories/field-clearing-partition.backend.js +11 -2
  12. package/dist/esm/base-classes/services/repositories/field-clearing-partition.backend.js.map +1 -1
  13. package/dist/esm/base-classes/services/repositories/http-api/http-api-resource-repository.backend.service.d.ts +1 -0
  14. package/dist/esm/base-classes/services/repositories/http-api/http-api-resource-repository.backend.service.d.ts.map +1 -1
  15. package/dist/esm/base-classes/services/repositories/http-api/http-api-resource-repository.backend.service.js +6 -1
  16. package/dist/esm/base-classes/services/repositories/http-api/http-api-resource-repository.backend.service.js.map +1 -1
  17. package/dist/esm/base-classes/services/repositories/http-api/odata-query-compiler.backend.d.ts.map +1 -1
  18. package/dist/esm/base-classes/services/repositories/http-api/odata-query-compiler.backend.js +16 -0
  19. package/dist/esm/base-classes/services/repositories/http-api/odata-query-compiler.backend.js.map +1 -1
  20. package/dist/esm/base-classes/services/repositories/infrastructure-tier-contract.backend.d.ts +20 -6
  21. package/dist/esm/base-classes/services/repositories/infrastructure-tier-contract.backend.d.ts.map +1 -1
  22. package/dist/esm/base-classes/services/repositories/infrastructure-tier-contract.backend.js +88 -32
  23. package/dist/esm/base-classes/services/repositories/infrastructure-tier-contract.backend.js.map +1 -1
  24. package/dist/esm/base-classes/services/repositories/introspection/introspection-resource-repository.backend.service.d.ts +3 -1
  25. package/dist/esm/base-classes/services/repositories/introspection/introspection-resource-repository.backend.service.d.ts.map +1 -1
  26. package/dist/esm/base-classes/services/repositories/introspection/introspection-resource-repository.backend.service.js +32 -7
  27. package/dist/esm/base-classes/services/repositories/introspection/introspection-resource-repository.backend.service.js.map +1 -1
  28. package/dist/esm/base-classes/services/repositories/introspection/introspection-resource-source.backend.port.d.ts +8 -5
  29. package/dist/esm/base-classes/services/repositories/introspection/introspection-resource-source.backend.port.d.ts.map +1 -1
  30. package/dist/esm/base-classes/services/repositories/introspection/introspection-resource-source.backend.port.js.map +1 -1
  31. package/dist/esm/base-classes/services/repositories/postgresql-resource-repository.backend.service.d.ts +31 -0
  32. package/dist/esm/base-classes/services/repositories/postgresql-resource-repository.backend.service.d.ts.map +1 -1
  33. package/dist/esm/base-classes/services/repositories/postgresql-resource-repository.backend.service.js +127 -5
  34. package/dist/esm/base-classes/services/repositories/postgresql-resource-repository.backend.service.js.map +1 -1
  35. package/dist/esm/base-classes/services/repositories/repositories-registry-handler.backend.service.d.ts +71 -1
  36. package/dist/esm/base-classes/services/repositories/repositories-registry-handler.backend.service.d.ts.map +1 -1
  37. package/dist/esm/base-classes/services/repositories/repositories-registry-handler.backend.service.js +62 -2
  38. package/dist/esm/base-classes/services/repositories/repositories-registry-handler.backend.service.js.map +1 -1
  39. package/dist/esm/base-classes/services/repositories/repository-resource.backend.service.d.ts +21 -0
  40. package/dist/esm/base-classes/services/repositories/repository-resource.backend.service.d.ts.map +1 -1
  41. package/dist/esm/base-classes/services/repositories/repository-resource.backend.service.js +126 -4
  42. package/dist/esm/base-classes/services/repositories/repository-resource.backend.service.js.map +1 -1
  43. package/dist/esm/base-classes/services/services/resource-operation-implementation-coverage.backend.utils.d.ts +3 -26
  44. package/dist/esm/base-classes/services/services/resource-operation-implementation-coverage.backend.utils.d.ts.map +1 -1
  45. package/dist/esm/base-classes/services/services/resource-operation-implementation-coverage.backend.utils.js +3 -68
  46. package/dist/esm/base-classes/services/services/resource-operation-implementation-coverage.backend.utils.js.map +1 -1
  47. package/dist/esm/base-classes/services/services/services-registry-handler.backend.service.d.ts.map +1 -1
  48. package/dist/esm/base-classes/services/services/services-registry-handler.backend.service.js +4 -2
  49. package/dist/esm/base-classes/services/services/services-registry-handler.backend.service.js.map +1 -1
  50. package/dist/esm/billing/services/custom-impl/billing-account.mutations.custom-impl.backend.service.js +6 -6
  51. package/dist/esm/billing/services/custom-impl/billing-account.mutations.custom-impl.backend.service.js.map +1 -1
  52. package/dist/esm/compliance/audit-trails/services/audit-event-dispatcher.backend.service.d.ts +38 -0
  53. package/dist/esm/compliance/audit-trails/services/audit-event-dispatcher.backend.service.d.ts.map +1 -1
  54. package/dist/esm/compliance/audit-trails/services/audit-event-dispatcher.backend.service.js +40 -0
  55. package/dist/esm/compliance/audit-trails/services/audit-event-dispatcher.backend.service.js.map +1 -1
  56. package/dist/esm/compliance/audit-trails/services/audit-logs.backend.service.d.ts +20 -0
  57. package/dist/esm/compliance/audit-trails/services/audit-logs.backend.service.d.ts.map +1 -1
  58. package/dist/esm/compliance/audit-trails/services/audit-logs.backend.service.js +28 -0
  59. package/dist/esm/compliance/audit-trails/services/audit-logs.backend.service.js.map +1 -1
  60. package/dist/esm/compliance/audit-trails/services/siem-export-config-cache-invalidation.custom-impl.backend.service.d.ts +12 -0
  61. package/dist/esm/compliance/audit-trails/services/siem-export-config-cache-invalidation.custom-impl.backend.service.d.ts.map +1 -0
  62. package/dist/esm/compliance/audit-trails/services/siem-export-config-cache-invalidation.custom-impl.backend.service.js +118 -0
  63. package/dist/esm/compliance/audit-trails/services/siem-export-config-cache-invalidation.custom-impl.backend.service.js.map +1 -0
  64. package/dist/esm/compliance/audit-trails/services/siem-webhook-delivery.backend.service.d.ts.map +1 -1
  65. package/dist/esm/compliance/audit-trails/services/siem-webhook-delivery.backend.service.js +8 -5
  66. package/dist/esm/compliance/audit-trails/services/siem-webhook-delivery.backend.service.js.map +1 -1
  67. package/dist/esm/compliance/audit-trails/services/utils/ocsf-activity-mapping.backend.utils.d.ts.map +1 -1
  68. package/dist/esm/compliance/audit-trails/services/utils/ocsf-activity-mapping.backend.utils.js +14 -0
  69. package/dist/esm/compliance/audit-trails/services/utils/ocsf-activity-mapping.backend.utils.js.map +1 -1
  70. package/dist/esm/config/custom/application-config-controller.backend.service.d.ts.map +1 -1
  71. package/dist/esm/config/custom/application-config-controller.backend.service.js +7 -1
  72. package/dist/esm/config/custom/application-config-controller.backend.service.js.map +1 -1
  73. package/dist/esm/config/models/app-config.backend.schemas.d.ts +4 -0
  74. package/dist/esm/config/models/app-config.backend.schemas.d.ts.map +1 -1
  75. package/dist/esm/config/services/app-configuration.backend.utils.d.ts +10 -1
  76. package/dist/esm/config/services/app-configuration.backend.utils.d.ts.map +1 -1
  77. package/dist/esm/config/services/app-configuration.backend.utils.js +13 -2
  78. package/dist/esm/config/services/app-configuration.backend.utils.js.map +1 -1
  79. package/dist/esm/data-seeding/services/shared/data-seed-scope-iteration.shared.utils.d.ts +2 -2
  80. package/dist/esm/data-seeding/services/shared/data-seed-scope-iteration.shared.utils.js +2 -2
  81. package/dist/esm/data-seeding/services/shared/data-seed-scope-iteration.shared.utils.js.map +1 -1
  82. package/dist/esm/databases/models/filter.backend.schemas.js +2 -2
  83. package/dist/esm/databases/models/filter.backend.schemas.js.map +1 -1
  84. package/dist/esm/databases/services/aggregation/aggregation-authorization.backend.service.d.ts.map +1 -1
  85. package/dist/esm/databases/services/aggregation/aggregation-authorization.backend.service.js +20 -1
  86. package/dist/esm/databases/services/aggregation/aggregation-authorization.backend.service.js.map +1 -1
  87. package/dist/esm/databases/services/redis.service.d.ts +27 -0
  88. package/dist/esm/databases/services/redis.service.d.ts.map +1 -1
  89. package/dist/esm/databases/services/redis.service.js +29 -0
  90. package/dist/esm/databases/services/redis.service.js.map +1 -1
  91. package/dist/esm/errors/services/error-handler.backend.service.d.ts.map +1 -1
  92. package/dist/esm/errors/services/error-handler.backend.service.js +5 -0
  93. package/dist/esm/errors/services/error-handler.backend.service.js.map +1 -1
  94. package/dist/esm/external-data/controllers/external-data-admin.backend.contracts.d.ts +41 -0
  95. package/dist/esm/external-data/controllers/external-data-admin.backend.contracts.d.ts.map +1 -0
  96. package/dist/esm/external-data/controllers/external-data-admin.backend.contracts.js +40 -0
  97. package/dist/esm/external-data/controllers/external-data-admin.backend.contracts.js.map +1 -0
  98. package/dist/esm/external-data/controllers/external-data-admin.backend.controller.d.ts +64 -0
  99. package/dist/esm/external-data/controllers/external-data-admin.backend.controller.d.ts.map +1 -0
  100. package/dist/esm/external-data/controllers/external-data-admin.backend.controller.js +193 -0
  101. package/dist/esm/external-data/controllers/external-data-admin.backend.controller.js.map +1 -0
  102. package/dist/esm/external-data/external-data-pipeline-run.backend.service.d.ts +13 -2
  103. package/dist/esm/external-data/external-data-pipeline-run.backend.service.d.ts.map +1 -1
  104. package/dist/esm/external-data/external-data-pipeline-run.backend.service.js +8 -2
  105. package/dist/esm/external-data/external-data-pipeline-run.backend.service.js.map +1 -1
  106. package/dist/esm/external-data/external-data-transform.backend.d.ts.map +1 -1
  107. package/dist/esm/external-data/external-data-transform.backend.js +7 -1
  108. package/dist/esm/external-data/external-data-transform.backend.js.map +1 -1
  109. package/dist/esm/external-data/models/external-data-pipeline-state.backend.schemas.d.ts +2 -0
  110. package/dist/esm/external-data/models/external-data-pipeline-state.backend.schemas.d.ts.map +1 -1
  111. package/dist/esm/external-data/models/external-data-pipeline-state.backend.schemas.js +13 -0
  112. package/dist/esm/external-data/models/external-data-pipeline-state.backend.schemas.js.map +1 -1
  113. package/dist/esm/external-providers/services/external-providers-registry.backend.service.d.ts +14 -0
  114. package/dist/esm/external-providers/services/external-providers-registry.backend.service.d.ts.map +1 -1
  115. package/dist/esm/external-providers/services/external-providers-registry.backend.service.js +6 -0
  116. package/dist/esm/external-providers/services/external-providers-registry.backend.service.js.map +1 -1
  117. package/dist/esm/files/controllers/file.controller.d.ts +24 -1
  118. package/dist/esm/files/controllers/file.controller.d.ts.map +1 -1
  119. package/dist/esm/files/controllers/file.controller.js +66 -2
  120. package/dist/esm/files/controllers/file.controller.js.map +1 -1
  121. package/dist/esm/files/handlers/file-upload-route-locals.backend.types.d.ts +17 -0
  122. package/dist/esm/files/handlers/file-upload-route-locals.backend.types.d.ts.map +1 -0
  123. package/dist/esm/files/handlers/file-upload-route-locals.backend.types.js +1 -0
  124. package/dist/esm/files/handlers/file-upload-route-locals.backend.types.js.map +1 -0
  125. package/dist/esm/files/handlers/resource-file-upload-handler.backend.service.d.ts +5 -1
  126. package/dist/esm/files/handlers/resource-file-upload-handler.backend.service.d.ts.map +1 -1
  127. package/dist/esm/files/handlers/resource-file-upload-handler.backend.service.js +42 -3
  128. package/dist/esm/files/handlers/resource-file-upload-handler.backend.service.js.map +1 -1
  129. package/dist/esm/files/services/file-operation-handler.backend.service.d.ts +18 -0
  130. package/dist/esm/files/services/file-operation-handler.backend.service.d.ts.map +1 -1
  131. package/dist/esm/files/services/file-operation-handler.backend.service.js +36 -28
  132. package/dist/esm/files/services/file-operation-handler.backend.service.js.map +1 -1
  133. package/dist/esm/files/services/file-upload-grant.backend.service.d.ts +163 -0
  134. package/dist/esm/files/services/file-upload-grant.backend.service.d.ts.map +1 -0
  135. package/dist/esm/files/services/file-upload-grant.backend.service.js +442 -0
  136. package/dist/esm/files/services/file-upload-grant.backend.service.js.map +1 -0
  137. package/dist/esm/files/services/files.backend.service.d.ts +19 -0
  138. package/dist/esm/files/services/files.backend.service.d.ts.map +1 -1
  139. package/dist/esm/files/services/files.backend.service.js +78 -15
  140. package/dist/esm/files/services/files.backend.service.js.map +1 -1
  141. package/dist/esm/files/utils/file-uploader.utils.d.ts.map +1 -1
  142. package/dist/esm/files/utils/file-uploader.utils.js +12 -0
  143. package/dist/esm/files/utils/file-uploader.utils.js.map +1 -1
  144. package/dist/esm/flows-actors/a2a/services/a2a-task-cancellation.backend.service.js +5 -5
  145. package/dist/esm/flows-actors/a2a/services/a2a-task-cancellation.backend.service.js.map +1 -1
  146. package/dist/esm/flows-actors/services/agents.backend.service.d.ts +79 -0
  147. package/dist/esm/flows-actors/services/agents.backend.service.d.ts.map +1 -1
  148. package/dist/esm/flows-actors/services/agents.backend.service.js +319 -42
  149. package/dist/esm/flows-actors/services/agents.backend.service.js.map +1 -1
  150. package/dist/esm/flows-actors/utils/acp-session-runner.backend.utils.d.ts +151 -41
  151. package/dist/esm/flows-actors/utils/acp-session-runner.backend.utils.d.ts.map +1 -1
  152. package/dist/esm/flows-actors/utils/acp-session-runner.backend.utils.js +300 -139
  153. package/dist/esm/flows-actors/utils/acp-session-runner.backend.utils.js.map +1 -1
  154. package/dist/esm/index.d.ts +4 -1
  155. package/dist/esm/index.d.ts.map +1 -1
  156. package/dist/esm/index.js +14 -1
  157. package/dist/esm/index.js.map +1 -1
  158. package/dist/esm/inject/models/container.d.ts.map +1 -1
  159. package/dist/esm/inject/models/container.js +4 -0
  160. package/dist/esm/inject/models/container.js.map +1 -1
  161. package/dist/esm/inject/models/service-types.d.ts +3 -0
  162. package/dist/esm/inject/models/service-types.d.ts.map +1 -1
  163. package/dist/esm/inject/models/service-types.js +2 -0
  164. package/dist/esm/inject/models/service-types.js.map +1 -1
  165. package/dist/esm/mcp/controllers/mcp.controller.d.ts.map +1 -1
  166. package/dist/esm/mcp/controllers/mcp.controller.js +12 -4
  167. package/dist/esm/mcp/controllers/mcp.controller.js.map +1 -1
  168. package/dist/esm/mcp/models/mcp-tool.backend.schemas.d.ts +34 -0
  169. package/dist/esm/mcp/models/mcp-tool.backend.schemas.d.ts.map +1 -1
  170. package/dist/esm/mcp/models/mcp-tool.backend.schemas.js +42 -0
  171. package/dist/esm/mcp/models/mcp-tool.backend.schemas.js.map +1 -1
  172. package/dist/esm/mcp/services/mcp-server.backend.service.d.ts +29 -1
  173. package/dist/esm/mcp/services/mcp-server.backend.service.d.ts.map +1 -1
  174. package/dist/esm/mcp/services/mcp-server.backend.service.js +131 -3
  175. package/dist/esm/mcp/services/mcp-server.backend.service.js.map +1 -1
  176. package/dist/esm/mcp/services/mcp-tool-adapter.backend.service.d.ts +64 -2
  177. package/dist/esm/mcp/services/mcp-tool-adapter.backend.service.d.ts.map +1 -1
  178. package/dist/esm/mcp/services/mcp-tool-adapter.backend.service.js +130 -6
  179. package/dist/esm/mcp/services/mcp-tool-adapter.backend.service.js.map +1 -1
  180. package/dist/esm/notifications/services/notifications-dispatcher.backend.service.d.ts +9 -0
  181. package/dist/esm/notifications/services/notifications-dispatcher.backend.service.d.ts.map +1 -1
  182. package/dist/esm/notifications/services/notifications-dispatcher.backend.service.js +11 -36
  183. package/dist/esm/notifications/services/notifications-dispatcher.backend.service.js.map +1 -1
  184. package/dist/esm/notifications/webhooks/outbound/batches/m2m-webhook-delivery.batch.backend.service.d.ts.map +1 -1
  185. package/dist/esm/notifications/webhooks/outbound/batches/m2m-webhook-delivery.batch.backend.service.js +9 -7
  186. package/dist/esm/notifications/webhooks/outbound/batches/m2m-webhook-delivery.batch.backend.service.js.map +1 -1
  187. package/dist/esm/notifications/webhooks/outbound/batches/m2m-webhook-delivery.batch.utils.d.ts +5 -5
  188. package/dist/esm/notifications/webhooks/outbound/batches/m2m-webhook-delivery.batch.utils.d.ts.map +1 -1
  189. package/dist/esm/notifications/webhooks/outbound/batches/m2m-webhook-delivery.batch.utils.js +14 -13
  190. package/dist/esm/notifications/webhooks/outbound/batches/m2m-webhook-delivery.batch.utils.js.map +1 -1
  191. package/dist/esm/notifications/webhooks/outbound/m2m-webhook-signed-request.backend.utils.d.ts +2 -1
  192. package/dist/esm/notifications/webhooks/outbound/m2m-webhook-signed-request.backend.utils.d.ts.map +1 -1
  193. package/dist/esm/notifications/webhooks/outbound/m2m-webhook-signed-request.backend.utils.js +2 -2
  194. package/dist/esm/notifications/webhooks/outbound/m2m-webhook-signed-request.backend.utils.js.map +1 -1
  195. package/dist/esm/notifications/webhooks/outbound/services/webhook-config-test-endpoint.custom-impl.backend.service.d.ts.map +1 -1
  196. package/dist/esm/notifications/webhooks/outbound/services/webhook-config-test-endpoint.custom-impl.backend.service.js +9 -7
  197. package/dist/esm/notifications/webhooks/outbound/services/webhook-config-test-endpoint.custom-impl.backend.service.js.map +1 -1
  198. package/dist/esm/organizations/services/organization-unit-custom-implementation.backend.service.d.ts.map +1 -1
  199. package/dist/esm/organizations/services/organization-unit-custom-implementation.backend.service.js +4 -1
  200. package/dist/esm/organizations/services/organization-unit-custom-implementation.backend.service.js.map +1 -1
  201. package/dist/esm/requests/service/websocket.service.d.ts.map +1 -1
  202. package/dist/esm/requests/service/websocket.service.js +56 -7
  203. package/dist/esm/requests/service/websocket.service.js.map +1 -1
  204. package/dist/esm/resources/models/custom-service-implementations.backend.definition.d.ts.map +1 -1
  205. package/dist/esm/resources/models/custom-service-implementations.backend.definition.js +2 -0
  206. package/dist/esm/resources/models/custom-service-implementations.backend.definition.js.map +1 -1
  207. package/dist/esm/resources/services/resource-id.backend.service.js +3 -3
  208. package/dist/esm/resources/services/resource-id.backend.service.js.map +1 -1
  209. package/dist/esm/resources/services/resources-registry.backend.service.js +1 -1
  210. package/dist/esm/resources/services/resources-registry.backend.service.js.map +1 -1
  211. package/dist/esm/runtime/services/execution-context-creator.backend.service.d.ts.map +1 -1
  212. package/dist/esm/runtime/services/execution-context-creator.backend.service.js +13 -7
  213. package/dist/esm/runtime/services/execution-context-creator.backend.service.js.map +1 -1
  214. package/dist/esm/runtime/services/start-application.backend.service.d.ts +28 -0
  215. package/dist/esm/runtime/services/start-application.backend.service.d.ts.map +1 -1
  216. package/dist/esm/runtime/services/start-application.backend.service.js +82 -0
  217. package/dist/esm/runtime/services/start-application.backend.service.js.map +1 -1
  218. package/dist/esm/security/authentications/models/jwt.backend.schemas.d.ts +3 -23
  219. package/dist/esm/security/authentications/models/jwt.backend.schemas.d.ts.map +1 -1
  220. package/dist/esm/security/authentications/models/jwt.backend.schemas.js +20 -45
  221. package/dist/esm/security/authentications/models/jwt.backend.schemas.js.map +1 -1
  222. package/dist/esm/security/authentications/services/auth-methods/auth-method-management.backend.service.d.ts +9 -1
  223. package/dist/esm/security/authentications/services/auth-methods/auth-method-management.backend.service.d.ts.map +1 -1
  224. package/dist/esm/security/authentications/services/auth-methods/auth-method-management.backend.service.js +9 -1
  225. package/dist/esm/security/authentications/services/auth-methods/auth-method-management.backend.service.js.map +1 -1
  226. package/dist/esm/security/authentications/services/auth-methods/auth-mfa.backend.service.d.ts.map +1 -1
  227. package/dist/esm/security/authentications/services/auth-methods/auth-mfa.backend.service.js +5 -3
  228. package/dist/esm/security/authentications/services/auth-methods/auth-mfa.backend.service.js.map +1 -1
  229. package/dist/esm/security/authentications/services/auth-token-issuer.backend.service.d.ts +25 -1
  230. package/dist/esm/security/authentications/services/auth-token-issuer.backend.service.d.ts.map +1 -1
  231. package/dist/esm/security/authentications/services/auth-token-issuer.backend.service.js +55 -4
  232. package/dist/esm/security/authentications/services/auth-token-issuer.backend.service.js.map +1 -1
  233. package/dist/esm/security/authorizations/initiator-organization-confinement.backend.d.ts.map +1 -1
  234. package/dist/esm/security/authorizations/initiator-organization-confinement.backend.js +24 -1
  235. package/dist/esm/security/authorizations/initiator-organization-confinement.backend.js.map +1 -1
  236. package/dist/esm/security/authorizations/privileged-access-review.backend.utils.d.ts +117 -0
  237. package/dist/esm/security/authorizations/privileged-access-review.backend.utils.d.ts.map +1 -0
  238. package/dist/esm/security/authorizations/privileged-access-review.backend.utils.js +104 -0
  239. package/dist/esm/security/authorizations/privileged-access-review.backend.utils.js.map +1 -0
  240. package/dist/esm/security/authorizations/services/authorizations.backend.service.d.ts +101 -6
  241. package/dist/esm/security/authorizations/services/authorizations.backend.service.d.ts.map +1 -1
  242. package/dist/esm/security/authorizations/services/authorizations.backend.service.js +210 -30
  243. package/dist/esm/security/authorizations/services/authorizations.backend.service.js.map +1 -1
  244. package/dist/esm/security/consumable-tokens/batches/consumable-tokens-cleanup.batch.backend.service.d.ts +18 -1
  245. package/dist/esm/security/consumable-tokens/batches/consumable-tokens-cleanup.batch.backend.service.d.ts.map +1 -1
  246. package/dist/esm/security/consumable-tokens/batches/consumable-tokens-cleanup.batch.backend.service.js +110 -74
  247. package/dist/esm/security/consumable-tokens/batches/consumable-tokens-cleanup.batch.backend.service.js.map +1 -1
  248. package/dist/esm/security/consumable-tokens/consumable-token-models.exports.d.ts +22 -0
  249. package/dist/esm/security/consumable-tokens/consumable-token-models.exports.d.ts.map +1 -0
  250. package/dist/esm/security/consumable-tokens/consumable-token-models.exports.js +22 -0
  251. package/dist/esm/security/consumable-tokens/consumable-token-models.exports.js.map +1 -0
  252. package/dist/esm/security/consumable-tokens/models/consumable-tokens.backend.resources-config.schemas.d.ts +17 -3
  253. package/dist/esm/security/consumable-tokens/models/consumable-tokens.backend.resources-config.schemas.d.ts.map +1 -1
  254. package/dist/esm/security/consumable-tokens/models/consumable-tokens.backend.resources-config.schemas.js +12 -0
  255. package/dist/esm/security/consumable-tokens/models/consumable-tokens.backend.resources-config.schemas.js.map +1 -1
  256. package/dist/esm/security/consumable-tokens/services/consumable-token.backend.service.d.ts +20 -0
  257. package/dist/esm/security/consumable-tokens/services/consumable-token.backend.service.d.ts.map +1 -1
  258. package/dist/esm/security/consumable-tokens/services/consumable-token.backend.service.js +58 -5
  259. package/dist/esm/security/consumable-tokens/services/consumable-token.backend.service.js.map +1 -1
  260. package/dist/esm/security/jwt/services/jwt.backend.service.d.ts.map +1 -1
  261. package/dist/esm/security/jwt/services/jwt.backend.service.js +1 -18
  262. package/dist/esm/security/jwt/services/jwt.backend.service.js.map +1 -1
  263. package/dist/tsconfig.build.tsbuildinfo +1 -1
  264. package/package.json +15 -9
  265. package/dist/esm/.builder.pid +0 -9
@@ -219,40 +219,40 @@ export function createBillingAccountOpenCheckoutFactories() {
219
219
  if (!promotion) {
220
220
  throw utils.errorBuilder.buildError(ErrorType.NOT_FOUND, executionContext, {
221
221
  customMessageReference: ErrorCustomMessageReference.BILLING_PROMOTION_NOT_FOUND,
222
- context: { code: input.promotionCode },
222
+ context: { promotionCode: input.promotionCode },
223
223
  });
224
224
  }
225
225
  const now = new Date();
226
226
  if (promotion.validFrom && now < new Date(promotion.validFrom)) {
227
227
  throw utils.errorBuilder.buildError(ErrorType.BUSINESS_RULE, executionContext, {
228
228
  customMessageReference: ErrorCustomMessageReference.BILLING_PROMOTION_INVALID,
229
- context: { code: input.promotionCode, reason: 'Promotion has not started yet' },
229
+ context: { promotionCode: input.promotionCode, reason: 'Promotion has not started yet' },
230
230
  });
231
231
  }
232
232
  if (promotion.validUntil && now > new Date(promotion.validUntil)) {
233
233
  throw utils.errorBuilder.buildError(ErrorType.BUSINESS_RULE, executionContext, {
234
234
  customMessageReference: ErrorCustomMessageReference.BILLING_PROMOTION_INVALID,
235
- context: { code: input.promotionCode, reason: 'Promotion has expired' },
235
+ context: { promotionCode: input.promotionCode, reason: 'Promotion has expired' },
236
236
  });
237
237
  }
238
238
  if (promotion.maxRedemptions !== null && promotion.maxRedemptions !== undefined) {
239
239
  if ((promotion.timesRedeemed ?? 0) >= promotion.maxRedemptions) {
240
240
  throw utils.errorBuilder.buildError(ErrorType.BUSINESS_RULE, executionContext, {
241
241
  customMessageReference: ErrorCustomMessageReference.BILLING_PROMOTION_INVALID,
242
- context: { code: input.promotionCode, reason: 'Maximum redemptions reached' },
242
+ context: { promotionCode: input.promotionCode, reason: 'Maximum redemptions reached' },
243
243
  });
244
244
  }
245
245
  }
246
246
  if (promotion.applicableProducts?.length > 0 && !promotion.applicableProducts.includes(input.productKey)) {
247
247
  throw utils.errorBuilder.buildError(ErrorType.BUSINESS_RULE, executionContext, {
248
248
  customMessageReference: ErrorCustomMessageReference.BILLING_PROMOTION_INVALID,
249
- context: { code: input.promotionCode, reason: 'Promotion is not applicable to this product', productKey: input.productKey },
249
+ context: { promotionCode: input.promotionCode, reason: 'Promotion is not applicable to this product', productKey: input.productKey },
250
250
  });
251
251
  }
252
252
  if (!promotion.providerPromotionId && !promotion.providerCouponId) {
253
253
  throw utils.errorBuilder.buildError(ErrorType.BUSINESS_RULE, executionContext, {
254
254
  customMessageReference: ErrorCustomMessageReference.BILLING_PROMOTION_INVALID,
255
- context: { code: input.promotionCode, reason: 'Promotion is not synced to billing provider' },
255
+ context: { promotionCode: input.promotionCode, reason: 'Promotion is not synced to billing provider' },
256
256
  });
257
257
  }
258
258
  providerPromotionCodeId = promotion.providerPromotionId;
@@ -1 +1 @@
1
- {"version":3,"file":"billing-account.mutations.custom-impl.backend.service.js","sourceRoot":"","sources":["../../../../../../src/billing/services/custom-impl/billing-account.mutations.custom-impl.backend.service.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Billing Account Mutations Custom Implementations\n *\n * Implements CREATE, OPEN_CHECKOUT, OPEN_PORTAL operations\n * on billing account polymorphic variants.\n *\n * Patterns:\n * CREATE → PREFIX_CORE_OPERATIONS — PREFIX calls provider.createCustomer(), core CREATE writes to DB\n * DELETE → PREFIX_CORE_OPERATIONS — PREFIX refuses the hard delete (409) while retained\n * financial children still reference the account; core DELETE otherwise proceeds\n * OPEN_CHECKOUT → REPLACE_CORE_OPERATIONS — calls provider.createCheckoutSession(), returns { url, sessionId }\n * OPEN_PORTAL → REPLACE_CORE_OPERATIONS — calls provider.createCustomerPortalSession(), returns { url }\n *\n * One factory per polymorphic variant (org, user) per operation.\n *\n * @see billing-polymorphic-full-implementation.md §4.4\n */\n\nimport {\n CoreResourceOperation,\n CoreResourceType,\n ResourceOperationVariantType,\n ResourcePrimaryScope,\n ProductType,\n ErrorType,\n ErrorCustomMessageReference,\n BillingAccount_Operations,\n BillingAccount_OpenCheckoutRequestDto,\n BillingAccount_OpenPortalRequestDto,\n} from '@wildo-ai/saas-models';\nimport {\n createResourceCustomServiceImplementation,\n type ResourceCustomServiceImplementationFactory,\n} from '~wildo-backend-lib/base-classes/services/services/resource-operation-custom-implementation.factory.backend';\nimport type { ResourcesRegistryBackendService } from '~wildo-backend-lib/resources/services/resources-registry.backend.service';\nimport {\n describeUnaddressableShape,\n resolveSingleSubjectId,\n} from '~wildo-backend-lib/base-classes/utils/single-subject-addressing.backend.utils';\nimport { getBillingProvider, getBillingConfig, getBillingStateService } from '~wildo-backend-lib/billing/services/custom-impl/billing-custom-implementations.backend';\nimport { assertNoBillingAccountDependents, readProductByKey, readPriceForProduct, readPromotionByCode, resolveBillingVariantsFromResourceType } from '~wildo-backend-lib/billing/services/custom-impl/billing-custom-impl-utils.backend';\nimport { buildCheckoutSessionMetadata } from '~wildo-backend-lib/billing/services/billing-checkout-metadata.backend.utils';\n\n// ── Billing account polymorphic variant types ────────────────────────\n\nconst BILLING_ACCOUNT_VARIANT_TYPES = [\n CoreResourceType.ORGANIZATION_BILLING_ACCOUNTS,\n CoreResourceType.USER_BILLING_ACCOUNTS,\n] as const;\n\n// ════════════════════════════════════════════════════════════════════\n// CREATE — PREFIX_CORE_OPERATIONS\n// ════════════════════════════════════════════════════════════════════\n\n/**\n * Create billing account CREATE custom impl factories for all variants.\n *\n * PREFIX: Creates a customer on the billing provider (Stripe), returns the create data\n * augmented with providerCustomerId and providerRef.\n * CORE: Standard CREATE writes to DB.\n *\n * Registered on `INTERNAL_CALL` — the variant the resource configuration actually declares\n * for CREATE. This registration was authored against `REPOSITORY_ONLY` and was orphaned when\n * 623c5aee moved the configured variant to `INTERNAL_CALL` (the auto-create path runs through\n * `SystemAccessBackendService.createAsSystem`, which the service tier only reaches via\n * INTERNAL_CALL / API_CALL). Because custom implementations resolve by an EXACT operation-path\n * key that embeds `variantType`, the stale registration was unreachable rather than degraded:\n * `provider.createCustomer()` never ran and every auto-created account was persisted with no\n * `providerCustomerId` — the only local key mapping invoices and subscriptions back to the\n * provider customer. Pinned by `billing-custom-impl-operation-path-coherence.test.ts`.\n */\nexport function createBillingAccountCreateFactories(): ResourceCustomServiceImplementationFactory[] {\n return BILLING_ACCOUNT_VARIANT_TYPES.map((variantResourceType) => {\n const factory = (_resourcesRegistryService: ResourcesRegistryBackendService) => {\n return createResourceCustomServiceImplementation({\n operationPath: {\n resourceIdentifier: variantResourceType,\n operationIdentifier: CoreResourceOperation.CREATE,\n variantType: ResourceOperationVariantType.INTERNAL_CALL,\n isOperationDefault: true,\n },\n handlers: {\n prefixCoreOperations: async (_id, input, executionContext, _operationPath, utils) => {\n const provider = getBillingProvider();\n const config = getBillingConfig();\n\n // Extract contact info from the initiator's user context\n const userContext = utils.initiatorUserContext;\n const email = userContext?.email || 'unknown@example.com';\n const name = userContext?.displayName || userContext?.name || userContext?.email || 'Unknown';\n\n const { providerCustomerId } = await provider.createCustomer({\n email,\n name,\n metadata: {\n resourceType: variantResourceType,\n ...input.metadata,\n },\n });\n\n return {\n ...input,\n providerCustomerId,\n providerRef: config.providerRef || 'stripe',\n isActive: true,\n };\n },\n },\n });\n };\n\n return factory as unknown as ResourceCustomServiceImplementationFactory;\n });\n}\n\n// ════════════════════════════════════════════════════════════════════\n// DELETE — PREFIX_CORE_OPERATIONS (dependents guard)\n// ════════════════════════════════════════════════════════════════════\n\n/**\n * Create billing account DELETE guard factories for all variants.\n *\n * PREFIX: refuses the hard delete with 409 `BILLING_ACCOUNT_HAS_DEPENDENTS` while retained\n * subscriptions / invoice refs / usage records / credit pools still reference the account.\n * There is no CORE replacement — when the guard passes, the standard DELETE proceeds untouched.\n *\n * This enforces the billing-retention decision (2026-07-29) rather than adding new policy: that\n * decision disabled all 18 billing cascades so the financial audit trail survives its scope, and\n * its rationale block explicitly forbids the resulting half-state (\"retaining the children while\n * deleting the account strands the provider-customer mapping\"). A direct hard delete was the only\n * remaining path into that state. See `assertNoBillingAccountDependents` for the full reasoning.\n *\n * Registered on `INTERNAL_CALL` — the variant the resource configuration actually declares for\n * DELETE. Custom implementations are resolved by an exact operation-path key that embeds\n * `variantType`, so a guard registered under any other variant would never run.\n */\nexport function createBillingAccountDeleteDependentsGuardFactories(): ResourceCustomServiceImplementationFactory[] {\n return BILLING_ACCOUNT_VARIANT_TYPES.map((variantResourceType) => {\n const factory = (_resourcesRegistryService: ResourcesRegistryBackendService) => {\n return createResourceCustomServiceImplementation({\n operationPath: {\n resourceIdentifier: variantResourceType,\n operationIdentifier: CoreResourceOperation.DELETE,\n variantType: ResourceOperationVariantType.INTERNAL_CALL,\n isOperationDefault: true,\n },\n handlers: {\n prefixCoreOperations: async (_id, input, executionContext, _operationPath, utils) => {\n // Fail closed: the dependent scan filters on a single account id, so a POPULATION-addressed\n // delete (an ids array or an unpinned filter reaching this path via `doByOperationPath`)\n // must be refused rather than silently producing a filter that matches nothing and waving\n // the delete through. DELETE is CRITICAL.\n //\n // A conditional `{ _id, … }` filter resolves to that one account and is admitted — the scan\n // then runs against exactly the account the delete can destroy.\n const billingAccountId = resolveSingleSubjectId(_id);\n if (billingAccountId === undefined) {\n throw utils.errorBuilder.buildError(ErrorType.VALIDATION, executionContext, {\n context: {\n message: 'Billing account DELETE requires a single addressed account to verify dependents',\n resourceType: variantResourceType,\n receivedAddressing: describeUnaddressableShape(_id),\n },\n });\n }\n\n const variants = resolveBillingVariantsFromResourceType(variantResourceType);\n if (!variants) {\n throw utils.errorBuilder.buildError(ErrorType.CONFIGURATION, executionContext, {\n customMessageReference: ErrorCustomMessageReference.BILLING_SCOPE_NOT_ENABLED,\n context: { resourceType: variantResourceType },\n });\n }\n\n await assertNoBillingAccountDependents(billingAccountId, variants, executionContext, utils);\n\n // Guard only — the core DELETE input is passed through unchanged.\n return input;\n },\n },\n });\n };\n\n return factory as unknown as ResourceCustomServiceImplementationFactory;\n });\n}\n\n// ════════════════════════════════════════════════════════════════════\n// OPEN_CHECKOUT — REPLACE_CORE_OPERATIONS\n// ════════════════════════════════════════════════════════════════════\n\n/**\n * Create OPEN_CHECKOUT custom impl factories for all variants.\n *\n * REPLACE: Resolves the billing account, product, and price.\n * Creates a Stripe Checkout Session and returns the redirect URL.\n * No DB write — the subscription is created by webhook after payment.\n */\nexport function createBillingAccountOpenCheckoutFactories(): ResourceCustomServiceImplementationFactory[] {\n return BILLING_ACCOUNT_VARIANT_TYPES.map((variantResourceType) => {\n const factory = (_resourcesRegistryService: ResourcesRegistryBackendService) => {\n return createResourceCustomServiceImplementation({\n operationPath: {\n resourceIdentifier: variantResourceType,\n operationIdentifier: BillingAccount_Operations.OPEN_CHECKOUT,\n variantType: ResourceOperationVariantType.API_CALL,\n isOperationDefault: true,\n },\n requestDtoSchema: BillingAccount_OpenCheckoutRequestDto,\n handlers: {\n replaceCoreOperations: async (_id, input, executionContext, _operationPath, utils) => {\n const billingAccount = utils.currentObject;\n\n if (!billingAccount?.providerCustomerId) {\n throw utils.errorBuilder.buildError(ErrorType.BUSINESS_RULE, executionContext, {\n customMessageReference: ErrorCustomMessageReference.BILLING_NO_PROVIDER_CUSTOMER,\n context: { billingAccountId: _id },\n });\n }\n\n // Resolve product and price\n const product = await readProductByKey(input.productKey, executionContext, utils);\n\n // Validate product scope — prevent purchasing a product targeted at a different scope\n const variants = resolveBillingVariantsFromResourceType(variantResourceType);\n if (variants && product.targetScopes && !product.targetScopes.includes(variants.scopeType)) {\n throw utils.errorBuilder.buildError(ErrorType.BUSINESS_RULE, executionContext, {\n customMessageReference: ErrorCustomMessageReference.BILLING_PRODUCT_SCOPE_MISMATCH,\n context: {\n productKey: input.productKey,\n productScopes: product.targetScopes,\n currentScope: variants.scopeType,\n },\n });\n }\n\n // The price MUST belong to the product being purchased. Resolving the two independently\n // (which this did) bills the provider for one product's price while the local record —\n // and therefore every entitlement derived from it — names another.\n const price = await readPriceForProduct(input.priceId, product, executionContext, utils);\n\n if (!price.providerPriceId) {\n throw utils.errorBuilder.buildError(ErrorType.CONFIGURATION, executionContext, {\n customMessageReference: ErrorCustomMessageReference.BILLING_NO_PROVIDER_PRICE,\n context: { priceId: input.priceId, productKey: input.productKey },\n });\n }\n\n // Determine checkout mode based on product type\n const isSubscription = [ProductType.PLAN, ProductType.ADDON, ProductType.METERED].includes(product.type as ProductType);\n\n // Resolve trial days\n const config = getBillingConfig();\n const trialDays = input.trialDays\n ?? product.behaviorPolicy?.trial?.days\n ?? config.defaultBehaviorPolicy.defaultTrialDays;\n\n // Resolve promotion code → provider promotion code ID (C1 + H3)\n let providerPromotionCodeId: string | undefined;\n if (input.promotionCode) {\n const promotion = await readPromotionByCode(input.promotionCode, executionContext, utils);\n if (!promotion) {\n throw utils.errorBuilder.buildError(ErrorType.NOT_FOUND, executionContext, {\n customMessageReference: ErrorCustomMessageReference.BILLING_PROMOTION_NOT_FOUND,\n context: { code: input.promotionCode },\n });\n }\n\n const now = new Date();\n if (promotion.validFrom && now < new Date(promotion.validFrom)) {\n throw utils.errorBuilder.buildError(ErrorType.BUSINESS_RULE, executionContext, {\n customMessageReference: ErrorCustomMessageReference.BILLING_PROMOTION_INVALID,\n context: { code: input.promotionCode, reason: 'Promotion has not started yet' },\n });\n }\n if (promotion.validUntil && now > new Date(promotion.validUntil)) {\n throw utils.errorBuilder.buildError(ErrorType.BUSINESS_RULE, executionContext, {\n customMessageReference: ErrorCustomMessageReference.BILLING_PROMOTION_INVALID,\n context: { code: input.promotionCode, reason: 'Promotion has expired' },\n });\n }\n if (promotion.maxRedemptions !== null && promotion.maxRedemptions !== undefined) {\n if ((promotion.timesRedeemed ?? 0) >= promotion.maxRedemptions) {\n throw utils.errorBuilder.buildError(ErrorType.BUSINESS_RULE, executionContext, {\n customMessageReference: ErrorCustomMessageReference.BILLING_PROMOTION_INVALID,\n context: { code: input.promotionCode, reason: 'Maximum redemptions reached' },\n });\n }\n }\n if (promotion.applicableProducts?.length > 0 && !promotion.applicableProducts.includes(input.productKey)) {\n throw utils.errorBuilder.buildError(ErrorType.BUSINESS_RULE, executionContext, {\n customMessageReference: ErrorCustomMessageReference.BILLING_PROMOTION_INVALID,\n context: { code: input.promotionCode, reason: 'Promotion is not applicable to this product', productKey: input.productKey },\n });\n }\n if (!promotion.providerPromotionId && !promotion.providerCouponId) {\n throw utils.errorBuilder.buildError(ErrorType.BUSINESS_RULE, executionContext, {\n customMessageReference: ErrorCustomMessageReference.BILLING_PROMOTION_INVALID,\n context: { code: input.promotionCode, reason: 'Promotion is not synced to billing provider' },\n });\n }\n providerPromotionCodeId = promotion.providerPromotionId;\n }\n\n // Create checkout session on provider\n const provider = getBillingProvider();\n const session = await provider.createCheckoutSession({\n providerCustomerId: billingAccount.providerCustomerId,\n lineItems: [{ providerPriceId: price.providerPriceId, quantity: input.quantity }],\n mode: isSubscription ? 'subscription' : 'payment',\n successUrl: input.successUrl,\n cancelUrl: input.cancelUrl,\n trialPeriodDays: isSubscription && trialDays > 0 ? trialDays : undefined,\n allowPromotionCodes: !providerPromotionCodeId,\n providerPromotionCodeId,\n // The provider echoes this map back VERBATIM inside a genuinely signed\n // `checkout.session.completed`, and the webhook router turns it into a credit grant. It\n // used to be assembled with `...input.metadata` LAST, so a caller-supplied record could\n // overwrite `billingAccountId` / `productKey` / `quantity` — no signature forgery\n // required, because a valid signature proves the PROVIDER sent the bytes, not that WE\n // wrote them. The builder writes the framework's reserved keys last and strips reserved\n // keys out of caller metadata; see `billing-checkout-metadata.backend.utils.ts`.\n metadata: buildCheckoutSessionMetadata({\n billingAccountId: _id!,\n productKey: input.productKey,\n quantity: input.quantity,\n callerMetadata: input.metadata,\n }),\n });\n\n return { url: session.url, sessionId: session.sessionId };\n },\n },\n });\n };\n\n return factory as unknown as ResourceCustomServiceImplementationFactory;\n });\n}\n\n// ════════════════════════════════════════════════════════════════════\n// OPEN_PORTAL — REPLACE_CORE_OPERATIONS\n// ════════════════════════════════════════════════════════════════════\n\n/**\n * Create OPEN_PORTAL custom impl factories for all variants.\n *\n * REPLACE: Resolves the billing account's providerCustomerId, creates a\n * Stripe Customer Portal session, and returns the redirect URL.\n * No DB write.\n */\nexport function createBillingAccountOpenPortalFactories(): ResourceCustomServiceImplementationFactory[] {\n return BILLING_ACCOUNT_VARIANT_TYPES.map((variantResourceType) => {\n const factory = (_resourcesRegistryService: ResourcesRegistryBackendService) => {\n return createResourceCustomServiceImplementation({\n operationPath: {\n resourceIdentifier: variantResourceType,\n operationIdentifier: BillingAccount_Operations.OPEN_PORTAL,\n variantType: ResourceOperationVariantType.API_CALL,\n isOperationDefault: true,\n },\n requestDtoSchema: BillingAccount_OpenPortalRequestDto,\n handlers: {\n replaceCoreOperations: async (_id, input, executionContext, _operationPath, utils) => {\n const billingAccount = utils.currentObject;\n\n if (!billingAccount?.providerCustomerId) {\n throw utils.errorBuilder.buildError(ErrorType.BUSINESS_RULE, executionContext, {\n customMessageReference: ErrorCustomMessageReference.BILLING_NO_PROVIDER_CUSTOMER,\n context: { billingAccountId: _id },\n });\n }\n\n const provider = getBillingProvider();\n const result = await provider.createCustomerPortalSession(\n billingAccount.providerCustomerId,\n input.returnUrl,\n );\n\n return { url: result.url };\n },\n },\n });\n };\n\n return factory as unknown as ResourceCustomServiceImplementationFactory;\n });\n}\n\n// ════════════════════════════════════════════════════════════════════\n// GET_STATE — REPLACE_CORE_OPERATIONS (collection-level)\n// ════════════════════════════════════════════════════════════════════\n\n/**\n * Create GET_STATE custom impl factories for all variants.\n *\n * REPLACE: Collection-level operation (no entity ID). Resolves scopeType\n * and scopeEntityId from execution context, delegates to BillingStateBackendService.\n * Returns the full aggregated billing state with automatic authorization via\n * the engine's parent scope validation.\n */\nexport function createBillingAccountGetStateFactories(): ResourceCustomServiceImplementationFactory[] {\n return BILLING_ACCOUNT_VARIANT_TYPES.map((variantResourceType) => {\n const factory = (_resourcesRegistryService: ResourcesRegistryBackendService) => {\n return createResourceCustomServiceImplementation({\n operationPath: {\n resourceIdentifier: variantResourceType,\n operationIdentifier: BillingAccount_Operations.GET_STATE,\n variantType: ResourceOperationVariantType.API_CALL,\n isOperationDefault: true,\n },\n handlers: {\n replaceCoreOperations: async (_id, _input, executionContext, _operationPath, _utils) => {\n const variants = resolveBillingVariantsFromResourceType(variantResourceType);\n if (!variants) {\n throw _utils.errorBuilder.buildError(ErrorType.CONFIGURATION, executionContext, {\n customMessageReference: ErrorCustomMessageReference.BILLING_SCOPE_NOT_ENABLED,\n context: { resourceType: variantResourceType },\n });\n }\n\n const scopeEntityId = executionContext[variants.scopeKey] as string | undefined;\n if (!scopeEntityId) {\n throw _utils.errorBuilder.buildError(ErrorType.VALIDATION, executionContext, {\n context: { message: 'Missing scope entity ID in execution context', scopeKey: variants.scopeKey },\n });\n }\n\n const billingConfig = getBillingConfig();\n const enabledScopes = billingConfig.enabledScopes ?? [ResourcePrimaryScope.ORGANIZATIONS];\n\n const billingStateService = getBillingStateService();\n return billingStateService.getState(variants.scopeType, scopeEntityId, enabledScopes, executionContext);\n },\n },\n });\n };\n\n return factory as unknown as ResourceCustomServiceImplementationFactory;\n });\n}\n"]}
1
+ {"version":3,"file":"billing-account.mutations.custom-impl.backend.service.js","sourceRoot":"","sources":["../../../../../../src/billing/services/custom-impl/billing-account.mutations.custom-impl.backend.service.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Billing Account Mutations Custom Implementations\n *\n * Implements CREATE, OPEN_CHECKOUT, OPEN_PORTAL operations\n * on billing account polymorphic variants.\n *\n * Patterns:\n * CREATE → PREFIX_CORE_OPERATIONS — PREFIX calls provider.createCustomer(), core CREATE writes to DB\n * DELETE → PREFIX_CORE_OPERATIONS — PREFIX refuses the hard delete (409) while retained\n * financial children still reference the account; core DELETE otherwise proceeds\n * OPEN_CHECKOUT → REPLACE_CORE_OPERATIONS — calls provider.createCheckoutSession(), returns { url, sessionId }\n * OPEN_PORTAL → REPLACE_CORE_OPERATIONS — calls provider.createCustomerPortalSession(), returns { url }\n *\n * One factory per polymorphic variant (org, user) per operation.\n *\n * @see billing-polymorphic-full-implementation.md §4.4\n */\n\nimport {\n CoreResourceOperation,\n CoreResourceType,\n ResourceOperationVariantType,\n ResourcePrimaryScope,\n ProductType,\n ErrorType,\n ErrorCustomMessageReference,\n BillingAccount_Operations,\n BillingAccount_OpenCheckoutRequestDto,\n BillingAccount_OpenPortalRequestDto,\n} from '@wildo-ai/saas-models';\nimport {\n createResourceCustomServiceImplementation,\n type ResourceCustomServiceImplementationFactory,\n} from '~wildo-backend-lib/base-classes/services/services/resource-operation-custom-implementation.factory.backend';\nimport type { ResourcesRegistryBackendService } from '~wildo-backend-lib/resources/services/resources-registry.backend.service';\nimport {\n describeUnaddressableShape,\n resolveSingleSubjectId,\n} from '~wildo-backend-lib/base-classes/utils/single-subject-addressing.backend.utils';\nimport { getBillingProvider, getBillingConfig, getBillingStateService } from '~wildo-backend-lib/billing/services/custom-impl/billing-custom-implementations.backend';\nimport { assertNoBillingAccountDependents, readProductByKey, readPriceForProduct, readPromotionByCode, resolveBillingVariantsFromResourceType } from '~wildo-backend-lib/billing/services/custom-impl/billing-custom-impl-utils.backend';\nimport { buildCheckoutSessionMetadata } from '~wildo-backend-lib/billing/services/billing-checkout-metadata.backend.utils';\n\n// ── Billing account polymorphic variant types ────────────────────────\n\nconst BILLING_ACCOUNT_VARIANT_TYPES = [\n CoreResourceType.ORGANIZATION_BILLING_ACCOUNTS,\n CoreResourceType.USER_BILLING_ACCOUNTS,\n] as const;\n\n// ════════════════════════════════════════════════════════════════════\n// CREATE — PREFIX_CORE_OPERATIONS\n// ════════════════════════════════════════════════════════════════════\n\n/**\n * Create billing account CREATE custom impl factories for all variants.\n *\n * PREFIX: Creates a customer on the billing provider (Stripe), returns the create data\n * augmented with providerCustomerId and providerRef.\n * CORE: Standard CREATE writes to DB.\n *\n * Registered on `INTERNAL_CALL` — the variant the resource configuration actually declares\n * for CREATE. This registration was authored against `REPOSITORY_ONLY` and was orphaned when\n * 623c5aee moved the configured variant to `INTERNAL_CALL` (the auto-create path runs through\n * `SystemAccessBackendService.createAsSystem`, which the service tier only reaches via\n * INTERNAL_CALL / API_CALL). Because custom implementations resolve by an EXACT operation-path\n * key that embeds `variantType`, the stale registration was unreachable rather than degraded:\n * `provider.createCustomer()` never ran and every auto-created account was persisted with no\n * `providerCustomerId` — the only local key mapping invoices and subscriptions back to the\n * provider customer. Pinned by `billing-custom-impl-operation-path-coherence.test.ts`.\n */\nexport function createBillingAccountCreateFactories(): ResourceCustomServiceImplementationFactory[] {\n return BILLING_ACCOUNT_VARIANT_TYPES.map((variantResourceType) => {\n const factory = (_resourcesRegistryService: ResourcesRegistryBackendService) => {\n return createResourceCustomServiceImplementation({\n operationPath: {\n resourceIdentifier: variantResourceType,\n operationIdentifier: CoreResourceOperation.CREATE,\n variantType: ResourceOperationVariantType.INTERNAL_CALL,\n isOperationDefault: true,\n },\n handlers: {\n prefixCoreOperations: async (_id, input, executionContext, _operationPath, utils) => {\n const provider = getBillingProvider();\n const config = getBillingConfig();\n\n // Extract contact info from the initiator's user context\n const userContext = utils.initiatorUserContext;\n const email = userContext?.email || 'unknown@example.com';\n const name = userContext?.displayName || userContext?.name || userContext?.email || 'Unknown';\n\n const { providerCustomerId } = await provider.createCustomer({\n email,\n name,\n metadata: {\n resourceType: variantResourceType,\n ...input.metadata,\n },\n });\n\n return {\n ...input,\n providerCustomerId,\n providerRef: config.providerRef || 'stripe',\n isActive: true,\n };\n },\n },\n });\n };\n\n return factory as unknown as ResourceCustomServiceImplementationFactory;\n });\n}\n\n// ════════════════════════════════════════════════════════════════════\n// DELETE — PREFIX_CORE_OPERATIONS (dependents guard)\n// ════════════════════════════════════════════════════════════════════\n\n/**\n * Create billing account DELETE guard factories for all variants.\n *\n * PREFIX: refuses the hard delete with 409 `BILLING_ACCOUNT_HAS_DEPENDENTS` while retained\n * subscriptions / invoice refs / usage records / credit pools still reference the account.\n * There is no CORE replacement — when the guard passes, the standard DELETE proceeds untouched.\n *\n * This enforces the billing-retention decision (2026-07-29) rather than adding new policy: that\n * decision disabled all 18 billing cascades so the financial audit trail survives its scope, and\n * its rationale block explicitly forbids the resulting half-state (\"retaining the children while\n * deleting the account strands the provider-customer mapping\"). A direct hard delete was the only\n * remaining path into that state. See `assertNoBillingAccountDependents` for the full reasoning.\n *\n * Registered on `INTERNAL_CALL` — the variant the resource configuration actually declares for\n * DELETE. Custom implementations are resolved by an exact operation-path key that embeds\n * `variantType`, so a guard registered under any other variant would never run.\n */\nexport function createBillingAccountDeleteDependentsGuardFactories(): ResourceCustomServiceImplementationFactory[] {\n return BILLING_ACCOUNT_VARIANT_TYPES.map((variantResourceType) => {\n const factory = (_resourcesRegistryService: ResourcesRegistryBackendService) => {\n return createResourceCustomServiceImplementation({\n operationPath: {\n resourceIdentifier: variantResourceType,\n operationIdentifier: CoreResourceOperation.DELETE,\n variantType: ResourceOperationVariantType.INTERNAL_CALL,\n isOperationDefault: true,\n },\n handlers: {\n prefixCoreOperations: async (_id, input, executionContext, _operationPath, utils) => {\n // Fail closed: the dependent scan filters on a single account id, so a POPULATION-addressed\n // delete (an ids array or an unpinned filter reaching this path via `doByOperationPath`)\n // must be refused rather than silently producing a filter that matches nothing and waving\n // the delete through. DELETE is CRITICAL.\n //\n // A conditional `{ _id, … }` filter resolves to that one account and is admitted — the scan\n // then runs against exactly the account the delete can destroy.\n const billingAccountId = resolveSingleSubjectId(_id);\n if (billingAccountId === undefined) {\n throw utils.errorBuilder.buildError(ErrorType.VALIDATION, executionContext, {\n context: {\n message: 'Billing account DELETE requires a single addressed account to verify dependents',\n resourceType: variantResourceType,\n receivedAddressing: describeUnaddressableShape(_id),\n },\n });\n }\n\n const variants = resolveBillingVariantsFromResourceType(variantResourceType);\n if (!variants) {\n throw utils.errorBuilder.buildError(ErrorType.CONFIGURATION, executionContext, {\n customMessageReference: ErrorCustomMessageReference.BILLING_SCOPE_NOT_ENABLED,\n context: { resourceType: variantResourceType },\n });\n }\n\n await assertNoBillingAccountDependents(billingAccountId, variants, executionContext, utils);\n\n // Guard only — the core DELETE input is passed through unchanged.\n return input;\n },\n },\n });\n };\n\n return factory as unknown as ResourceCustomServiceImplementationFactory;\n });\n}\n\n// ════════════════════════════════════════════════════════════════════\n// OPEN_CHECKOUT — REPLACE_CORE_OPERATIONS\n// ════════════════════════════════════════════════════════════════════\n\n/**\n * Create OPEN_CHECKOUT custom impl factories for all variants.\n *\n * REPLACE: Resolves the billing account, product, and price.\n * Creates a Stripe Checkout Session and returns the redirect URL.\n * No DB write — the subscription is created by webhook after payment.\n */\nexport function createBillingAccountOpenCheckoutFactories(): ResourceCustomServiceImplementationFactory[] {\n return BILLING_ACCOUNT_VARIANT_TYPES.map((variantResourceType) => {\n const factory = (_resourcesRegistryService: ResourcesRegistryBackendService) => {\n return createResourceCustomServiceImplementation({\n operationPath: {\n resourceIdentifier: variantResourceType,\n operationIdentifier: BillingAccount_Operations.OPEN_CHECKOUT,\n variantType: ResourceOperationVariantType.API_CALL,\n isOperationDefault: true,\n },\n requestDtoSchema: BillingAccount_OpenCheckoutRequestDto,\n handlers: {\n replaceCoreOperations: async (_id, input, executionContext, _operationPath, utils) => {\n const billingAccount = utils.currentObject;\n\n if (!billingAccount?.providerCustomerId) {\n throw utils.errorBuilder.buildError(ErrorType.BUSINESS_RULE, executionContext, {\n customMessageReference: ErrorCustomMessageReference.BILLING_NO_PROVIDER_CUSTOMER,\n context: { billingAccountId: _id },\n });\n }\n\n // Resolve product and price\n const product = await readProductByKey(input.productKey, executionContext, utils);\n\n // Validate product scope — prevent purchasing a product targeted at a different scope\n const variants = resolveBillingVariantsFromResourceType(variantResourceType);\n if (variants && product.targetScopes && !product.targetScopes.includes(variants.scopeType)) {\n throw utils.errorBuilder.buildError(ErrorType.BUSINESS_RULE, executionContext, {\n customMessageReference: ErrorCustomMessageReference.BILLING_PRODUCT_SCOPE_MISMATCH,\n context: {\n productKey: input.productKey,\n productScopes: product.targetScopes,\n currentScope: variants.scopeType,\n },\n });\n }\n\n // The price MUST belong to the product being purchased. Resolving the two independently\n // (which this did) bills the provider for one product's price while the local record —\n // and therefore every entitlement derived from it — names another.\n const price = await readPriceForProduct(input.priceId, product, executionContext, utils);\n\n if (!price.providerPriceId) {\n throw utils.errorBuilder.buildError(ErrorType.CONFIGURATION, executionContext, {\n customMessageReference: ErrorCustomMessageReference.BILLING_NO_PROVIDER_PRICE,\n context: { priceId: input.priceId, productKey: input.productKey },\n });\n }\n\n // Determine checkout mode based on product type\n const isSubscription = [ProductType.PLAN, ProductType.ADDON, ProductType.METERED].includes(product.type as ProductType);\n\n // Resolve trial days\n const config = getBillingConfig();\n const trialDays = input.trialDays\n ?? product.behaviorPolicy?.trial?.days\n ?? config.defaultBehaviorPolicy.defaultTrialDays;\n\n // Resolve promotion code → provider promotion code ID (C1 + H3)\n let providerPromotionCodeId: string | undefined;\n if (input.promotionCode) {\n const promotion = await readPromotionByCode(input.promotionCode, executionContext, utils);\n if (!promotion) {\n throw utils.errorBuilder.buildError(ErrorType.NOT_FOUND, executionContext, {\n customMessageReference: ErrorCustomMessageReference.BILLING_PROMOTION_NOT_FOUND,\n context: { promotionCode: input.promotionCode },\n });\n }\n\n const now = new Date();\n if (promotion.validFrom && now < new Date(promotion.validFrom)) {\n throw utils.errorBuilder.buildError(ErrorType.BUSINESS_RULE, executionContext, {\n customMessageReference: ErrorCustomMessageReference.BILLING_PROMOTION_INVALID,\n context: { promotionCode: input.promotionCode, reason: 'Promotion has not started yet' },\n });\n }\n if (promotion.validUntil && now > new Date(promotion.validUntil)) {\n throw utils.errorBuilder.buildError(ErrorType.BUSINESS_RULE, executionContext, {\n customMessageReference: ErrorCustomMessageReference.BILLING_PROMOTION_INVALID,\n context: { promotionCode: input.promotionCode, reason: 'Promotion has expired' },\n });\n }\n if (promotion.maxRedemptions !== null && promotion.maxRedemptions !== undefined) {\n if ((promotion.timesRedeemed ?? 0) >= promotion.maxRedemptions) {\n throw utils.errorBuilder.buildError(ErrorType.BUSINESS_RULE, executionContext, {\n customMessageReference: ErrorCustomMessageReference.BILLING_PROMOTION_INVALID,\n context: { promotionCode: input.promotionCode, reason: 'Maximum redemptions reached' },\n });\n }\n }\n if (promotion.applicableProducts?.length > 0 && !promotion.applicableProducts.includes(input.productKey)) {\n throw utils.errorBuilder.buildError(ErrorType.BUSINESS_RULE, executionContext, {\n customMessageReference: ErrorCustomMessageReference.BILLING_PROMOTION_INVALID,\n context: { promotionCode: input.promotionCode, reason: 'Promotion is not applicable to this product', productKey: input.productKey },\n });\n }\n if (!promotion.providerPromotionId && !promotion.providerCouponId) {\n throw utils.errorBuilder.buildError(ErrorType.BUSINESS_RULE, executionContext, {\n customMessageReference: ErrorCustomMessageReference.BILLING_PROMOTION_INVALID,\n context: { promotionCode: input.promotionCode, reason: 'Promotion is not synced to billing provider' },\n });\n }\n providerPromotionCodeId = promotion.providerPromotionId;\n }\n\n // Create checkout session on provider\n const provider = getBillingProvider();\n const session = await provider.createCheckoutSession({\n providerCustomerId: billingAccount.providerCustomerId,\n lineItems: [{ providerPriceId: price.providerPriceId, quantity: input.quantity }],\n mode: isSubscription ? 'subscription' : 'payment',\n successUrl: input.successUrl,\n cancelUrl: input.cancelUrl,\n trialPeriodDays: isSubscription && trialDays > 0 ? trialDays : undefined,\n allowPromotionCodes: !providerPromotionCodeId,\n providerPromotionCodeId,\n // The provider echoes this map back VERBATIM inside a genuinely signed\n // `checkout.session.completed`, and the webhook router turns it into a credit grant. It\n // used to be assembled with `...input.metadata` LAST, so a caller-supplied record could\n // overwrite `billingAccountId` / `productKey` / `quantity` — no signature forgery\n // required, because a valid signature proves the PROVIDER sent the bytes, not that WE\n // wrote them. The builder writes the framework's reserved keys last and strips reserved\n // keys out of caller metadata; see `billing-checkout-metadata.backend.utils.ts`.\n metadata: buildCheckoutSessionMetadata({\n billingAccountId: _id!,\n productKey: input.productKey,\n quantity: input.quantity,\n callerMetadata: input.metadata,\n }),\n });\n\n return { url: session.url, sessionId: session.sessionId };\n },\n },\n });\n };\n\n return factory as unknown as ResourceCustomServiceImplementationFactory;\n });\n}\n\n// ════════════════════════════════════════════════════════════════════\n// OPEN_PORTAL — REPLACE_CORE_OPERATIONS\n// ════════════════════════════════════════════════════════════════════\n\n/**\n * Create OPEN_PORTAL custom impl factories for all variants.\n *\n * REPLACE: Resolves the billing account's providerCustomerId, creates a\n * Stripe Customer Portal session, and returns the redirect URL.\n * No DB write.\n */\nexport function createBillingAccountOpenPortalFactories(): ResourceCustomServiceImplementationFactory[] {\n return BILLING_ACCOUNT_VARIANT_TYPES.map((variantResourceType) => {\n const factory = (_resourcesRegistryService: ResourcesRegistryBackendService) => {\n return createResourceCustomServiceImplementation({\n operationPath: {\n resourceIdentifier: variantResourceType,\n operationIdentifier: BillingAccount_Operations.OPEN_PORTAL,\n variantType: ResourceOperationVariantType.API_CALL,\n isOperationDefault: true,\n },\n requestDtoSchema: BillingAccount_OpenPortalRequestDto,\n handlers: {\n replaceCoreOperations: async (_id, input, executionContext, _operationPath, utils) => {\n const billingAccount = utils.currentObject;\n\n if (!billingAccount?.providerCustomerId) {\n throw utils.errorBuilder.buildError(ErrorType.BUSINESS_RULE, executionContext, {\n customMessageReference: ErrorCustomMessageReference.BILLING_NO_PROVIDER_CUSTOMER,\n context: { billingAccountId: _id },\n });\n }\n\n const provider = getBillingProvider();\n const result = await provider.createCustomerPortalSession(\n billingAccount.providerCustomerId,\n input.returnUrl,\n );\n\n return { url: result.url };\n },\n },\n });\n };\n\n return factory as unknown as ResourceCustomServiceImplementationFactory;\n });\n}\n\n// ════════════════════════════════════════════════════════════════════\n// GET_STATE — REPLACE_CORE_OPERATIONS (collection-level)\n// ════════════════════════════════════════════════════════════════════\n\n/**\n * Create GET_STATE custom impl factories for all variants.\n *\n * REPLACE: Collection-level operation (no entity ID). Resolves scopeType\n * and scopeEntityId from execution context, delegates to BillingStateBackendService.\n * Returns the full aggregated billing state with automatic authorization via\n * the engine's parent scope validation.\n */\nexport function createBillingAccountGetStateFactories(): ResourceCustomServiceImplementationFactory[] {\n return BILLING_ACCOUNT_VARIANT_TYPES.map((variantResourceType) => {\n const factory = (_resourcesRegistryService: ResourcesRegistryBackendService) => {\n return createResourceCustomServiceImplementation({\n operationPath: {\n resourceIdentifier: variantResourceType,\n operationIdentifier: BillingAccount_Operations.GET_STATE,\n variantType: ResourceOperationVariantType.API_CALL,\n isOperationDefault: true,\n },\n handlers: {\n replaceCoreOperations: async (_id, _input, executionContext, _operationPath, _utils) => {\n const variants = resolveBillingVariantsFromResourceType(variantResourceType);\n if (!variants) {\n throw _utils.errorBuilder.buildError(ErrorType.CONFIGURATION, executionContext, {\n customMessageReference: ErrorCustomMessageReference.BILLING_SCOPE_NOT_ENABLED,\n context: { resourceType: variantResourceType },\n });\n }\n\n const scopeEntityId = executionContext[variants.scopeKey] as string | undefined;\n if (!scopeEntityId) {\n throw _utils.errorBuilder.buildError(ErrorType.VALIDATION, executionContext, {\n context: { message: 'Missing scope entity ID in execution context', scopeKey: variants.scopeKey },\n });\n }\n\n const billingConfig = getBillingConfig();\n const enabledScopes = billingConfig.enabledScopes ?? [ResourcePrimaryScope.ORGANIZATIONS];\n\n const billingStateService = getBillingStateService();\n return billingStateService.getState(variants.scopeType, scopeEntityId, enabledScopes, executionContext);\n },\n },\n });\n };\n\n return factory as unknown as ResourceCustomServiceImplementationFactory;\n });\n}\n"]}
@@ -113,6 +113,44 @@ export declare class AuditEventDispatcherBackendService {
113
113
  * config) so a non-configured org is not re-read every event.
114
114
  */
115
115
  private _loadSiemConfigCached;
116
+ /**
117
+ * Drop one organization's cached SIEM config so the NEXT event re-reads it.
118
+ *
119
+ * ## Why this exists
120
+ *
121
+ * Without it the cache had no invalidation at all — only the 5-minute TTL and the size-1000
122
+ * FIFO — so enabling a sink left the organization's events unrouted for up to that window, and
123
+ * audit events are not replayed. The ENABLE direction is the sharp one: `null` is cached
124
+ * deliberately (see {@link _loadSiemConfigCached}) and `null` is exactly the state an
125
+ * organization is in the moment before it first configures a sink, so the first five minutes of
126
+ * a newly-pointed SIEM were silently empty.
127
+ *
128
+ * ## Where it is called from, and why not from the config service
129
+ *
130
+ * From a postfix hook on the SIEM-export-config RESOURCE
131
+ * (`siem-export-config-cache-invalidation.custom-impl.backend.service.ts`), not from
132
+ * `AuthOrganizationConfigurationService.saveSiemExportConfig`.
133
+ *
134
+ * That service was the obvious call site and it is the WRONG one: it is only one of the writers.
135
+ * The config is an ordinary resource, so an update also lands through
136
+ * `ServicesRegistryHandler.update` on `organizationSiemExportConfig` directly. Hooking the
137
+ * service would have applied the invariant at one of two call sites, which is the defect shape
138
+ * `.claude/rules/invariant-call-site-enumeration.md` exists to stop. The resource operation is
139
+ * the chokepoint BOTH writers pass through — `saveSiemExportConfig` itself dispatches its write
140
+ * via `ServicesRegistryHandler`, so covering the resource covers it for free.
141
+ *
142
+ * Note `saveSiemExportConfig` separately refreshes its OWN `siemExportConfigs` map. That is a
143
+ * different cache in a different service and was never the one deciding routing.
144
+ *
145
+ * ## Semantics
146
+ *
147
+ * Deleting the entry rather than overwriting it: the next read goes to the repository, so this
148
+ * cannot install a wrong value, only force a re-read. Idempotent, and a no-op for an
149
+ * organization with no entry — which is the common case, since only organizations that have
150
+ * raised an audit event since the last eviction are in the map at all. Synchronous and
151
+ * infallible on purpose, so the caller needs no error handling.
152
+ */
153
+ invalidateSiemConfig(organizationId: string): void;
116
154
  /**
117
155
  * Deliver one event to an org's SIEM webhook sink: apply the sink's per-event filter,
118
156
  * build + HMAC the envelope, and enqueue for async delivery via the SIEM webhook
@@ -1 +1 @@
1
- {"version":3,"file":"audit-event-dispatcher.backend.service.d.ts","sourceRoot":"","sources":["../../../../../../src/compliance/audit-trails/services/audit-event-dispatcher.backend.service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,kBAAkB,CAAC;AAK1B,OAAO,EAAE,aAAa,EAAE,MAAM,8DAA8D,CAAC;AAC7F,OAAO,EAAE,uBAAuB,EAAE,MAAM,sEAAsE,CAAC;AAC/G,OAAO,EAAE,oCAAoC,EAAE,MAAM,2GAA2G,CAAC;AACjK,OAAO,EAAE,yCAAyC,EAAE,MAAM,qGAAqG,CAAC;AAChK,OAAO,EAAE,mBAAmB,EAAE,MAAM,gDAAgD,CAAC;AAErF,OAAO,EAKL,KAAK,0BAA0B,EAG/B,KAAK,cAAc,EACnB,KAAK,oBAAoB,EACzB,aAAa,EACd,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qEAAqE,CAAC;AAoB5G;;;;;;GAMG;AACH,eAAO,MAAM,mBAAmB;;CAEtB,CAAC;AACX,MAAM,MAAM,mBAAmB,GAAG,CAAC,OAAO,mBAAmB,CAAC,CAAC,MAAM,OAAO,mBAAmB,CAAC,CAAC;AAYjG,qBACa,kCAAkC;IASR,OAAO,CAAC,QAAQ,CAAC,MAAM;IACb,OAAO,CAAC,QAAQ,CAAC,gBAAgB;IACb,OAAO,CAAC,QAAQ,CAAC,gBAAgB;IAClD,OAAO,CAAC,QAAQ,CAAC,YAAY;IACd,OAAO,CAAC,QAAQ,CAAC,oBAAoB;IAZtG,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA+D;IAExF,mEAAmE;IACnE,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAyD;IACvF,qGAAqG;IACrG,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAgF;gBAG3D,MAAM,EAAE,aAAa,EACX,gBAAgB,EAAE,uBAAuB,EACrB,gBAAgB,EAAE,oCAAoC,EACvE,YAAY,EAAE,mBAAmB,EAClB,oBAAoB,EAAE,yCAAyC;IAMjJ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAgDG;IACU,gCAAgC,CAC3C,EAAE,EAAE,gBAAgB,CAAC,GAAG,CAAC,EACzB,UAAU,EAAE,IAAI,CAAC,cAAc,EAAE,KAAK,GAAG,WAAW,GAAG,WAAW,CAAC,EACnE,cAAc,EAAE,MAAM,GACrB,OAAO,CAAC,IAAI,CAAC;IAgDhB;;;;;;;;;;;;OAYG;YACW,qBAAqB;IAgBnC;;;;;;;;OAQG;YACW,qBAAqB;IAmBnC;;;;;;OAMG;YACW,sBAAsB;IAyCpC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA+BG;IACU,0BAA0B,CAAC,MAAM,EAAE;QAC9C,QAAQ,EAAE,0BAA0B,CAAC;QACrC,cAAc,EAAE,MAAM,CAAC;QACvB,UAAU,EAAE,oBAAoB,CAAC;QACjC,SAAS,CAAC,EAAE,aAAa,CAAC;QAC1B,OAAO,CAAC,EAAE,MAAM,CAAC;KAClB,GAAG,OAAO,CAAC,IAAI,CAAC;IAgCjB;;OAEG;IACH,OAAO,CAAC,kBAAkB;IA6B1B;;OAEG;IACH,OAAO,CAAC,cAAc;IA+CtB;;OAEG;IACH,OAAO,CAAC,aAAa;IAkBrB;;;;OAIG;YACW,eAAe;IAiC7B;;;OAGG;IACH,OAAO,CAAC,oBAAoB;IAO5B;;;OAGG;IACH,OAAO,CAAC,WAAW;IAMnB;;;OAGG;IACI,kBAAkB,CAAC,QAAQ,EAAE,0BAA0B,EAAE,YAAY,EAAE,MAAM,GAAG,OAAO;CAQ/F"}
1
+ {"version":3,"file":"audit-event-dispatcher.backend.service.d.ts","sourceRoot":"","sources":["../../../../../../src/compliance/audit-trails/services/audit-event-dispatcher.backend.service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,kBAAkB,CAAC;AAK1B,OAAO,EAAE,aAAa,EAAE,MAAM,8DAA8D,CAAC;AAC7F,OAAO,EAAE,uBAAuB,EAAE,MAAM,sEAAsE,CAAC;AAC/G,OAAO,EAAE,oCAAoC,EAAE,MAAM,2GAA2G,CAAC;AACjK,OAAO,EAAE,yCAAyC,EAAE,MAAM,qGAAqG,CAAC;AAChK,OAAO,EAAE,mBAAmB,EAAE,MAAM,gDAAgD,CAAC;AAErF,OAAO,EAKL,KAAK,0BAA0B,EAG/B,KAAK,cAAc,EACnB,KAAK,oBAAoB,EACzB,aAAa,EACd,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qEAAqE,CAAC;AAoB5G;;;;;;GAMG;AACH,eAAO,MAAM,mBAAmB;;CAEtB,CAAC;AACX,MAAM,MAAM,mBAAmB,GAAG,CAAC,OAAO,mBAAmB,CAAC,CAAC,MAAM,OAAO,mBAAmB,CAAC,CAAC;AAYjG,qBACa,kCAAkC;IASR,OAAO,CAAC,QAAQ,CAAC,MAAM;IACb,OAAO,CAAC,QAAQ,CAAC,gBAAgB;IACb,OAAO,CAAC,QAAQ,CAAC,gBAAgB;IAClD,OAAO,CAAC,QAAQ,CAAC,YAAY;IACd,OAAO,CAAC,QAAQ,CAAC,oBAAoB;IAZtG,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA+D;IAExF,mEAAmE;IACnE,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAyD;IACvF,qGAAqG;IACrG,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAgF;gBAG3D,MAAM,EAAE,aAAa,EACX,gBAAgB,EAAE,uBAAuB,EACrB,gBAAgB,EAAE,oCAAoC,EACvE,YAAY,EAAE,mBAAmB,EAClB,oBAAoB,EAAE,yCAAyC;IAMjJ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAgDG;IACU,gCAAgC,CAC3C,EAAE,EAAE,gBAAgB,CAAC,GAAG,CAAC,EACzB,UAAU,EAAE,IAAI,CAAC,cAAc,EAAE,KAAK,GAAG,WAAW,GAAG,WAAW,CAAC,EACnE,cAAc,EAAE,MAAM,GACrB,OAAO,CAAC,IAAI,CAAC;IAgDhB;;;;;;;;;;;;OAYG;YACW,qBAAqB;IAgBnC;;;;;;;;OAQG;YACW,qBAAqB;IAmBnC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAoCG;IACI,oBAAoB,CAAC,cAAc,EAAE,MAAM,GAAG,IAAI;IAIzD;;;;;;OAMG;YACW,sBAAsB;IAyCpC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA+BG;IACU,0BAA0B,CAAC,MAAM,EAAE;QAC9C,QAAQ,EAAE,0BAA0B,CAAC;QACrC,cAAc,EAAE,MAAM,CAAC;QACvB,UAAU,EAAE,oBAAoB,CAAC;QACjC,SAAS,CAAC,EAAE,aAAa,CAAC;QAC1B,OAAO,CAAC,EAAE,MAAM,CAAC;KAClB,GAAG,OAAO,CAAC,IAAI,CAAC;IAgCjB;;OAEG;IACH,OAAO,CAAC,kBAAkB;IA6B1B;;OAEG;IACH,OAAO,CAAC,cAAc;IA+CtB;;OAEG;IACH,OAAO,CAAC,aAAa;IAkBrB;;;;OAIG;YACW,eAAe;IAiC7B;;;OAGG;IACH,OAAO,CAAC,oBAAoB;IAO5B;;;OAGG;IACH,OAAO,CAAC,WAAW;IAMnB;;;OAGG;IACI,kBAAkB,CAAC,QAAQ,EAAE,0BAA0B,EAAE,YAAY,EAAE,MAAM,GAAG,OAAO;CAQ/F"}
@@ -219,6 +219,46 @@ let AuditEventDispatcherBackendService = class AuditEventDispatcherBackendServic
219
219
  this._siemConfigCache.set(organizationId, { config, cachedAt: Date.now() });
220
220
  return config;
221
221
  }
222
+ /**
223
+ * Drop one organization's cached SIEM config so the NEXT event re-reads it.
224
+ *
225
+ * ## Why this exists
226
+ *
227
+ * Without it the cache had no invalidation at all — only the 5-minute TTL and the size-1000
228
+ * FIFO — so enabling a sink left the organization's events unrouted for up to that window, and
229
+ * audit events are not replayed. The ENABLE direction is the sharp one: `null` is cached
230
+ * deliberately (see {@link _loadSiemConfigCached}) and `null` is exactly the state an
231
+ * organization is in the moment before it first configures a sink, so the first five minutes of
232
+ * a newly-pointed SIEM were silently empty.
233
+ *
234
+ * ## Where it is called from, and why not from the config service
235
+ *
236
+ * From a postfix hook on the SIEM-export-config RESOURCE
237
+ * (`siem-export-config-cache-invalidation.custom-impl.backend.service.ts`), not from
238
+ * `AuthOrganizationConfigurationService.saveSiemExportConfig`.
239
+ *
240
+ * That service was the obvious call site and it is the WRONG one: it is only one of the writers.
241
+ * The config is an ordinary resource, so an update also lands through
242
+ * `ServicesRegistryHandler.update` on `organizationSiemExportConfig` directly. Hooking the
243
+ * service would have applied the invariant at one of two call sites, which is the defect shape
244
+ * `.claude/rules/invariant-call-site-enumeration.md` exists to stop. The resource operation is
245
+ * the chokepoint BOTH writers pass through — `saveSiemExportConfig` itself dispatches its write
246
+ * via `ServicesRegistryHandler`, so covering the resource covers it for free.
247
+ *
248
+ * Note `saveSiemExportConfig` separately refreshes its OWN `siemExportConfigs` map. That is a
249
+ * different cache in a different service and was never the one deciding routing.
250
+ *
251
+ * ## Semantics
252
+ *
253
+ * Deleting the entry rather than overwriting it: the next read goes to the repository, so this
254
+ * cannot install a wrong value, only force a re-read. Idempotent, and a no-op for an
255
+ * organization with no entry — which is the common case, since only organizations that have
256
+ * raised an audit event since the last eviction are in the map at all. Synchronous and
257
+ * infallible on purpose, so the caller needs no error handling.
258
+ */
259
+ invalidateSiemConfig(organizationId) {
260
+ this._siemConfigCache.delete(organizationId);
261
+ }
222
262
  /**
223
263
  * Deliver one event to an org's SIEM webhook sink: apply the sink's per-event filter,
224
264
  * build + HMAC the envelope, and enqueue for async delivery via the SIEM webhook
@@ -1 +1 @@
1
- {"version":3,"file":"audit-event-dispatcher.backend.service.js","sourceRoot":"","sources":["../../../../../../src/compliance/audit-trails/services/audit-event-dispatcher.backend.service.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Audit Event Dispatcher Backend Service\n *\n * Layer 2 of the three-layer audit event pipeline (Step 18).\n * Receives audit events after MongoDB write and dispatches them\n * to the organization's SIEM endpoint if configured.\n *\n * This is fire-and-forget — if dispatch fails, the audit log\n * in MongoDB remains authoritative.\n */\n\nimport 'reflect-metadata';\nimport { inject, injectable } from 'inversify';\nimport { v4 as uuidv4 } from 'uuid';\nimport { createHmac, createHash, timingSafeEqual } from 'crypto';\nimport { SAAS_SERVICE_TYPES } from '~wildo-backend-lib/inject/models/service-types';\nimport { LoggerService } from '~wildo-backend-lib/monitoring/logger/services/logger.service';\nimport { AppConfigurationService } from '~wildo-backend-lib/config/services/app-configuration.backend.service';\nimport { AuthOrganizationConfigurationService } from '~wildo-backend-lib/security/authentications/services/admin-config/auth-organization-configuration.service';\nimport { RepositoriesRegistryHandlerBackendService } from '~wildo-backend-lib/base-classes/services/repositories/repositories-registry-handler.backend.service';\nimport { QueueBackendService } from '~wildo-backend-lib/queue/queue.backend.service';\nimport { SIEM_WEBHOOK_DELIVERY_BATCH_REF } from '~wildo-backend-lib/base-classes/services/batches/engine-custom-batch-manifests';\nimport {\n AuditEventOutcome,\n CoreAuditableEventType,\n CoreResourceType,\n getAuditEventClassification,\n type SecurityAuditEventEnvelope,\n type AuditEventCategory,\n type AuditEventSeverity,\n type AuditableEvent,\n type SiemExportConfigBase,\n AuthScopeType,\n} from '@wildo-ai/saas-models';\nimport type { ExecutionContext } from '~wildo-backend-lib/runtime/models/execution-context.backend.schemas';\n\n/** F6: HMAC domain separator for event integrity */\nconst SIEM_HMAC_DOMAIN = 'wildo:siem:event-integrity:v1';\n/** F3: Org name cache TTL (5 minutes) */\nconst ORG_NAME_CACHE_TTL_MS = 5 * 60 * 1000;\n/** B4: Max entries in org name cache to prevent unbounded growth */\nconst ORG_NAME_CACHE_MAX_SIZE = 1000;\n/** R2: SIEM-config cache TTL (5 min) — the config is otherwise read (via an audited, self-elevating `readAsSystem`) per event per org. */\nconst SIEM_CONFIG_CACHE_TTL_MS = 5 * 60 * 1000;\n/** R2: Max entries in the SIEM-config cache to prevent unbounded growth. */\nconst SIEM_CONFIG_CACHE_MAX_SIZE = 1000;\n\n/** Severity ordering for minimum severity filtering */\nconst SEVERITY_ORDER: Record<string, number> = {\n 'info': 0,\n 'warning': 1,\n 'critical': 2,\n};\n\n/**\n * The per-org audit sink TYPES an organization can route its authenticated audit\n * events to. Today the only type is the SIEM webhook. Adding a per-org destination\n * later (e.g. per-org immudb, per observability §11) is a new member HERE + a branch\n * in `_resolveOrgAuditSinks` + a `case` in `dispatchToOrganizationAuditSinks` — the F4\n * routing (org from the authenticated EC) and the envelope/HMAC contracts do not change.\n */\nexport const PerOrgAuditSinkType = {\n SIEM_WEBHOOK: 'siem_webhook',\n} as const;\nexport type PerOrgAuditSinkType = (typeof PerOrgAuditSinkType)[keyof typeof PerOrgAuditSinkType];\n\n/**\n * A per-org audit sink RESOLVED for a specific org, carrying exactly the config needed\n * to deliver one event to it. A discriminated union (one member today) — the dispatcher\n * fans out by `type`, so a new sink type extends this union rather than reshaping callers.\n */\ntype ResolvedOrgAuditSink = {\n type: typeof PerOrgAuditSinkType.SIEM_WEBHOOK;\n siemConfig: SiemExportConfigBase;\n};\n\n@injectable()\nexport class AuditEventDispatcherBackendService {\n private readonly logDebug: (message: string, context?: Record<string, unknown>) => void;\n\n /** F3: Lightweight org name cache to avoid per-event DB lookups */\n private readonly _orgNameCache = new Map<string, { name: string; cachedAt: number }>();\n /** R2: per-org SIEM-config cache — avoids a per-event per-org audited `readAsSystem` config load. */\n private readonly _siemConfigCache = new Map<string, { config: SiemExportConfigBase | null; cachedAt: number }>();\n\n constructor(\n @inject(SAAS_SERVICE_TYPES.Logger) private readonly logger: LoggerService,\n @inject(SAAS_SERVICE_TYPES.AppConfigService) private readonly appConfigService: AppConfigurationService,\n @inject(SAAS_SERVICE_TYPES.AuthOrganizationConfigurationService) private readonly orgConfigService: AuthOrganizationConfigurationService,\n @inject(SAAS_SERVICE_TYPES.QueueBackendService) private readonly queueService: QueueBackendService,\n @inject(SAAS_SERVICE_TYPES.RepositoriesRegistryHandlerService) private readonly repositoriesRegistry: RepositoriesRegistryHandlerBackendService,\n ) {\n this.logDebug = this.logger.debugByType('AuditEventDispatcherBackendService');\n this.logger.setDebugTypeLogging('AuditEventDispatcherBackendService', false);\n }\n\n /**\n * Dispatch an audit event to the organization's configured per-org audit SINKS.\n *\n * F4 (tenant segregation): the caller (`AuditLogsBackendService`) passes the\n * AUTHENTICATED org (resolved from the request `ExecutionContext`), NEVER the\n * caller-supplied `auditEvent.organizationId`. Sink config is loaded for THIS org\n * only (`_resolveOrgAuditSinks`), so an event can only reach the sinks of the tenant\n * it is authenticated for.\n *\n * Layer 2 of the pipeline: called after the MongoDB write (Layer 1), wired directly\n * into `AuditLogsBackendService` (NOT a resource pipeline post-hook). Fire-and-forget\n * — a resolution or per-sink delivery failure is logged but NEVER thrown onto the\n * business op, and per-sink failures are ISOLATED (F7) so one destination never\n * suppresses another.\n *\n * The seam: `_resolveOrgAuditSinks` returns the 0..N sinks the org has configured\n * (today only a SIEM webhook); this method fans each out by its `type`. Adding a\n * per-org destination later = a new {@link PerOrgAuditSinkType} + a resolver branch +\n * a `case` here.\n *\n * ── APPLICATION scope is RESERVED / DORMANT — there is NO app-scoped producer ──────\n * This producer is ORGANIZATION-scoped ONLY, on purpose. The F4 model routes on a\n * TRUSTED authenticated *tenant* (org), and the sole caller (`AuditLogsBackendService`)\n * fans out over the event's verified member orgs. There is deliberately no\n * `dispatchToApplicationAuditSinks` / `_resolveAppAuditSinks`.\n *\n * The SIEM export-config and delivery-DLQ resources DO each carry an `APPLICATION`\n * polymorphic variant (uniform with SSO/SCIM over `AUTH_SCOPE_VARIANTS`), and every\n * downstream seam is already scope-generic: `loadSiemExportConfig(ec, APPLICATION, appId)`,\n * {@link enqueueSiemWebhookDelivery} (`scopeType`/`scopeId`), the delivery worker's\n * circuit-breaker key, and `_enqueueToDLQ`'s `APPLICATION_SIEM_DELIVERY_DLQ` branch all\n * accept the app scope. But NOTHING emits a first-time app-scoped delivery. The only\n * caller that passes `scopeType: APPLICATION` today is the SIEM-DLQ *retry* custom-impl,\n * which is itself unreachable in production: an `APPLICATION_SIEM_DELIVERY_DLQ` row can\n * only be born from a FAILED app-scoped delivery — and none is ever produced — so the\n * app config-read, app DLQ, and app retry are mutually reachable only through each other.\n * NOTE the operator-visible trap this leaves: an app super-admin CAN enable\n * `APPLICATION_SIEM_EXPORT_CONFIG` (its own custom-impl + APP_ADMIN_SUPER_ADMIN roles)\n * and it will silently deliver NOTHING until a producer exists.\n *\n * To make it real — a SCOPED feature, NOT a mechanical mirror; settle the design fork\n * FIRST (which events an app SIEM receives, whether spanning all tenants, and how it\n * composes with per-org delivery so an event is not double-sent) — add a sibling\n * `_resolveAppAuditSinks(ec, appId)` loading the APP config, a\n * `dispatchToApplicationAuditSinks` fanning out identically, and a producer that calls it\n * with an app context, reusing the SAME envelope/HMAC/scope contract via\n * `enqueueSiemWebhookDelivery({ scopeType: AuthScopeType.APPLICATION, scopeId })`. Until\n * then, treat the APP variant as reserved plumbing, not a wired path.\n */\n public async dispatchToOrganizationAuditSinks(\n ec: ExecutionContext<any>,\n auditEvent: Omit<AuditableEvent, '_id' | 'createdAt' | 'updatedAt'>,\n organizationId: string,\n ): Promise<void> {\n // No authenticated org → no per-org dispatch (fail-closed, F4).\n if (!organizationId) return;\n\n let sinks: ResolvedOrgAuditSink[];\n try {\n sinks = await this._resolveOrgAuditSinks(ec, organizationId);\n } catch (error) {\n // Fire-and-forget: a config-resolution failure must never throw onto the business op.\n this.logger.error?.('Failed to resolve org audit sinks', {\n error,\n eventType: auditEvent.eventType,\n organizationId,\n });\n return;\n }\n\n for (const sink of sinks) {\n try {\n switch (sink.type) {\n case PerOrgAuditSinkType.SIEM_WEBHOOK:\n await this._dispatchToSiemWebhook(ec, auditEvent, organizationId, sink.siemConfig);\n break;\n default:\n // F-1 safety net: a future `PerOrgAuditSinkType` member with no `case` above is\n // LOGGED, never silently dropped (the original finding was a silent no-op). A\n // single-member discriminated union does NOT narrow this `default` to `never`, so\n // a compile-time `assertNever(sink)` guard only becomes possible once a 2nd sink\n // type exists — convert this branch to it then. Until then the resolver's +\n // switch's JSDoc pointers (\"add a case here\") + this warn are the guard.\n this.logger.warn?.(\n 'Unhandled per-org audit sink type — event NOT delivered to this sink; add a `case` in dispatchToOrganizationAuditSinks',\n { sinkType: sink.type, eventType: auditEvent.eventType, organizationId },\n );\n break;\n }\n } catch (error) {\n // Per-sink isolation (F7): one org sink failing never affects the others.\n this.logger.error?.('Failed to dispatch audit event to an org audit sink', {\n error,\n sinkType: sink.type,\n eventType: auditEvent.eventType,\n organizationId,\n });\n }\n }\n }\n\n /**\n * Resolve the per-org audit SINKS an org has configured — the 0..N destinations\n * {@link dispatchToOrganizationAuditSinks} fans an event out to. Keyed on the org the\n * caller already validated as authenticated (F4). Today the only per-org sink type is\n * the SIEM webhook; a future per-org destination appends its own descriptor HERE.\n *\n * Two-level feature gate for `CoreFeature.SECURITY_EVENT_EXPORT`:\n * Level 1 (here): `siemConfig.enabled` — the runtime gate.\n * Level 2 (admin API): `FeatureGateService` checks the org's subscription tier has\n * `SECURITY_EVENT_EXPORT` before allowing `enabled = true`. We cannot use\n * `FeatureResolutionService` here (fire-and-forget context; internal ECs bypass\n * feature resolution), so the pre-validated `enabled` flag IS the per-org gate.\n */\n private async _resolveOrgAuditSinks(\n ec: ExecutionContext<any>,\n organizationId: string,\n ): Promise<ResolvedOrgAuditSink[]> {\n const sinks: ResolvedOrgAuditSink[] = [];\n\n const siemConfig = await this._loadSiemConfigCached(ec, organizationId);\n if (siemConfig?.enabled) {\n sinks.push({ type: PerOrgAuditSinkType.SIEM_WEBHOOK, siemConfig });\n }\n\n // Future per-org sink types (e.g. per-org immudb) resolve here — each appends its\n // own descriptor; the dispatcher switch fans out by `type`.\n return sinks;\n }\n\n /**\n * R2: load the org's SIEM config through a short-TTL cache. Without it,\n * {@link _resolveOrgAuditSinks} reads the config via a self-elevating `readAsSystem` PER\n * EVENT PER ORG — and `readAsSystem` ALSO emits a `SYSTEM_CONTEXT_READ` audit — so every\n * routed audit event (× N orgs on the multi-org fan-out) doubled the audit-write volume to\n * the DB + immudb. Mirrors {@link _orgNameCache}: an enable/disable/rename takes effect within\n * the TTL, acceptable for a rarely-changed config. `null` is cached too (an org with no SIEM\n * config) so a non-configured org is not re-read every event.\n */\n private async _loadSiemConfigCached(\n ec: ExecutionContext<any>,\n organizationId: string,\n ): Promise<SiemExportConfigBase | null> {\n const cached = this._siemConfigCache.get(organizationId);\n if (cached && Date.now() - cached.cachedAt < SIEM_CONFIG_CACHE_TTL_MS) {\n return cached.config;\n }\n const config = await this.orgConfigService.loadSiemExportConfig(ec, AuthScopeType.ORGANIZATION, organizationId);\n if (this._siemConfigCache.size >= SIEM_CONFIG_CACHE_MAX_SIZE) {\n const oldestKey = this._siemConfigCache.keys().next().value;\n if (oldestKey !== undefined) {\n this._siemConfigCache.delete(oldestKey);\n }\n }\n this._siemConfigCache.set(organizationId, { config, cachedAt: Date.now() });\n return config;\n }\n\n /**\n * Deliver one event to an org's SIEM webhook sink: apply the sink's per-event filter,\n * build + HMAC the envelope, and enqueue for async delivery via the SIEM webhook\n * delivery worker (`siemWebhookDeliveryBatchService`, registered under\n * `SIEM_WEBHOOK_DELIVERY_BATCH_REF` in `start-application`). May throw; the caller\n * isolates per-sink failures.\n */\n private async _dispatchToSiemWebhook(\n ec: ExecutionContext<any>,\n auditEvent: Omit<AuditableEvent, '_id' | 'createdAt' | 'updatedAt'>,\n organizationId: string,\n siemConfig: SiemExportConfigBase,\n ): Promise<void> {\n // Classify the event (always returns a value; defaults to data_access/info for unknown types)\n const classification = getAuditEventClassification(auditEvent.eventType);\n\n // Apply the sink's per-event filter\n if (!this._passesEventFilter(auditEvent.eventType, classification, siemConfig)) {\n this.logDebug('Event filtered out by SIEM config', {\n eventType: auditEvent.eventType,\n organizationId,\n });\n return;\n }\n\n // F3: Resolve org name for SIEM analyst readability\n const orgName = await this._resolveOrgName(ec, organizationId);\n\n // Extract application context from eventData if available\n const appId = (auditEvent.eventData as Record<string, unknown>)?.applicationId as string | undefined;\n const appName = (auditEvent.eventData as Record<string, unknown>)?.applicationName as string | undefined;\n\n // Build the envelope (F3: includes orgName, Task 3.12c: includes app context)\n const envelope = this._buildEnvelope(auditEvent, organizationId, classification, orgName, appId, appName);\n\n // Enqueue for async delivery through the single job-contract seam\n // (payload shape + F6 envelope HMAC + batch ref). This dispatch path is\n // org-scoped; {@link enqueueSiemWebhookDelivery} defaults scopeType/scopeId to\n // the organization, so behaviour is unchanged.\n await this.enqueueSiemWebhookDelivery({ envelope, organizationId, siemConfig });\n\n this.logDebug('Audit event enqueued for SIEM delivery', {\n eventId: envelope.eventId,\n eventType: auditEvent.eventType,\n organizationId,\n });\n }\n\n /**\n * Enqueue ONE already-built envelope for async SIEM webhook delivery.\n *\n * The SINGLE authority for the SIEM delivery-job contract — the\n * {@link SIEMDeliveryJobPayload} shape, the F6 envelope HMAC, and the\n * `SIEM_WEBHOOK_DELIVERY_BATCH_REF` batch ref — shared by two producers:\n * 1. first-time dispatch ({@link _dispatchToSiemWebhook}, org scope), and\n * 2. manual DLQ drain (the `siem-delivery-dlq` RETRY_SINGLE / RETRY_ALL\n * custom impls), which re-enqueue a dead-lettered row's STORED envelope\n * for either the ORG or APP scope.\n *\n * Keeping this in one place is not cosmetic: the delivery worker has no\n * back-reference to a DLQ row, so a retry that fails is re-captured as a\n * brand-new DLQ row. If the retry path built a subtly different payload (a\n * dropped `siemConfig` field, a stale HMAC domain), the mismatch would surface\n * only as a silently looping dead-letter — never a compile error. One seam\n * makes that class of drift impossible.\n *\n * @param params.scopeType Defaults to {@link AuthScopeType.ORGANIZATION} (the\n * first-time dispatch path is org-only). The DLQ APP variant passes\n * {@link AuthScopeType.APPLICATION} so the worker's circuit-breaker key and\n * any re-DLQ capture resolve to the application scope, not the org. NOTE: the\n * SIEM-DLQ retry custom-impl is the ONLY caller that passes `APPLICATION` today —\n * there is NO first-time app-scoped producer (the APPLICATION variant is reserved /\n * dormant; see {@link dispatchToOrganizationAuditSinks} for the full rationale and\n * the wiring a future app producer would add).\n * @param params.scopeId Defaults to `organizationId`.\n *\n * May throw on enqueue failure — the CALLER decides isolation: the dispatch\n * path swallows it per-sink (fire-and-forget, F7); the DLQ retry surfaces it to\n * the operator so the row is left intact for another attempt.\n */\n public async enqueueSiemWebhookDelivery(params: {\n envelope: SecurityAuditEventEnvelope;\n organizationId: string;\n siemConfig: SiemExportConfigBase;\n scopeType?: AuthScopeType;\n scopeId?: string;\n }): Promise<void> {\n const { envelope, organizationId, siemConfig } = params;\n const scopeType = params.scopeType ?? AuthScopeType.ORGANIZATION;\n\n // F6: HMAC integrity signature over the envelope payload — computed HERE so the\n // key/domain never diverges between the dispatch and retry producers.\n const envelopeHmac = this._computeEnvelopeHmac(envelope);\n\n // Using the shared constant keeps enqueue ↔ registration ↔ spec cross-validation\n // on one literal. The `siemConfig` projection strips the config to the transport\n // fields the worker needs (no eventFilter — filtering already happened upstream).\n await this.queueService.enqueueCustomBatch(\n SIEM_WEBHOOK_DELIVERY_BATCH_REF,\n {\n envelope,\n envelopeHmac,\n organizationId,\n scopeType,\n scopeId: params.scopeId ?? organizationId,\n siemConfig: {\n webhookUrl: siemConfig.webhookUrl,\n authMethod: siemConfig.authMethod,\n authCredential: siemConfig.authCredential,\n authHeaderName: siemConfig.authHeaderName,\n eventFormat: siemConfig.eventFormat,\n customHeaders: siemConfig.customHeaders,\n delivery: siemConfig.delivery,\n },\n },\n );\n }\n\n /**\n * Check if an event passes the org's SIEM event filter.\n */\n private _passesEventFilter(\n eventType: string,\n classification: { category: AuditEventCategory; severity: AuditEventSeverity },\n config: SiemExportConfigBase,\n ): boolean {\n const filter = config.eventFilter;\n\n // Check category filter\n if (!filter.categories.includes(classification.category)) {\n return false;\n }\n\n // Check severity minimum\n const eventSeverityOrder = SEVERITY_ORDER[classification.severity] ?? 0;\n const minimumSeverityOrder = SEVERITY_ORDER[filter.severityMinimum] ?? 0;\n if (eventSeverityOrder < minimumSeverityOrder) {\n return false;\n }\n\n // Check specific event type filter (if configured)\n if (filter.specificEventTypes && filter.specificEventTypes.length > 0) {\n if (!filter.specificEventTypes.includes(eventType)) {\n return false;\n }\n }\n\n return true;\n }\n\n /**\n * Build a SecurityAuditEventEnvelope from an audit event.\n */\n private _buildEnvelope(\n auditEvent: Omit<AuditableEvent, '_id' | 'createdAt' | 'updatedAt'>,\n organizationId: string,\n classification: { category: AuditEventCategory; severity: AuditEventSeverity },\n organizationName?: string,\n applicationId?: string,\n applicationName?: string,\n ): SecurityAuditEventEnvelope {\n const eventData = auditEvent.eventData as Record<string, unknown>;\n\n return {\n eventId: uuidv4(),\n eventType: auditEvent.eventType,\n eventCategory: classification.category,\n severity: classification.severity,\n timestamp: new Date().toISOString(),\n correlationId: auditEvent.correlationId,\n // Undefined unless the application opted in — the row itself is only ever populated under the policy.\n clientInstanceId: auditEvent.clientInstanceId,\n frontendServiceName: auditEvent.frontendServiceName,\n\n organizationId,\n organizationName, // F3: Populated via org name cache\n applicationId,\n applicationName,\n userId: auditEvent.userId,\n userEmail: (eventData.email as string) || undefined,\n\n sourceService: this.appConfigService.applicationId,\n sourceVersion: process.env.APP_VERSION || '0.0.0',\n sourceEnvironment: this.appConfigService.applicationEnv,\n\n // Auth-specific context from eventData (when applicable)\n authMethod: (eventData.authMethod as string) || undefined,\n authSource: (eventData.authSource as string) || undefined,\n providerRef: (eventData.providerRef as string) || undefined,\n connectionType: (eventData.connectionType as string) || undefined,\n ipAddress: (eventData.ipAddress as string) || undefined,\n userAgent: (eventData.userAgent as string) || undefined,\n\n eventData: eventData as Record<string, any>,\n\n outcome: this._inferOutcome(auditEvent.eventType),\n outcomeReason: (eventData.failureReason as string) || (eventData.reason as string) || undefined,\n };\n }\n\n /**\n * Infer success/failure outcome from event type.\n */\n private _inferOutcome(eventType: string): AuditEventOutcome {\n const failureEventTypes = [\n CoreAuditableEventType.USER_LOGIN_FAILED,\n CoreAuditableEventType.USER_ACCOUNT_LOCKED,\n CoreAuditableEventType.USER_SSO_LOGIN_FAILED,\n CoreAuditableEventType.USER_SSO_ENFORCEMENT_BLOCKED,\n CoreAuditableEventType.SAML_LOGIN_FAILED,\n CoreAuditableEventType.SAML_ASSERTION_REPLAY_DETECTED,\n CoreAuditableEventType.MACHINE_AUTH_FAILED,\n ];\n\n return failureEventTypes.includes(eventType as CoreAuditableEventType) ? AuditEventOutcome.FAILURE : AuditEventOutcome.SUCCESS;\n }\n\n // ─────────────────────────────────────────────────────────────\n // F3: Organization Name Cache\n // ─────────────────────────────────────────────────────────────\n\n /**\n * F3: Resolve organization name with a lightweight in-memory cache.\n * Cache entries expire after ORG_NAME_CACHE_TTL_MS to pick up renames.\n * Returns undefined if lookup fails (fire-and-forget context — don't throw).\n */\n private async _resolveOrgName(ec: ExecutionContext<any>, organizationId: string): Promise<string | undefined> {\n // Check cache first\n const cached = this._orgNameCache.get(organizationId);\n if (cached && (Date.now() - cached.cachedAt) < ORG_NAME_CACHE_TTL_MS) {\n return cached.name;\n }\n\n try {\n const orgsRepo = this.repositoriesRegistry.getRepositoryOrThrow(CoreResourceType.ORGANIZATIONS);\n const org = await orgsRepo.read<{ _id: string; name: string }>(ec, organizationId);\n\n if (org?.name) {\n // B4: Evict oldest entries when cache exceeds max size (simple LRU approximation)\n if (this._orgNameCache.size >= ORG_NAME_CACHE_MAX_SIZE) {\n const oldestKey = this._orgNameCache.keys().next().value;\n if (oldestKey !== undefined) {\n this._orgNameCache.delete(oldestKey);\n }\n }\n this._orgNameCache.set(organizationId, { name: org.name, cachedAt: Date.now() });\n return org.name;\n }\n } catch {\n // Fire-and-forget — org name is optional in the envelope\n }\n\n return undefined;\n }\n\n // ─────────────────────────────────────────────────────────────\n // F6: Event Integrity (HMAC)\n // ─────────────────────────────────────────────────────────────\n\n /**\n * F6: Compute HMAC-SHA256 over the serialized envelope for integrity protection.\n * The delivery service verifies this HMAC before processing, detecting queue tampering.\n */\n private _computeEnvelopeHmac(envelope: SecurityAuditEventEnvelope): string {\n const hmacKey = this._getHmacKey();\n return createHmac('sha256', hmacKey)\n .update(JSON.stringify(envelope))\n .digest('hex');\n }\n\n /**\n * Derive a 256-bit HMAC key from the app's session encryption key\n * using SHA-256 with a domain separator (consistent with SIEM encryption key derivation).\n */\n private _getHmacKey(): Buffer {\n return createHash('sha256')\n .update(`${SIEM_HMAC_DOMAIN}:${this.appConfigService.secrets.security.session.encryptionKey}`)\n .digest();\n }\n\n /**\n * F6: Public static-like method to verify envelope HMAC on the delivery side.\n * Returns true if the HMAC matches, false otherwise.\n */\n public verifyEnvelopeHmac(envelope: SecurityAuditEventEnvelope, expectedHmac: string): boolean {\n const computedHmac = this._computeEnvelopeHmac(envelope);\n // Use crypto.timingSafeEqual for constant-time comparison (prevents timing attacks)\n const computedBuf = Buffer.from(computedHmac, 'utf-8');\n const expectedBuf = Buffer.from(expectedHmac, 'utf-8');\n if (computedBuf.length !== expectedBuf.length) return false;\n return timingSafeEqual(computedBuf, expectedBuf);\n }\n}\n"]}
1
+ {"version":3,"file":"audit-event-dispatcher.backend.service.js","sourceRoot":"","sources":["../../../../../../src/compliance/audit-trails/services/audit-event-dispatcher.backend.service.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Audit Event Dispatcher Backend Service\n *\n * Layer 2 of the three-layer audit event pipeline (Step 18).\n * Receives audit events after MongoDB write and dispatches them\n * to the organization's SIEM endpoint if configured.\n *\n * This is fire-and-forget — if dispatch fails, the audit log\n * in MongoDB remains authoritative.\n */\n\nimport 'reflect-metadata';\nimport { inject, injectable } from 'inversify';\nimport { v4 as uuidv4 } from 'uuid';\nimport { createHmac, createHash, timingSafeEqual } from 'crypto';\nimport { SAAS_SERVICE_TYPES } from '~wildo-backend-lib/inject/models/service-types';\nimport { LoggerService } from '~wildo-backend-lib/monitoring/logger/services/logger.service';\nimport { AppConfigurationService } from '~wildo-backend-lib/config/services/app-configuration.backend.service';\nimport { AuthOrganizationConfigurationService } from '~wildo-backend-lib/security/authentications/services/admin-config/auth-organization-configuration.service';\nimport { RepositoriesRegistryHandlerBackendService } from '~wildo-backend-lib/base-classes/services/repositories/repositories-registry-handler.backend.service';\nimport { QueueBackendService } from '~wildo-backend-lib/queue/queue.backend.service';\nimport { SIEM_WEBHOOK_DELIVERY_BATCH_REF } from '~wildo-backend-lib/base-classes/services/batches/engine-custom-batch-manifests';\nimport {\n AuditEventOutcome,\n CoreAuditableEventType,\n CoreResourceType,\n getAuditEventClassification,\n type SecurityAuditEventEnvelope,\n type AuditEventCategory,\n type AuditEventSeverity,\n type AuditableEvent,\n type SiemExportConfigBase,\n AuthScopeType,\n} from '@wildo-ai/saas-models';\nimport type { ExecutionContext } from '~wildo-backend-lib/runtime/models/execution-context.backend.schemas';\n\n/** F6: HMAC domain separator for event integrity */\nconst SIEM_HMAC_DOMAIN = 'wildo:siem:event-integrity:v1';\n/** F3: Org name cache TTL (5 minutes) */\nconst ORG_NAME_CACHE_TTL_MS = 5 * 60 * 1000;\n/** B4: Max entries in org name cache to prevent unbounded growth */\nconst ORG_NAME_CACHE_MAX_SIZE = 1000;\n/** R2: SIEM-config cache TTL (5 min) — the config is otherwise read (via an audited, self-elevating `readAsSystem`) per event per org. */\nconst SIEM_CONFIG_CACHE_TTL_MS = 5 * 60 * 1000;\n/** R2: Max entries in the SIEM-config cache to prevent unbounded growth. */\nconst SIEM_CONFIG_CACHE_MAX_SIZE = 1000;\n\n/** Severity ordering for minimum severity filtering */\nconst SEVERITY_ORDER: Record<string, number> = {\n 'info': 0,\n 'warning': 1,\n 'critical': 2,\n};\n\n/**\n * The per-org audit sink TYPES an organization can route its authenticated audit\n * events to. Today the only type is the SIEM webhook. Adding a per-org destination\n * later (e.g. per-org immudb, per observability §11) is a new member HERE + a branch\n * in `_resolveOrgAuditSinks` + a `case` in `dispatchToOrganizationAuditSinks` — the F4\n * routing (org from the authenticated EC) and the envelope/HMAC contracts do not change.\n */\nexport const PerOrgAuditSinkType = {\n SIEM_WEBHOOK: 'siem_webhook',\n} as const;\nexport type PerOrgAuditSinkType = (typeof PerOrgAuditSinkType)[keyof typeof PerOrgAuditSinkType];\n\n/**\n * A per-org audit sink RESOLVED for a specific org, carrying exactly the config needed\n * to deliver one event to it. A discriminated union (one member today) — the dispatcher\n * fans out by `type`, so a new sink type extends this union rather than reshaping callers.\n */\ntype ResolvedOrgAuditSink = {\n type: typeof PerOrgAuditSinkType.SIEM_WEBHOOK;\n siemConfig: SiemExportConfigBase;\n};\n\n@injectable()\nexport class AuditEventDispatcherBackendService {\n private readonly logDebug: (message: string, context?: Record<string, unknown>) => void;\n\n /** F3: Lightweight org name cache to avoid per-event DB lookups */\n private readonly _orgNameCache = new Map<string, { name: string; cachedAt: number }>();\n /** R2: per-org SIEM-config cache — avoids a per-event per-org audited `readAsSystem` config load. */\n private readonly _siemConfigCache = new Map<string, { config: SiemExportConfigBase | null; cachedAt: number }>();\n\n constructor(\n @inject(SAAS_SERVICE_TYPES.Logger) private readonly logger: LoggerService,\n @inject(SAAS_SERVICE_TYPES.AppConfigService) private readonly appConfigService: AppConfigurationService,\n @inject(SAAS_SERVICE_TYPES.AuthOrganizationConfigurationService) private readonly orgConfigService: AuthOrganizationConfigurationService,\n @inject(SAAS_SERVICE_TYPES.QueueBackendService) private readonly queueService: QueueBackendService,\n @inject(SAAS_SERVICE_TYPES.RepositoriesRegistryHandlerService) private readonly repositoriesRegistry: RepositoriesRegistryHandlerBackendService,\n ) {\n this.logDebug = this.logger.debugByType('AuditEventDispatcherBackendService');\n this.logger.setDebugTypeLogging('AuditEventDispatcherBackendService', false);\n }\n\n /**\n * Dispatch an audit event to the organization's configured per-org audit SINKS.\n *\n * F4 (tenant segregation): the caller (`AuditLogsBackendService`) passes the\n * AUTHENTICATED org (resolved from the request `ExecutionContext`), NEVER the\n * caller-supplied `auditEvent.organizationId`. Sink config is loaded for THIS org\n * only (`_resolveOrgAuditSinks`), so an event can only reach the sinks of the tenant\n * it is authenticated for.\n *\n * Layer 2 of the pipeline: called after the MongoDB write (Layer 1), wired directly\n * into `AuditLogsBackendService` (NOT a resource pipeline post-hook). Fire-and-forget\n * — a resolution or per-sink delivery failure is logged but NEVER thrown onto the\n * business op, and per-sink failures are ISOLATED (F7) so one destination never\n * suppresses another.\n *\n * The seam: `_resolveOrgAuditSinks` returns the 0..N sinks the org has configured\n * (today only a SIEM webhook); this method fans each out by its `type`. Adding a\n * per-org destination later = a new {@link PerOrgAuditSinkType} + a resolver branch +\n * a `case` here.\n *\n * ── APPLICATION scope is RESERVED / DORMANT — there is NO app-scoped producer ──────\n * This producer is ORGANIZATION-scoped ONLY, on purpose. The F4 model routes on a\n * TRUSTED authenticated *tenant* (org), and the sole caller (`AuditLogsBackendService`)\n * fans out over the event's verified member orgs. There is deliberately no\n * `dispatchToApplicationAuditSinks` / `_resolveAppAuditSinks`.\n *\n * The SIEM export-config and delivery-DLQ resources DO each carry an `APPLICATION`\n * polymorphic variant (uniform with SSO/SCIM over `AUTH_SCOPE_VARIANTS`), and every\n * downstream seam is already scope-generic: `loadSiemExportConfig(ec, APPLICATION, appId)`,\n * {@link enqueueSiemWebhookDelivery} (`scopeType`/`scopeId`), the delivery worker's\n * circuit-breaker key, and `_enqueueToDLQ`'s `APPLICATION_SIEM_DELIVERY_DLQ` branch all\n * accept the app scope. But NOTHING emits a first-time app-scoped delivery. The only\n * caller that passes `scopeType: APPLICATION` today is the SIEM-DLQ *retry* custom-impl,\n * which is itself unreachable in production: an `APPLICATION_SIEM_DELIVERY_DLQ` row can\n * only be born from a FAILED app-scoped delivery — and none is ever produced — so the\n * app config-read, app DLQ, and app retry are mutually reachable only through each other.\n * NOTE the operator-visible trap this leaves: an app super-admin CAN enable\n * `APPLICATION_SIEM_EXPORT_CONFIG` (its own custom-impl + APP_ADMIN_SUPER_ADMIN roles)\n * and it will silently deliver NOTHING until a producer exists.\n *\n * To make it real — a SCOPED feature, NOT a mechanical mirror; settle the design fork\n * FIRST (which events an app SIEM receives, whether spanning all tenants, and how it\n * composes with per-org delivery so an event is not double-sent) — add a sibling\n * `_resolveAppAuditSinks(ec, appId)` loading the APP config, a\n * `dispatchToApplicationAuditSinks` fanning out identically, and a producer that calls it\n * with an app context, reusing the SAME envelope/HMAC/scope contract via\n * `enqueueSiemWebhookDelivery({ scopeType: AuthScopeType.APPLICATION, scopeId })`. Until\n * then, treat the APP variant as reserved plumbing, not a wired path.\n */\n public async dispatchToOrganizationAuditSinks(\n ec: ExecutionContext<any>,\n auditEvent: Omit<AuditableEvent, '_id' | 'createdAt' | 'updatedAt'>,\n organizationId: string,\n ): Promise<void> {\n // No authenticated org → no per-org dispatch (fail-closed, F4).\n if (!organizationId) return;\n\n let sinks: ResolvedOrgAuditSink[];\n try {\n sinks = await this._resolveOrgAuditSinks(ec, organizationId);\n } catch (error) {\n // Fire-and-forget: a config-resolution failure must never throw onto the business op.\n this.logger.error?.('Failed to resolve org audit sinks', {\n error,\n eventType: auditEvent.eventType,\n organizationId,\n });\n return;\n }\n\n for (const sink of sinks) {\n try {\n switch (sink.type) {\n case PerOrgAuditSinkType.SIEM_WEBHOOK:\n await this._dispatchToSiemWebhook(ec, auditEvent, organizationId, sink.siemConfig);\n break;\n default:\n // F-1 safety net: a future `PerOrgAuditSinkType` member with no `case` above is\n // LOGGED, never silently dropped (the original finding was a silent no-op). A\n // single-member discriminated union does NOT narrow this `default` to `never`, so\n // a compile-time `assertNever(sink)` guard only becomes possible once a 2nd sink\n // type exists — convert this branch to it then. Until then the resolver's +\n // switch's JSDoc pointers (\"add a case here\") + this warn are the guard.\n this.logger.warn?.(\n 'Unhandled per-org audit sink type — event NOT delivered to this sink; add a `case` in dispatchToOrganizationAuditSinks',\n { sinkType: sink.type, eventType: auditEvent.eventType, organizationId },\n );\n break;\n }\n } catch (error) {\n // Per-sink isolation (F7): one org sink failing never affects the others.\n this.logger.error?.('Failed to dispatch audit event to an org audit sink', {\n error,\n sinkType: sink.type,\n eventType: auditEvent.eventType,\n organizationId,\n });\n }\n }\n }\n\n /**\n * Resolve the per-org audit SINKS an org has configured — the 0..N destinations\n * {@link dispatchToOrganizationAuditSinks} fans an event out to. Keyed on the org the\n * caller already validated as authenticated (F4). Today the only per-org sink type is\n * the SIEM webhook; a future per-org destination appends its own descriptor HERE.\n *\n * Two-level feature gate for `CoreFeature.SECURITY_EVENT_EXPORT`:\n * Level 1 (here): `siemConfig.enabled` — the runtime gate.\n * Level 2 (admin API): `FeatureGateService` checks the org's subscription tier has\n * `SECURITY_EVENT_EXPORT` before allowing `enabled = true`. We cannot use\n * `FeatureResolutionService` here (fire-and-forget context; internal ECs bypass\n * feature resolution), so the pre-validated `enabled` flag IS the per-org gate.\n */\n private async _resolveOrgAuditSinks(\n ec: ExecutionContext<any>,\n organizationId: string,\n ): Promise<ResolvedOrgAuditSink[]> {\n const sinks: ResolvedOrgAuditSink[] = [];\n\n const siemConfig = await this._loadSiemConfigCached(ec, organizationId);\n if (siemConfig?.enabled) {\n sinks.push({ type: PerOrgAuditSinkType.SIEM_WEBHOOK, siemConfig });\n }\n\n // Future per-org sink types (e.g. per-org immudb) resolve here — each appends its\n // own descriptor; the dispatcher switch fans out by `type`.\n return sinks;\n }\n\n /**\n * R2: load the org's SIEM config through a short-TTL cache. Without it,\n * {@link _resolveOrgAuditSinks} reads the config via a self-elevating `readAsSystem` PER\n * EVENT PER ORG — and `readAsSystem` ALSO emits a `SYSTEM_CONTEXT_READ` audit — so every\n * routed audit event (× N orgs on the multi-org fan-out) doubled the audit-write volume to\n * the DB + immudb. Mirrors {@link _orgNameCache}: an enable/disable/rename takes effect within\n * the TTL, acceptable for a rarely-changed config. `null` is cached too (an org with no SIEM\n * config) so a non-configured org is not re-read every event.\n */\n private async _loadSiemConfigCached(\n ec: ExecutionContext<any>,\n organizationId: string,\n ): Promise<SiemExportConfigBase | null> {\n const cached = this._siemConfigCache.get(organizationId);\n if (cached && Date.now() - cached.cachedAt < SIEM_CONFIG_CACHE_TTL_MS) {\n return cached.config;\n }\n const config = await this.orgConfigService.loadSiemExportConfig(ec, AuthScopeType.ORGANIZATION, organizationId);\n if (this._siemConfigCache.size >= SIEM_CONFIG_CACHE_MAX_SIZE) {\n const oldestKey = this._siemConfigCache.keys().next().value;\n if (oldestKey !== undefined) {\n this._siemConfigCache.delete(oldestKey);\n }\n }\n this._siemConfigCache.set(organizationId, { config, cachedAt: Date.now() });\n return config;\n }\n\n /**\n * Drop one organization's cached SIEM config so the NEXT event re-reads it.\n *\n * ## Why this exists\n *\n * Without it the cache had no invalidation at all — only the 5-minute TTL and the size-1000\n * FIFO — so enabling a sink left the organization's events unrouted for up to that window, and\n * audit events are not replayed. The ENABLE direction is the sharp one: `null` is cached\n * deliberately (see {@link _loadSiemConfigCached}) and `null` is exactly the state an\n * organization is in the moment before it first configures a sink, so the first five minutes of\n * a newly-pointed SIEM were silently empty.\n *\n * ## Where it is called from, and why not from the config service\n *\n * From a postfix hook on the SIEM-export-config RESOURCE\n * (`siem-export-config-cache-invalidation.custom-impl.backend.service.ts`), not from\n * `AuthOrganizationConfigurationService.saveSiemExportConfig`.\n *\n * That service was the obvious call site and it is the WRONG one: it is only one of the writers.\n * The config is an ordinary resource, so an update also lands through\n * `ServicesRegistryHandler.update` on `organizationSiemExportConfig` directly. Hooking the\n * service would have applied the invariant at one of two call sites, which is the defect shape\n * `.claude/rules/invariant-call-site-enumeration.md` exists to stop. The resource operation is\n * the chokepoint BOTH writers pass through — `saveSiemExportConfig` itself dispatches its write\n * via `ServicesRegistryHandler`, so covering the resource covers it for free.\n *\n * Note `saveSiemExportConfig` separately refreshes its OWN `siemExportConfigs` map. That is a\n * different cache in a different service and was never the one deciding routing.\n *\n * ## Semantics\n *\n * Deleting the entry rather than overwriting it: the next read goes to the repository, so this\n * cannot install a wrong value, only force a re-read. Idempotent, and a no-op for an\n * organization with no entry — which is the common case, since only organizations that have\n * raised an audit event since the last eviction are in the map at all. Synchronous and\n * infallible on purpose, so the caller needs no error handling.\n */\n public invalidateSiemConfig(organizationId: string): void {\n this._siemConfigCache.delete(organizationId);\n }\n\n /**\n * Deliver one event to an org's SIEM webhook sink: apply the sink's per-event filter,\n * build + HMAC the envelope, and enqueue for async delivery via the SIEM webhook\n * delivery worker (`siemWebhookDeliveryBatchService`, registered under\n * `SIEM_WEBHOOK_DELIVERY_BATCH_REF` in `start-application`). May throw; the caller\n * isolates per-sink failures.\n */\n private async _dispatchToSiemWebhook(\n ec: ExecutionContext<any>,\n auditEvent: Omit<AuditableEvent, '_id' | 'createdAt' | 'updatedAt'>,\n organizationId: string,\n siemConfig: SiemExportConfigBase,\n ): Promise<void> {\n // Classify the event (always returns a value; defaults to data_access/info for unknown types)\n const classification = getAuditEventClassification(auditEvent.eventType);\n\n // Apply the sink's per-event filter\n if (!this._passesEventFilter(auditEvent.eventType, classification, siemConfig)) {\n this.logDebug('Event filtered out by SIEM config', {\n eventType: auditEvent.eventType,\n organizationId,\n });\n return;\n }\n\n // F3: Resolve org name for SIEM analyst readability\n const orgName = await this._resolveOrgName(ec, organizationId);\n\n // Extract application context from eventData if available\n const appId = (auditEvent.eventData as Record<string, unknown>)?.applicationId as string | undefined;\n const appName = (auditEvent.eventData as Record<string, unknown>)?.applicationName as string | undefined;\n\n // Build the envelope (F3: includes orgName, Task 3.12c: includes app context)\n const envelope = this._buildEnvelope(auditEvent, organizationId, classification, orgName, appId, appName);\n\n // Enqueue for async delivery through the single job-contract seam\n // (payload shape + F6 envelope HMAC + batch ref). This dispatch path is\n // org-scoped; {@link enqueueSiemWebhookDelivery} defaults scopeType/scopeId to\n // the organization, so behaviour is unchanged.\n await this.enqueueSiemWebhookDelivery({ envelope, organizationId, siemConfig });\n\n this.logDebug('Audit event enqueued for SIEM delivery', {\n eventId: envelope.eventId,\n eventType: auditEvent.eventType,\n organizationId,\n });\n }\n\n /**\n * Enqueue ONE already-built envelope for async SIEM webhook delivery.\n *\n * The SINGLE authority for the SIEM delivery-job contract — the\n * {@link SIEMDeliveryJobPayload} shape, the F6 envelope HMAC, and the\n * `SIEM_WEBHOOK_DELIVERY_BATCH_REF` batch ref — shared by two producers:\n * 1. first-time dispatch ({@link _dispatchToSiemWebhook}, org scope), and\n * 2. manual DLQ drain (the `siem-delivery-dlq` RETRY_SINGLE / RETRY_ALL\n * custom impls), which re-enqueue a dead-lettered row's STORED envelope\n * for either the ORG or APP scope.\n *\n * Keeping this in one place is not cosmetic: the delivery worker has no\n * back-reference to a DLQ row, so a retry that fails is re-captured as a\n * brand-new DLQ row. If the retry path built a subtly different payload (a\n * dropped `siemConfig` field, a stale HMAC domain), the mismatch would surface\n * only as a silently looping dead-letter — never a compile error. One seam\n * makes that class of drift impossible.\n *\n * @param params.scopeType Defaults to {@link AuthScopeType.ORGANIZATION} (the\n * first-time dispatch path is org-only). The DLQ APP variant passes\n * {@link AuthScopeType.APPLICATION} so the worker's circuit-breaker key and\n * any re-DLQ capture resolve to the application scope, not the org. NOTE: the\n * SIEM-DLQ retry custom-impl is the ONLY caller that passes `APPLICATION` today —\n * there is NO first-time app-scoped producer (the APPLICATION variant is reserved /\n * dormant; see {@link dispatchToOrganizationAuditSinks} for the full rationale and\n * the wiring a future app producer would add).\n * @param params.scopeId Defaults to `organizationId`.\n *\n * May throw on enqueue failure — the CALLER decides isolation: the dispatch\n * path swallows it per-sink (fire-and-forget, F7); the DLQ retry surfaces it to\n * the operator so the row is left intact for another attempt.\n */\n public async enqueueSiemWebhookDelivery(params: {\n envelope: SecurityAuditEventEnvelope;\n organizationId: string;\n siemConfig: SiemExportConfigBase;\n scopeType?: AuthScopeType;\n scopeId?: string;\n }): Promise<void> {\n const { envelope, organizationId, siemConfig } = params;\n const scopeType = params.scopeType ?? AuthScopeType.ORGANIZATION;\n\n // F6: HMAC integrity signature over the envelope payload — computed HERE so the\n // key/domain never diverges between the dispatch and retry producers.\n const envelopeHmac = this._computeEnvelopeHmac(envelope);\n\n // Using the shared constant keeps enqueue ↔ registration ↔ spec cross-validation\n // on one literal. The `siemConfig` projection strips the config to the transport\n // fields the worker needs (no eventFilter — filtering already happened upstream).\n await this.queueService.enqueueCustomBatch(\n SIEM_WEBHOOK_DELIVERY_BATCH_REF,\n {\n envelope,\n envelopeHmac,\n organizationId,\n scopeType,\n scopeId: params.scopeId ?? organizationId,\n siemConfig: {\n webhookUrl: siemConfig.webhookUrl,\n authMethod: siemConfig.authMethod,\n authCredential: siemConfig.authCredential,\n authHeaderName: siemConfig.authHeaderName,\n eventFormat: siemConfig.eventFormat,\n customHeaders: siemConfig.customHeaders,\n delivery: siemConfig.delivery,\n },\n },\n );\n }\n\n /**\n * Check if an event passes the org's SIEM event filter.\n */\n private _passesEventFilter(\n eventType: string,\n classification: { category: AuditEventCategory; severity: AuditEventSeverity },\n config: SiemExportConfigBase,\n ): boolean {\n const filter = config.eventFilter;\n\n // Check category filter\n if (!filter.categories.includes(classification.category)) {\n return false;\n }\n\n // Check severity minimum\n const eventSeverityOrder = SEVERITY_ORDER[classification.severity] ?? 0;\n const minimumSeverityOrder = SEVERITY_ORDER[filter.severityMinimum] ?? 0;\n if (eventSeverityOrder < minimumSeverityOrder) {\n return false;\n }\n\n // Check specific event type filter (if configured)\n if (filter.specificEventTypes && filter.specificEventTypes.length > 0) {\n if (!filter.specificEventTypes.includes(eventType)) {\n return false;\n }\n }\n\n return true;\n }\n\n /**\n * Build a SecurityAuditEventEnvelope from an audit event.\n */\n private _buildEnvelope(\n auditEvent: Omit<AuditableEvent, '_id' | 'createdAt' | 'updatedAt'>,\n organizationId: string,\n classification: { category: AuditEventCategory; severity: AuditEventSeverity },\n organizationName?: string,\n applicationId?: string,\n applicationName?: string,\n ): SecurityAuditEventEnvelope {\n const eventData = auditEvent.eventData as Record<string, unknown>;\n\n return {\n eventId: uuidv4(),\n eventType: auditEvent.eventType,\n eventCategory: classification.category,\n severity: classification.severity,\n timestamp: new Date().toISOString(),\n correlationId: auditEvent.correlationId,\n // Undefined unless the application opted in — the row itself is only ever populated under the policy.\n clientInstanceId: auditEvent.clientInstanceId,\n frontendServiceName: auditEvent.frontendServiceName,\n\n organizationId,\n organizationName, // F3: Populated via org name cache\n applicationId,\n applicationName,\n userId: auditEvent.userId,\n userEmail: (eventData.email as string) || undefined,\n\n sourceService: this.appConfigService.applicationId,\n sourceVersion: process.env.APP_VERSION || '0.0.0',\n sourceEnvironment: this.appConfigService.applicationEnv,\n\n // Auth-specific context from eventData (when applicable)\n authMethod: (eventData.authMethod as string) || undefined,\n authSource: (eventData.authSource as string) || undefined,\n providerRef: (eventData.providerRef as string) || undefined,\n connectionType: (eventData.connectionType as string) || undefined,\n ipAddress: (eventData.ipAddress as string) || undefined,\n userAgent: (eventData.userAgent as string) || undefined,\n\n eventData: eventData as Record<string, any>,\n\n outcome: this._inferOutcome(auditEvent.eventType),\n outcomeReason: (eventData.failureReason as string) || (eventData.reason as string) || undefined,\n };\n }\n\n /**\n * Infer success/failure outcome from event type.\n */\n private _inferOutcome(eventType: string): AuditEventOutcome {\n const failureEventTypes = [\n CoreAuditableEventType.USER_LOGIN_FAILED,\n CoreAuditableEventType.USER_ACCOUNT_LOCKED,\n CoreAuditableEventType.USER_SSO_LOGIN_FAILED,\n CoreAuditableEventType.USER_SSO_ENFORCEMENT_BLOCKED,\n CoreAuditableEventType.SAML_LOGIN_FAILED,\n CoreAuditableEventType.SAML_ASSERTION_REPLAY_DETECTED,\n CoreAuditableEventType.MACHINE_AUTH_FAILED,\n ];\n\n return failureEventTypes.includes(eventType as CoreAuditableEventType) ? AuditEventOutcome.FAILURE : AuditEventOutcome.SUCCESS;\n }\n\n // ─────────────────────────────────────────────────────────────\n // F3: Organization Name Cache\n // ─────────────────────────────────────────────────────────────\n\n /**\n * F3: Resolve organization name with a lightweight in-memory cache.\n * Cache entries expire after ORG_NAME_CACHE_TTL_MS to pick up renames.\n * Returns undefined if lookup fails (fire-and-forget context — don't throw).\n */\n private async _resolveOrgName(ec: ExecutionContext<any>, organizationId: string): Promise<string | undefined> {\n // Check cache first\n const cached = this._orgNameCache.get(organizationId);\n if (cached && (Date.now() - cached.cachedAt) < ORG_NAME_CACHE_TTL_MS) {\n return cached.name;\n }\n\n try {\n const orgsRepo = this.repositoriesRegistry.getRepositoryOrThrow(CoreResourceType.ORGANIZATIONS);\n const org = await orgsRepo.read<{ _id: string; name: string }>(ec, organizationId);\n\n if (org?.name) {\n // B4: Evict oldest entries when cache exceeds max size (simple LRU approximation)\n if (this._orgNameCache.size >= ORG_NAME_CACHE_MAX_SIZE) {\n const oldestKey = this._orgNameCache.keys().next().value;\n if (oldestKey !== undefined) {\n this._orgNameCache.delete(oldestKey);\n }\n }\n this._orgNameCache.set(organizationId, { name: org.name, cachedAt: Date.now() });\n return org.name;\n }\n } catch {\n // Fire-and-forget — org name is optional in the envelope\n }\n\n return undefined;\n }\n\n // ─────────────────────────────────────────────────────────────\n // F6: Event Integrity (HMAC)\n // ─────────────────────────────────────────────────────────────\n\n /**\n * F6: Compute HMAC-SHA256 over the serialized envelope for integrity protection.\n * The delivery service verifies this HMAC before processing, detecting queue tampering.\n */\n private _computeEnvelopeHmac(envelope: SecurityAuditEventEnvelope): string {\n const hmacKey = this._getHmacKey();\n return createHmac('sha256', hmacKey)\n .update(JSON.stringify(envelope))\n .digest('hex');\n }\n\n /**\n * Derive a 256-bit HMAC key from the app's session encryption key\n * using SHA-256 with a domain separator (consistent with SIEM encryption key derivation).\n */\n private _getHmacKey(): Buffer {\n return createHash('sha256')\n .update(`${SIEM_HMAC_DOMAIN}:${this.appConfigService.secrets.security.session.encryptionKey}`)\n .digest();\n }\n\n /**\n * F6: Public static-like method to verify envelope HMAC on the delivery side.\n * Returns true if the HMAC matches, false otherwise.\n */\n public verifyEnvelopeHmac(envelope: SecurityAuditEventEnvelope, expectedHmac: string): boolean {\n const computedHmac = this._computeEnvelopeHmac(envelope);\n // Use crypto.timingSafeEqual for constant-time comparison (prevents timing attacks)\n const computedBuf = Buffer.from(computedHmac, 'utf-8');\n const expectedBuf = Buffer.from(expectedHmac, 'utf-8');\n if (computedBuf.length !== expectedBuf.length) return false;\n return timingSafeEqual(computedBuf, expectedBuf);\n }\n}\n"]}
@@ -135,6 +135,26 @@ export declare class AuditLogsBackendService {
135
135
  isFullResultSet: boolean;
136
136
  organizationId?: string;
137
137
  }): Promise<void>;
138
+ /**
139
+ * A refresh chain was retired to keep a principal within its concurrent-session cap.
140
+ *
141
+ * Best-effort like every other emit here: the eviction has already happened when this is called,
142
+ * and losing the record must never cost the enforcement.
143
+ *
144
+ * `evictedIssuedAt` is optional because the stored ordering score can be unreadable (a legacy
145
+ * whole-second score, a corrupted member). Absent means "we could not say when it was issued",
146
+ * which is honest; a synthesised instant would make the first-in-first-out claim unfalsifiable.
147
+ *
148
+ * assurance-control: WILDO.IDENTITY.SESSION_CONCURRENCY_LIMIT — evidence that the bound was
149
+ * enforced and WHICH chain it retired.
150
+ */
151
+ logUserSessionEvicted(data: {
152
+ userId: string;
153
+ evictedJti: string;
154
+ evictedIssuedAt?: Date;
155
+ configuredMaximum: number;
156
+ retainedCount: number;
157
+ }): Promise<void>;
138
158
  logSystemContextAccess(data: {
139
159
  resourceType: string;
140
160
  operation: CoreResourceOperation.READ | CoreResourceOperation.LIST;