@wildo-ai/saas-backend-lib 1.1.12 → 1.1.13

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 (226) hide show
  1. package/dist/esm/base-classes/models/manual-controllers.backend.definition.d.ts.map +1 -1
  2. package/dist/esm/base-classes/models/manual-controllers.backend.definition.js +2 -0
  3. package/dist/esm/base-classes/models/manual-controllers.backend.definition.js.map +1 -1
  4. package/dist/esm/base-classes/services/repositories/stored-field-omission.backend.d.ts +4 -0
  5. package/dist/esm/base-classes/services/repositories/stored-field-omission.backend.d.ts.map +1 -1
  6. package/dist/esm/base-classes/services/repositories/stored-field-omission.backend.js +20 -5
  7. package/dist/esm/base-classes/services/repositories/stored-field-omission.backend.js.map +1 -1
  8. package/dist/esm/base-classes/services/services/resource-operation-custom-implementation-registry.backend.service.d.ts +3 -0
  9. package/dist/esm/base-classes/services/services/resource-operation-custom-implementation-registry.backend.service.d.ts.map +1 -1
  10. package/dist/esm/base-classes/services/services/resource-operation-custom-implementation-registry.backend.service.js +10 -1
  11. package/dist/esm/base-classes/services/services/resource-operation-custom-implementation-registry.backend.service.js.map +1 -1
  12. package/dist/esm/base-classes/services/services/resource-operation-custom-implementation.factory.backend.d.ts +22 -0
  13. package/dist/esm/base-classes/services/services/resource-operation-custom-implementation.factory.backend.d.ts.map +1 -1
  14. package/dist/esm/base-classes/services/services/resource-operation-custom-implementation.factory.backend.js +25 -1
  15. package/dist/esm/base-classes/services/services/resource-operation-custom-implementation.factory.backend.js.map +1 -1
  16. package/dist/esm/base-classes/services/services/resource-operation-transaction-persistence.backend.utils.d.ts +38 -3
  17. package/dist/esm/base-classes/services/services/resource-operation-transaction-persistence.backend.utils.d.ts.map +1 -1
  18. package/dist/esm/base-classes/services/services/resource-operation-transaction-persistence.backend.utils.js +68 -5
  19. package/dist/esm/base-classes/services/services/resource-operation-transaction-persistence.backend.utils.js.map +1 -1
  20. package/dist/esm/base-classes/services/services/resource-status-transition.custom-impl.backend.service.d.ts +2 -2
  21. package/dist/esm/base-classes/services/services/resource-status-transition.custom-impl.backend.service.d.ts.map +1 -1
  22. package/dist/esm/base-classes/services/services/resource-status-transition.custom-impl.backend.service.js +6 -3
  23. package/dist/esm/base-classes/services/services/resource-status-transition.custom-impl.backend.service.js.map +1 -1
  24. package/dist/esm/base-classes/services/services/services-registry-handler.backend.service.d.ts +83 -0
  25. package/dist/esm/base-classes/services/services/services-registry-handler.backend.service.d.ts.map +1 -1
  26. package/dist/esm/base-classes/services/services/services-registry-handler.backend.service.js +217 -14
  27. package/dist/esm/base-classes/services/services/services-registry-handler.backend.service.js.map +1 -1
  28. package/dist/esm/compliance/approval-seals/approval-seal-currency.backend.d.ts +32 -0
  29. package/dist/esm/compliance/approval-seals/approval-seal-currency.backend.d.ts.map +1 -0
  30. package/dist/esm/compliance/approval-seals/approval-seal-currency.backend.js +36 -0
  31. package/dist/esm/compliance/approval-seals/approval-seal-currency.backend.js.map +1 -0
  32. package/dist/esm/compliance/approval-seals/approval-seal-on-record.backend.d.ts +38 -0
  33. package/dist/esm/compliance/approval-seals/approval-seal-on-record.backend.d.ts.map +1 -0
  34. package/dist/esm/compliance/approval-seals/approval-seal-on-record.backend.js +83 -0
  35. package/dist/esm/compliance/approval-seals/approval-seal-on-record.backend.js.map +1 -0
  36. package/dist/esm/compliance/approval-seals/approval-seal-presentation-read.backend.d.ts +36 -0
  37. package/dist/esm/compliance/approval-seals/approval-seal-presentation-read.backend.d.ts.map +1 -0
  38. package/dist/esm/compliance/approval-seals/approval-seal-presentation-read.backend.js +51 -0
  39. package/dist/esm/compliance/approval-seals/approval-seal-presentation-read.backend.js.map +1 -0
  40. package/dist/esm/compliance/approval-seals/approval-seal-relay-store.backend.d.ts +46 -0
  41. package/dist/esm/compliance/approval-seals/approval-seal-relay-store.backend.d.ts.map +1 -1
  42. package/dist/esm/compliance/approval-seals/approval-seal-relay-store.backend.js +68 -0
  43. package/dist/esm/compliance/approval-seals/approval-seal-relay-store.backend.js.map +1 -1
  44. package/dist/esm/compliance/approval-seals/approval-seal-store.backend.d.ts +27 -0
  45. package/dist/esm/compliance/approval-seals/approval-seal-store.backend.d.ts.map +1 -1
  46. package/dist/esm/compliance/approval-seals/approval-seal-store.backend.js +42 -3
  47. package/dist/esm/compliance/approval-seals/approval-seal-store.backend.js.map +1 -1
  48. package/dist/esm/compliance/approval-seals/approval-seals-admin.backend.contracts.d.ts +26 -1
  49. package/dist/esm/compliance/approval-seals/approval-seals-admin.backend.contracts.d.ts.map +1 -1
  50. package/dist/esm/compliance/approval-seals/approval-seals-admin.backend.contracts.js +10 -0
  51. package/dist/esm/compliance/approval-seals/approval-seals-admin.backend.contracts.js.map +1 -1
  52. package/dist/esm/compliance/approval-seals/approval-seals-admin.backend.controller.d.ts +10 -1
  53. package/dist/esm/compliance/approval-seals/approval-seals-admin.backend.controller.d.ts.map +1 -1
  54. package/dist/esm/compliance/approval-seals/approval-seals-admin.backend.controller.js +60 -28
  55. package/dist/esm/compliance/approval-seals/approval-seals-admin.backend.controller.js.map +1 -1
  56. package/dist/esm/compliance/approval-seals/approval-seals-on-record.backend.controller.d.ts +90 -0
  57. package/dist/esm/compliance/approval-seals/approval-seals-on-record.backend.controller.d.ts.map +1 -0
  58. package/dist/esm/compliance/approval-seals/approval-seals-on-record.backend.controller.js +373 -0
  59. package/dist/esm/compliance/approval-seals/approval-seals-on-record.backend.controller.js.map +1 -0
  60. package/dist/esm/compliance/approval-seals/decision-presentation.backend.d.ts +29 -2
  61. package/dist/esm/compliance/approval-seals/decision-presentation.backend.d.ts.map +1 -1
  62. package/dist/esm/compliance/approval-seals/decision-presentation.backend.js +40 -3
  63. package/dist/esm/compliance/approval-seals/decision-presentation.backend.js.map +1 -1
  64. package/dist/esm/compliance/electronic-signatures/pdf-signature-structure.backend.d.ts.map +1 -1
  65. package/dist/esm/compliance/electronic-signatures/pdf-signature-structure.backend.js +34 -6
  66. package/dist/esm/compliance/electronic-signatures/pdf-signature-structure.backend.js.map +1 -1
  67. package/dist/esm/compliance/electronic-signatures/signature-request-evidence-files.backend.d.ts +30 -6
  68. package/dist/esm/compliance/electronic-signatures/signature-request-evidence-files.backend.d.ts.map +1 -1
  69. package/dist/esm/compliance/electronic-signatures/signature-request-evidence-files.backend.js +67 -22
  70. package/dist/esm/compliance/electronic-signatures/signature-request-evidence-files.backend.js.map +1 -1
  71. package/dist/esm/compliance/electronic-signatures/signature-request-poll.batch.backend.service.d.ts.map +1 -1
  72. package/dist/esm/compliance/electronic-signatures/signature-request-poll.batch.backend.service.js +18 -1
  73. package/dist/esm/compliance/electronic-signatures/signature-request-poll.batch.backend.service.js.map +1 -1
  74. package/dist/esm/compliance/electronic-signatures/signature-requests.backend.service.d.ts +66 -1
  75. package/dist/esm/compliance/electronic-signatures/signature-requests.backend.service.d.ts.map +1 -1
  76. package/dist/esm/compliance/electronic-signatures/signature-requests.backend.service.js +161 -14
  77. package/dist/esm/compliance/electronic-signatures/signature-requests.backend.service.js.map +1 -1
  78. package/dist/esm/compliance/electronic-signatures/signed-document-validation.backend.d.ts +47 -0
  79. package/dist/esm/compliance/electronic-signatures/signed-document-validation.backend.d.ts.map +1 -1
  80. package/dist/esm/compliance/electronic-signatures/signed-document-validation.backend.js.map +1 -1
  81. package/dist/esm/external-providers/services/content-safety.backend.service.d.ts +27 -2
  82. package/dist/esm/external-providers/services/content-safety.backend.service.d.ts.map +1 -1
  83. package/dist/esm/external-providers/services/content-safety.backend.service.js +46 -1
  84. package/dist/esm/external-providers/services/content-safety.backend.service.js.map +1 -1
  85. package/dist/esm/external-providers/services/embeddings.backend.service.d.ts.map +1 -1
  86. package/dist/esm/external-providers/services/embeddings.backend.service.js +2 -0
  87. package/dist/esm/external-providers/services/embeddings.backend.service.js.map +1 -1
  88. package/dist/esm/external-providers/services/external-providers-registry.backend.service.d.ts +11 -0
  89. package/dist/esm/external-providers/services/external-providers-registry.backend.service.d.ts.map +1 -1
  90. package/dist/esm/external-providers/services/external-providers-registry.backend.service.js +23 -0
  91. package/dist/esm/external-providers/services/external-providers-registry.backend.service.js.map +1 -1
  92. package/dist/esm/external-providers/services/provider-service-account-token.backend.d.ts +19 -1
  93. package/dist/esm/external-providers/services/provider-service-account-token.backend.d.ts.map +1 -1
  94. package/dist/esm/external-providers/services/provider-service-account-token.backend.js +41 -1
  95. package/dist/esm/external-providers/services/provider-service-account-token.backend.js.map +1 -1
  96. package/dist/esm/files/utils/file-parent-read-context.backend.d.ts +27 -2
  97. package/dist/esm/files/utils/file-parent-read-context.backend.d.ts.map +1 -1
  98. package/dist/esm/files/utils/file-parent-read-context.backend.js +23 -4
  99. package/dist/esm/files/utils/file-parent-read-context.backend.js.map +1 -1
  100. package/dist/esm/flows-actors/content-guardrails/classifier-availability.backend.utils.d.ts +26 -0
  101. package/dist/esm/flows-actors/content-guardrails/classifier-availability.backend.utils.d.ts.map +1 -1
  102. package/dist/esm/flows-actors/content-guardrails/classifier-availability.backend.utils.js +100 -8
  103. package/dist/esm/flows-actors/content-guardrails/classifier-availability.backend.utils.js.map +1 -1
  104. package/dist/esm/flows-actors/content-guardrails/record-owner-policy.backend.d.ts +3 -1
  105. package/dist/esm/flows-actors/content-guardrails/record-owner-policy.backend.d.ts.map +1 -1
  106. package/dist/esm/flows-actors/content-guardrails/record-owner-policy.backend.js +8 -2
  107. package/dist/esm/flows-actors/content-guardrails/record-owner-policy.backend.js.map +1 -1
  108. package/dist/esm/flows-actors/models/function-tools.backend.definitions.d.ts +43 -0
  109. package/dist/esm/flows-actors/models/function-tools.backend.definitions.d.ts.map +1 -1
  110. package/dist/esm/flows-actors/models/function-tools.backend.definitions.js +37 -0
  111. package/dist/esm/flows-actors/models/function-tools.backend.definitions.js.map +1 -1
  112. package/dist/esm/flows-actors/services/agent-work-decisions.backend.service.d.ts +7 -0
  113. package/dist/esm/flows-actors/services/agent-work-decisions.backend.service.d.ts.map +1 -1
  114. package/dist/esm/flows-actors/services/agent-work-decisions.backend.service.js +8 -3
  115. package/dist/esm/flows-actors/services/agent-work-decisions.backend.service.js.map +1 -1
  116. package/dist/esm/flows-actors/services/agents.backend.service.d.ts +31 -6
  117. package/dist/esm/flows-actors/services/agents.backend.service.d.ts.map +1 -1
  118. package/dist/esm/flows-actors/services/agents.backend.service.js +78 -30
  119. package/dist/esm/flows-actors/services/agents.backend.service.js.map +1 -1
  120. package/dist/esm/flows-actors/services/autonomous-agent-turn.backend.service.d.ts.map +1 -1
  121. package/dist/esm/flows-actors/services/autonomous-agent-turn.backend.service.js +4 -0
  122. package/dist/esm/flows-actors/services/autonomous-agent-turn.backend.service.js.map +1 -1
  123. package/dist/esm/flows-actors/services/conversational-execution.backend.service.d.ts +28 -2
  124. package/dist/esm/flows-actors/services/conversational-execution.backend.service.d.ts.map +1 -1
  125. package/dist/esm/flows-actors/services/conversational-execution.backend.service.js +132 -22
  126. package/dist/esm/flows-actors/services/conversational-execution.backend.service.js.map +1 -1
  127. package/dist/esm/flows-actors/services/flow-actor-executions.custom-impl.backend.service.d.ts.map +1 -1
  128. package/dist/esm/flows-actors/services/flow-actor-executions.custom-impl.backend.service.js +12 -8
  129. package/dist/esm/flows-actors/services/flow-actor-executions.custom-impl.backend.service.js.map +1 -1
  130. package/dist/esm/flows-actors/services/flows-actors-registry.service.d.ts +5 -0
  131. package/dist/esm/flows-actors/services/flows-actors-registry.service.d.ts.map +1 -1
  132. package/dist/esm/flows-actors/services/flows-actors-registry.service.js +18 -2
  133. package/dist/esm/flows-actors/services/flows-actors-registry.service.js.map +1 -1
  134. package/dist/esm/flows-actors/utils/acp-session-runner.backend.utils.d.ts +33 -0
  135. package/dist/esm/flows-actors/utils/acp-session-runner.backend.utils.d.ts.map +1 -1
  136. package/dist/esm/flows-actors/utils/acp-session-runner.backend.utils.js +58 -0
  137. package/dist/esm/flows-actors/utils/acp-session-runner.backend.utils.js.map +1 -1
  138. package/dist/esm/inbound/services/inbound-delivery-dispatch.backend.service.d.ts +12 -1
  139. package/dist/esm/inbound/services/inbound-delivery-dispatch.backend.service.d.ts.map +1 -1
  140. package/dist/esm/inbound/services/inbound-delivery-dispatch.backend.service.js +23 -4
  141. package/dist/esm/inbound/services/inbound-delivery-dispatch.backend.service.js.map +1 -1
  142. package/dist/esm/inbound/services/inbound-delivery-receiver.backend.service.d.ts +12 -1
  143. package/dist/esm/inbound/services/inbound-delivery-receiver.backend.service.d.ts.map +1 -1
  144. package/dist/esm/inbound/services/inbound-delivery-receiver.backend.service.js +55 -17
  145. package/dist/esm/inbound/services/inbound-delivery-receiver.backend.service.js.map +1 -1
  146. package/dist/esm/inbound/services/inbound-delivery-store.backend.service.d.ts +22 -1
  147. package/dist/esm/inbound/services/inbound-delivery-store.backend.service.d.ts.map +1 -1
  148. package/dist/esm/inbound/services/inbound-delivery-store.backend.service.js +52 -9
  149. package/dist/esm/inbound/services/inbound-delivery-store.backend.service.js.map +1 -1
  150. package/dist/esm/inbound/services/inbound-delivery-stored-body.backend.service.d.ts +79 -0
  151. package/dist/esm/inbound/services/inbound-delivery-stored-body.backend.service.d.ts.map +1 -0
  152. package/dist/esm/inbound/services/inbound-delivery-stored-body.backend.service.js +211 -0
  153. package/dist/esm/inbound/services/inbound-delivery-stored-body.backend.service.js.map +1 -0
  154. package/dist/esm/inbound/services/inbound-delivery-subject-erasure-severance.backend.d.ts +2 -2
  155. package/dist/esm/inbound/services/inbound-delivery-subject-erasure-severance.backend.d.ts.map +1 -1
  156. package/dist/esm/inbound/services/inbound-delivery-subject-erasure-severance.backend.js +9 -1
  157. package/dist/esm/inbound/services/inbound-delivery-subject-erasure-severance.backend.js.map +1 -1
  158. package/dist/esm/index.d.ts +1 -0
  159. package/dist/esm/index.d.ts.map +1 -1
  160. package/dist/esm/index.js +1 -0
  161. package/dist/esm/index.js.map +1 -1
  162. package/dist/esm/inject/models/container.d.ts.map +1 -1
  163. package/dist/esm/inject/models/container.js +6 -0
  164. package/dist/esm/inject/models/container.js.map +1 -1
  165. package/dist/esm/inject/models/service-types.d.ts +5 -0
  166. package/dist/esm/inject/models/service-types.d.ts.map +1 -1
  167. package/dist/esm/inject/models/service-types.js +3 -0
  168. package/dist/esm/inject/models/service-types.js.map +1 -1
  169. package/dist/esm/monitoring/observability/models/observability.backend.schemas.d.ts +5 -0
  170. package/dist/esm/monitoring/observability/models/observability.backend.schemas.d.ts.map +1 -1
  171. package/dist/esm/monitoring/observability/models/observability.backend.schemas.js.map +1 -1
  172. package/dist/esm/monitoring/observability/services/metrics.service.d.ts +34 -1
  173. package/dist/esm/monitoring/observability/services/metrics.service.d.ts.map +1 -1
  174. package/dist/esm/monitoring/observability/services/metrics.service.js +49 -0
  175. package/dist/esm/monitoring/observability/services/metrics.service.js.map +1 -1
  176. package/dist/esm/notifications/webhooks/controllers/webhook-auto-controller.backend.service.d.ts +6 -1
  177. package/dist/esm/notifications/webhooks/controllers/webhook-auto-controller.backend.service.d.ts.map +1 -1
  178. package/dist/esm/notifications/webhooks/controllers/webhook-auto-controller.backend.service.js +15 -4
  179. package/dist/esm/notifications/webhooks/controllers/webhook-auto-controller.backend.service.js.map +1 -1
  180. package/dist/esm/notifications/webhooks/outbound/services/m2m-webhook-delivery-log.custom-impl.backend.service.d.ts.map +1 -1
  181. package/dist/esm/notifications/webhooks/outbound/services/m2m-webhook-delivery-log.custom-impl.backend.service.js +3 -1
  182. package/dist/esm/notifications/webhooks/outbound/services/m2m-webhook-delivery-log.custom-impl.backend.service.js.map +1 -1
  183. package/dist/esm/runtime/services/http-server.backend.service.d.ts +3 -1
  184. package/dist/esm/runtime/services/http-server.backend.service.d.ts.map +1 -1
  185. package/dist/esm/runtime/services/http-server.backend.service.js +11 -3
  186. package/dist/esm/runtime/services/http-server.backend.service.js.map +1 -1
  187. package/dist/esm/runtime/services/utils/inbound-endpoint-raw-body.backend.utils.d.ts +22 -0
  188. package/dist/esm/runtime/services/utils/inbound-endpoint-raw-body.backend.utils.d.ts.map +1 -1
  189. package/dist/esm/runtime/services/utils/inbound-endpoint-raw-body.backend.utils.js +54 -0
  190. package/dist/esm/runtime/services/utils/inbound-endpoint-raw-body.backend.utils.js.map +1 -1
  191. package/dist/esm/security/authentications/services/auth-methods/auth-password-reset.backend.service.d.ts +51 -0
  192. package/dist/esm/security/authentications/services/auth-methods/auth-password-reset.backend.service.d.ts.map +1 -1
  193. package/dist/esm/security/authentications/services/auth-methods/auth-password-reset.backend.service.js +87 -15
  194. package/dist/esm/security/authentications/services/auth-methods/auth-password-reset.backend.service.js.map +1 -1
  195. package/dist/esm/security/authentications/services/custom-impls/siem-delivery-dlq.custom-impl.backend.service.d.ts.map +1 -1
  196. package/dist/esm/security/authentications/services/custom-impls/siem-delivery-dlq.custom-impl.backend.service.js +5 -2
  197. package/dist/esm/security/authentications/services/custom-impls/siem-delivery-dlq.custom-impl.backend.service.js.map +1 -1
  198. package/dist/esm/security/authentications/services/custom-impls/sso-config-domain-verification.custom-impl.backend.service.d.ts.map +1 -1
  199. package/dist/esm/security/authentications/services/custom-impls/sso-config-domain-verification.custom-impl.backend.service.js +65 -24
  200. package/dist/esm/security/authentications/services/custom-impls/sso-config-domain-verification.custom-impl.backend.service.js.map +1 -1
  201. package/dist/esm/security/authorizations/super-admin-floor.backend.utils.d.ts +64 -1
  202. package/dist/esm/security/authorizations/super-admin-floor.backend.utils.d.ts.map +1 -1
  203. package/dist/esm/security/authorizations/super-admin-floor.backend.utils.js +84 -2
  204. package/dist/esm/security/authorizations/super-admin-floor.backend.utils.js.map +1 -1
  205. package/dist/esm/security/oauth-clients/oauth-clients.custom-impl.backend.service.d.ts +14 -0
  206. package/dist/esm/security/oauth-clients/oauth-clients.custom-impl.backend.service.d.ts.map +1 -1
  207. package/dist/esm/security/oauth-clients/oauth-clients.custom-impl.backend.service.js +13 -5
  208. package/dist/esm/security/oauth-clients/oauth-clients.custom-impl.backend.service.js.map +1 -1
  209. package/dist/esm/users/services/anonymous-consent.custom-impl.backend.service.d.ts.map +1 -1
  210. package/dist/esm/users/services/anonymous-consent.custom-impl.backend.service.js +6 -2
  211. package/dist/esm/users/services/anonymous-consent.custom-impl.backend.service.js.map +1 -1
  212. package/dist/esm/users/services/user-force-password-reset.custom-impl.backend.service.d.ts +7 -0
  213. package/dist/esm/users/services/user-force-password-reset.custom-impl.backend.service.d.ts.map +1 -1
  214. package/dist/esm/users/services/user-force-password-reset.custom-impl.backend.service.js +65 -20
  215. package/dist/esm/users/services/user-force-password-reset.custom-impl.backend.service.js.map +1 -1
  216. package/dist/esm/users/services/user-role-administration.custom-impl.backend.service.d.ts.map +1 -1
  217. package/dist/esm/users/services/user-role-administration.custom-impl.backend.service.js +11 -4
  218. package/dist/esm/users/services/user-role-administration.custom-impl.backend.service.js.map +1 -1
  219. package/dist/esm/users/services/user-self-custom-implementations.backend.d.ts.map +1 -1
  220. package/dist/esm/users/services/user-self-custom-implementations.backend.js +4 -2
  221. package/dist/esm/users/services/user-self-custom-implementations.backend.js.map +1 -1
  222. package/dist/esm/users/services/user-super-admin-floor.custom-impl.backend.service.d.ts.map +1 -1
  223. package/dist/esm/users/services/user-super-admin-floor.custom-impl.backend.service.js +16 -0
  224. package/dist/esm/users/services/user-super-admin-floor.custom-impl.backend.service.js.map +1 -1
  225. package/dist/tsconfig.build.tsbuildinfo +1 -1
  226. package/package.json +10 -10
@@ -0,0 +1,90 @@
1
+ import { LoggerService } from '../../monitoring/logger/services/logger.service';
2
+ import { ErrorBuilderBackendService } from '../../errors/services/error-builder.backend.service';
3
+ import { ErrorHandlerService } from '../../errors/services/error-handler.backend.service';
4
+ import { AbstractControllerBackendService } from '../../base-classes/services/controllers/abstract-controller.backend.service';
5
+ import { RepositoriesRegistryHandlerBackendService } from '../../base-classes/services/repositories/repositories-registry-handler.backend.service';
6
+ import type { ResourcesRegistryBackendService } from '../../resources/services/resources-registry.backend.service';
7
+ import type { ExecutionContextCreatorBackendService } from '../../runtime/services/execution-context-creator.backend.service';
8
+ import type { AuthorizationsBackendService } from '../../security/authorizations/services/authorizations.backend.service';
9
+ import type { SystemAccessBackendService } from '../../base-classes/services/services/system-access.backend.service';
10
+ import type { FilesBackendService } from '../../files/services/files.backend.service';
11
+ import type { AppConfigurationService } from '../../config/services/app-configuration.backend.service';
12
+ import type { FileFieldDetectionBackendService } from '../../files/services/file-field-detection.backend.service';
13
+ export declare class ApprovalSealsOnRecordControllerBackendService extends AbstractControllerBackendService {
14
+ private readonly repositoriesRegistry;
15
+ private readonly resourcesRegistry;
16
+ private readonly executionContextCreator;
17
+ private readonly authorizationsService;
18
+ /** The currency check reads the record as the system, as the operator's door does, so both compare the same values. */
19
+ private readonly systemAccess?;
20
+ /** A presentation moved to a frozen file, and the manifested copy, are read through the files service. */
21
+ private readonly files?;
22
+ private readonly appConfig?;
23
+ /** A file's view admits its reader through the files door's own check of the field the file is in. */
24
+ private readonly fileFieldDetection?;
25
+ private readonly store;
26
+ constructor(logger: LoggerService, errorBuilder: ErrorBuilderBackendService, errorHandler: ErrorHandlerService, repositoriesRegistry: RepositoriesRegistryHandlerBackendService, resourcesRegistry: ResourcesRegistryBackendService, executionContextCreator: ExecutionContextCreatorBackendService, authorizationsService: AuthorizationsBackendService,
27
+ /** The currency check reads the record as the system, as the operator's door does, so both compare the same values. */
28
+ systemAccess?: Pick<SystemAccessBackendService, 'readAsSystem'>,
29
+ /** A presentation moved to a frozen file, and the manifested copy, are read through the files service. */
30
+ files?: Pick<FilesBackendService, 'readFileBytes'>, appConfig?: Pick<AppConfigurationService, 'config' | 'isInitialized'>,
31
+ /** A file's view admits its reader through the files door's own check of the field the file is in. */
32
+ fileFieldDetection?: FileFieldDetectionBackendService);
33
+ protected setupRoutes(): void;
34
+ /** GET …/:field — the record's view of the seal it stores. */
35
+ private handleView;
36
+ /** GET …/:field/manifested-copy — the copy, checked against its recorded hash before a byte leaves. */
37
+ private handleManifestedCopy;
38
+ /**
39
+ * GET /approval-seals/files/:fileId — the seal that attests a file (#2913): the deliverable a person approved by its hash.
40
+ * Without currency, which is a statement about the record the approval is about, and which a reader of the file may not
41
+ * be able to read.
42
+ */
43
+ private handleFileView;
44
+ /** GET /approval-seals/files/:fileId/manifested-copy — that file's manifested copy. */
45
+ private handleFileManifestedCopy;
46
+ /**
47
+ * GET /approval-seals/decisions/:decisionRef — the seal of the signed-in person's own answer to a decision (#2913). The
48
+ * person who decided may always see the seal of their act (it is in their subject export); nobody else is answered.
49
+ *
50
+ * assurance-control: WILDO.ACCESS.LEAST_PRIVILEGE — a decision's seal is answered to the person who decided, and to nobody else through this door.
51
+ */
52
+ private handleDecisionView;
53
+ /** What a reader is told about one seal: its successor, its currency when asked for, its copy, its public check. */
54
+ private viewOf;
55
+ /** Send a SEALED seal's manifested copy, checked against its recorded hash before a byte leaves. */
56
+ private sendManifestedCopy;
57
+ /**
58
+ * Find the seal, check it is about the record the request names, and admit the caller through that record's READ, the
59
+ * record's field holding the seal's id. Every refusal is the same 404.
60
+ */
61
+ private admit;
62
+ /**
63
+ * The seal that attests a file and that its own record still names, then the caller admitted through the READ of the
64
+ * record the FILE belongs to (`authorizeFileParentRead`, over `authorizeRecordReadAsCaller`). A live seal first, latest
65
+ * decision first; then one its approver's erasure retained, known by id and subject only. A seal its record does not
66
+ * name (an approved effect that failed) is passed over. Every refusal is the same 404, the files door's.
67
+ */
68
+ private admitFile;
69
+ private attestingSealOf;
70
+ /**
71
+ * Whether the record a seal is about names it: the consumer stores a seal's id when the approved effect happens (a
72
+ * close's `signOffApprovalSealId`, or the reopening that set it aside), so a seal no field of its record names approved
73
+ * nothing that took effect. Read as the system; nothing of the record leaves.
74
+ */
75
+ private recordNamesSeal;
76
+ private fileNotFound;
77
+ /** Whether the registry knows a resource type, asked without letting an unknown name throw a 500. */
78
+ private resourceIsRegistered;
79
+ /**
80
+ * The approval's currency over its record (#2914), as the operator's door computes it. Undefined when it cannot be
81
+ * asked here (the presentation cannot be read, or no system access is bound): the view then says nothing about
82
+ * currency, which is never read as current.
83
+ */
84
+ private currencyOf;
85
+ /** The `files` row of the copy a seal names, when it is linked to the seal and servable; null otherwise. */
86
+ private servableCopyRow;
87
+ private manifestedCopyServable;
88
+ private notFound;
89
+ }
90
+ //# sourceMappingURL=approval-seals-on-record.backend.controller.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"approval-seals-on-record.backend.controller.d.ts","sourceRoot":"","sources":["../../../../src/compliance/approval-seals/approval-seals-on-record.backend.controller.ts"],"names":[],"mappings":"AAyDA,OAAO,EAAE,aAAa,EAAE,MAAM,8DAA8D,CAAC;AAC7F,OAAO,EAAE,0BAA0B,EAAE,MAAM,kEAAkE,CAAC;AAC9G,OAAO,EAAE,mBAAmB,EAAE,MAAM,kEAAkE,CAAC;AACvG,OAAO,EAAE,gCAAgC,EAAE,MAAM,0FAA0F,CAAC;AAC5I,OAAO,EAAE,yCAAyC,EAAE,MAAM,qGAAqG,CAAC;AAChK,OAAO,KAAK,EAAE,+BAA+B,EAAE,MAAM,0EAA0E,CAAC;AAChI,OAAO,KAAK,EAAE,qCAAqC,EAAE,MAAM,+EAA+E,CAAC;AAC3I,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,oFAAoF,CAAC;AACvI,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,iFAAiF,CAAC;AAClI,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,yDAAyD,CAAC;AACnG,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,sEAAsE,CAAC;AAIpH,OAAO,KAAK,EAAE,gCAAgC,EAAE,MAAM,wEAAwE,CAAC;AA6C/H,qBACa,6CAA8C,SAAQ,gCAAgC;IAOhC,OAAO,CAAC,QAAQ,CAAC,oBAAoB;IAChD,OAAO,CAAC,QAAQ,CAAC,iBAAiB;IACpB,OAAO,CAAC,QAAQ,CAAC,uBAAuB;IACxD,OAAO,CAAC,QAAQ,CAAC,qBAAqB;IACxF,uHAAuH;IAC3D,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAC;IAC1F,0GAA0G;IAC9C,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC;IAC1B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC;IACpF,sGAAsG;IACpC,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAC;IAhBxG,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAyB;gBAGV,MAAM,EAAE,aAAa,EACR,YAAY,EAAE,0BAA0B,EACxC,YAAY,EAAE,mBAAmB,EACD,oBAAoB,EAAE,yCAAyC,EAC1E,iBAAiB,EAAE,+BAA+B,EACpC,uBAAuB,EAAE,qCAAqC,EAC9E,qBAAqB,EAAE,4BAA4B;IACtH,uHAAuH;IAC1C,YAAY,CAAC,EAAE,IAAI,CAAC,0BAA0B,EAAE,cAAc,CAAC;IAC5I,0GAA0G;IAC7B,KAAK,CAAC,EAAE,IAAI,CAAC,mBAAmB,EAAE,eAAe,CAAC,EACrD,SAAS,CAAC,EAAE,IAAI,CAAC,uBAAuB,EAAE,QAAQ,GAAG,eAAe,CAAC;IAC/I,sGAAsG;IACnB,kBAAkB,CAAC,EAAE,gCAAgC;IAM1I,SAAS,CAAC,WAAW,IAAI,IAAI;IAQ7B,8DAA8D;YAChD,UAAU;IAOxB,uGAAuG;YACzF,oBAAoB;IAKlC;;;;OAIG;YACW,cAAc;IAO5B,uFAAuF;YACzE,wBAAwB;IAKtC;;;;;OAKG;YACW,kBAAkB;IAehC,oHAAoH;YACtG,MAAM;IAmBpB,oGAAoG;YACtF,kBAAkB;IAoBhC;;;OAGG;YACW,KAAK;IA6BnB;;;;;OAKG;YACW,SAAS;YAmBT,eAAe;IAY7B;;;;OAIG;YACW,eAAe;IAO7B,OAAO,CAAC,YAAY;IAMpB,qGAAqG;IACrG,OAAO,CAAC,oBAAoB;IAQ5B;;;;OAIG;YACW,UAAU;IA0BxB,4GAA4G;YAC9F,eAAe;YAQf,sBAAsB;IAIpC,OAAO,CAAC,QAAQ;CAKjB"}
@@ -0,0 +1,373 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var __metadata = (this && this.__metadata) || function (k, v) {
8
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
+ };
10
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
11
+ return function (target, key) { decorator(target, key, paramIndex); }
12
+ };
13
+ /**
14
+ * # The record's view of its approval seal (#2913)
15
+ *
16
+ * - `GET /approval-seals/:sealId/records/:resourceType/:resourceId/:field` → how the record shows the seal its `field`
17
+ * stores: who approved, when, with which meaning, whether it is still current over the record, its deliverable.
18
+ * - `…/manifested-copy` → the deliverable's manifested copy (#2907), as a PDF.
19
+ * - `GET /approval-seals/files/:fileId` (+ `/manifested-copy`) → the seal that attests a FILE, the deliverable a person
20
+ * approved by its hash: admitted through the READ of the record the file belongs to (the files door's own
21
+ * `authorizeFileParentRead`, itself `authorizeRecordReadAsCaller`), and only for a seal its own record still names.
22
+ * - `GET /approval-seals/decisions/:decisionRef` → the seal of the signed-in person's own answer to a decision, for the
23
+ * decision cards and the inbox: the decider reads the seal of their act, nobody else.
24
+ *
25
+ * ## Who is admitted, and to what
26
+ *
27
+ * Exactly the callers the RECORD's own READ operation admits, with their own credentials, through the routes that
28
+ * operation declares (`authorizeRecordReadAsCaller`, the admission a file in a record's field already uses). On top of
29
+ * that, three facts must hold, and any that does not answers the same 404 as a record that does not exist:
30
+ *
31
+ * 1. the record's `field` holds the seal's id: a seal is shown only where the consumer stored it, which it does when the
32
+ * approved effect happened, so a seal whose effect failed (a close that moved while it was being signed) never reads
33
+ * as approving the record;
34
+ * 2. the seal is ABOUT that record (`linkedResourceType` / `linkedResourceId`): a field cannot be pointed at another
35
+ * record's seal;
36
+ * 3. for the copy, the seal still owns a servable copy whose bytes match the hash recorded for them.
37
+ *
38
+ * Read-only, and nothing is recorded on the trail: the view hands the record's reader the approver's printed name, which
39
+ * a record that stores a decider (`closes.signedOffByUserId`) already shows them, and the record's own READ is the
40
+ * access decision. Currency names fields, never values.
41
+ *
42
+ * assurance-control: WILDO.AI.HUMAN_OVERSIGHT, WILDO.AUDIT.EVIDENCE_INTEGRITY — the person's approval of a record is shown where the record is read, with whether it still describes the record, computed from the seal, never assumed.
43
+ * assurance-control: WILDO.ACCESS.LEAST_PRIVILEGE — a seal is shown only to a caller the record's own READ admits, and only for the seal that record stores.
44
+ */
45
+ import { createHash } from 'crypto';
46
+ import { injectable, inject, optional } from 'inversify';
47
+ import { ApprovalSealOfDecisionParamsSchema, ApprovalSealOnFileParamsSchema, ApprovalSealOnRecordParamsSchema, ApprovalSealStatus, BackendApiType, ErrorCustomMessageReference, ErrorType, FILE_DOWNLOADABLE_STATUSES, HttpMethod, ManualControllerRouteKey, isWildoBackendError, } from '@wildo-ai/saas-models';
48
+ import { SAAS_SERVICE_TYPES } from '../../inject/models/service-types.js';
49
+ import { LoggerService } from '../../monitoring/logger/services/logger.service.js';
50
+ import { ErrorBuilderBackendService } from '../../errors/services/error-builder.backend.service.js';
51
+ import { ErrorHandlerService } from '../../errors/services/error-handler.backend.service.js';
52
+ import { AbstractControllerBackendService } from '../../base-classes/services/controllers/abstract-controller.backend.service.js';
53
+ import { RepositoriesRegistryHandlerBackendService } from '../../base-classes/services/repositories/repositories-registry-handler.backend.service.js';
54
+ import { describeCaughtError } from '../../errors/utils/error-log-level-classification.backend.utils.js';
55
+ import { authorizeFileParentRead, authorizeRecordReadAsCaller } from '../../files/utils/file-parent-read-context.backend.js';
56
+ import { createFileShareBinding } from '../../files/utils/file-share-binding.backend.js';
57
+ import { ApprovalSealRelayStore } from './approval-seal-relay-store.backend.js';
58
+ import { assessApprovalSealCurrency } from './approval-seal-currency.backend.js';
59
+ import { ApprovalSealPresentationReadOutcome, readApprovalSealPresentation } from './approval-seal-presentation-read.backend.js';
60
+ import { approvalSealPublicCheckUrlOf } from './approval-seal-public-check.backend.js';
61
+ import { approvalSealOnRecordViewOf } from './approval-seal-on-record.backend.js';
62
+ /** A stored id as a string: ids are strings in SQL and ObjectIds in Mongo, and a populated object is never an id. */
63
+ function storedIdOf(value) {
64
+ if (typeof value === 'string')
65
+ return value;
66
+ if (value && typeof value === 'object' && 'toHexString' in value && typeof value.toHexString === 'function') {
67
+ return value.toHexString();
68
+ }
69
+ return undefined;
70
+ }
71
+ /**
72
+ * How deep in a record a seal's id is looked for: a top-level field (`signOffApprovalSealId`), or a member of an entry of
73
+ * a top-level array (`reopenings[].previousSignOffApprovalSealId`). Bounded, so a record's size bounds the search.
74
+ */
75
+ const RECORD_SEAL_REFERENCE_DEPTH = 2;
76
+ /** Whether a stored value names `id`, within `depth` levels of objects and arrays. */
77
+ function valueNamesId(value, id, depth) {
78
+ if (storedIdOf(value) === id)
79
+ return true;
80
+ if (depth <= 0 || value === null || typeof value !== 'object' || value instanceof Date)
81
+ return false;
82
+ const members = Array.isArray(value) ? value : Object.values(value);
83
+ return members.some((member) => valueNamesId(member, id, Array.isArray(value) ? depth : depth - 1));
84
+ }
85
+ let ApprovalSealsOnRecordControllerBackendService = class ApprovalSealsOnRecordControllerBackendService extends AbstractControllerBackendService {
86
+ repositoriesRegistry;
87
+ resourcesRegistry;
88
+ executionContextCreator;
89
+ authorizationsService;
90
+ systemAccess;
91
+ files;
92
+ appConfig;
93
+ fileFieldDetection;
94
+ store;
95
+ constructor(logger, errorBuilder, errorHandler, repositoriesRegistry, resourcesRegistry, executionContextCreator, authorizationsService, systemAccess, files, appConfig, fileFieldDetection) {
96
+ super(logger, errorBuilder, errorHandler, 'ApprovalSealsOnRecordController');
97
+ this.repositoriesRegistry = repositoriesRegistry;
98
+ this.resourcesRegistry = resourcesRegistry;
99
+ this.executionContextCreator = executionContextCreator;
100
+ this.authorizationsService = authorizationsService;
101
+ this.systemAccess = systemAccess;
102
+ this.files = files;
103
+ this.appConfig = appConfig;
104
+ this.fileFieldDetection = fileFieldDetection;
105
+ this.store = new ApprovalSealRelayStore(repositoriesRegistry);
106
+ }
107
+ setupRoutes() {
108
+ this.mountManualRoute(ManualControllerRouteKey.APPROVAL_SEAL_ON_RECORD, HttpMethod.GET, (req, res) => this.handleView(req, res));
109
+ this.mountManualRoute(ManualControllerRouteKey.APPROVAL_SEAL_ON_RECORD_MANIFESTED_COPY, HttpMethod.GET, (req, res) => this.handleManifestedCopy(req, res));
110
+ this.mountManualRoute(ManualControllerRouteKey.APPROVAL_SEAL_ON_FILE, HttpMethod.GET, (req, res) => this.handleFileView(req, res));
111
+ this.mountManualRoute(ManualControllerRouteKey.APPROVAL_SEAL_ON_FILE_MANIFESTED_COPY, HttpMethod.GET, (req, res) => this.handleFileManifestedCopy(req, res));
112
+ this.mountManualRoute(ManualControllerRouteKey.APPROVAL_SEAL_OF_DECISION, HttpMethod.GET, (req, res) => this.handleDecisionView(req, res));
113
+ }
114
+ /** GET …/:field — the record's view of the seal it stores. */
115
+ async handleView(req, res) {
116
+ const { params, seal, retained } = await this.admit(req);
117
+ const view = await this.viewOf(params.sealId, seal, retained, { resourceType: params.resourceType, resourceId: params.resourceId }, true);
118
+ res.set('Cache-Control', 'no-store');
119
+ res.json(view);
120
+ }
121
+ /** GET …/:field/manifested-copy — the copy, checked against its recorded hash before a byte leaves. */
122
+ async handleManifestedCopy(req, res) {
123
+ const { params, seal } = await this.admit(req);
124
+ await this.sendManifestedCopy(seal, res, () => this.notFound(params, 'approval_seal_manifested_copy_not_servable'));
125
+ }
126
+ /**
127
+ * GET /approval-seals/files/:fileId — the seal that attests a file (#2913): the deliverable a person approved by its hash.
128
+ * Without currency, which is a statement about the record the approval is about, and which a reader of the file may not
129
+ * be able to read.
130
+ */
131
+ async handleFileView(req, res) {
132
+ const { found } = await this.admitFile(req);
133
+ const view = await this.viewOf(found.sealId, found.seal, found.seal === null, found.subject, false);
134
+ res.set('Cache-Control', 'no-store');
135
+ res.json(view);
136
+ }
137
+ /** GET /approval-seals/files/:fileId/manifested-copy — that file's manifested copy. */
138
+ async handleFileManifestedCopy(req, res) {
139
+ const { fileId, found } = await this.admitFile(req);
140
+ await this.sendManifestedCopy(found.seal, res, () => this.fileNotFound(fileId));
141
+ }
142
+ /**
143
+ * GET /approval-seals/decisions/:decisionRef — the seal of the signed-in person's own answer to a decision (#2913). The
144
+ * person who decided may always see the seal of their act (it is in their subject export); nobody else is answered.
145
+ *
146
+ * assurance-control: WILDO.ACCESS.LEAST_PRIVILEGE — a decision's seal is answered to the person who decided, and to nobody else through this door.
147
+ */
148
+ async handleDecisionView(req, res) {
149
+ const { decisionRef } = ApprovalSealOfDecisionParamsSchema.parse(req.params);
150
+ const executionContext = await this.executionContextCreator.createExecutionContextForManualController(req);
151
+ const userId = executionContext.initiatorIds?.userId;
152
+ const notFound = () => this.errorBuilder.buildError(ErrorType.NOT_FOUND, executionContext, {
153
+ customMessageReference: ErrorCustomMessageReference.NOT_FOUND, context: { reason: 'approval_seal_of_decision_not_found', decisionRef },
154
+ });
155
+ if (!userId)
156
+ throw notFound();
157
+ const seal = await this.store.latestOfDecisionBy(decisionRef, userId);
158
+ if (!seal)
159
+ throw notFound();
160
+ const view = await this.viewOf(seal._id, seal, false, { resourceType: seal.linkedResourceType, resourceId: seal.linkedResourceId }, true);
161
+ res.set('Cache-Control', 'no-store');
162
+ res.json(view);
163
+ }
164
+ /** What a reader is told about one seal: its successor, its currency when asked for, its copy, its public check. */
165
+ async viewOf(sealId, seal, retained, subject, withCurrency) {
166
+ const successor = seal ? await this.store.successorOf(sealId, subject) : null;
167
+ const currency = seal && withCurrency ? await this.currencyOf(seal) : undefined;
168
+ const manifestedCopyServable = seal ? await this.manifestedCopyServable(seal) : false;
169
+ const publicCheckUrl = approvalSealPublicCheckUrlOf(this.appConfig?.isInitialized()
170
+ ? this.appConfig.config.runtime?.endPoints?.[BackendApiType.MAIN_BACKEND_API]?.publicUrl
171
+ : undefined);
172
+ return approvalSealOnRecordViewOf({
173
+ sealId, seal, retained, successor, manifestedCopyServable,
174
+ ...(currency ? { currency } : {}), ...(publicCheckUrl ? { publicCheckUrl } : {}),
175
+ });
176
+ }
177
+ /** Send a SEALED seal's manifested copy, checked against its recorded hash before a byte leaves. */
178
+ async sendManifestedCopy(seal, res, notFound) {
179
+ if (!seal || seal.status !== ApprovalSealStatus.SEALED || !this.files)
180
+ throw notFound();
181
+ const fileRow = await this.servableCopyRow(seal);
182
+ if (!fileRow)
183
+ throw notFound();
184
+ const context = await this.executionContextCreator.createForInternalSystemOperation();
185
+ const bytes = await this.files.readFileBytes(fileRow, context);
186
+ const recorded = typeof fileRow.contentSha256 === 'string' ? fileRow.contentSha256 : undefined;
187
+ if (!recorded || createHash('sha256').update(bytes).digest('hex') !== recorded) {
188
+ this.logger.error('[ApprovalSeals] A manifested copy does not match the hash recorded for it; it is not served', { sealId: seal._id, fileId: fileRow.fileId });
189
+ throw this.errorBuilder.buildError(ErrorType.CONFLICT, undefined, {
190
+ customMessageReference: ErrorCustomMessageReference.CONFLICT, context: { reason: 'approval_seal_manifested_copy_mismatch', sealId: seal._id },
191
+ });
192
+ }
193
+ res.setHeader('Content-Type', 'application/pdf');
194
+ res.setHeader('Content-Disposition', `attachment; filename="approval-${seal._id}.pdf"`);
195
+ res.setHeader('Cache-Control', 'no-store');
196
+ res.setHeader('X-Content-Type-Options', 'nosniff');
197
+ res.end(bytes);
198
+ }
199
+ /**
200
+ * Find the seal, check it is about the record the request names, and admit the caller through that record's READ, the
201
+ * record's field holding the seal's id. Every refusal is the same 404.
202
+ */
203
+ async admit(req) {
204
+ const params = ApprovalSealOnRecordParamsSchema.parse(req.params);
205
+ const notFound = () => this.notFound(params, 'approval_seal_not_shown_on_record');
206
+ const seal = await this.store.read(params.sealId);
207
+ const subject = seal
208
+ ? { resourceType: seal.linkedResourceType, resourceId: seal.linkedResourceId }
209
+ : await this.store.retainedSubjectOf(params.sealId);
210
+ if (!subject || subject.resourceType !== params.resourceType || subject.resourceId !== params.resourceId)
211
+ throw notFound();
212
+ const resourceType = params.resourceType;
213
+ if (!this.resourceIsRegistered(resourceType))
214
+ throw notFound();
215
+ const configuration = this.resourcesRegistry.getResourceConfiguration(resourceType);
216
+ // The scope the record lives in, from the record itself: it only shapes the routes its READ is tried through.
217
+ const located = await this.repositoriesRegistry.readUnscopedByKey(resourceType, { _id: params.resourceId });
218
+ if (!located)
219
+ throw notFound();
220
+ const primaryScope = configuration.resourcePrimaryScope;
221
+ await authorizeRecordReadAsCaller(req, {
222
+ resourceType: params.resourceType,
223
+ resourceId: params.resourceId,
224
+ primaryScope,
225
+ applicationId: storedIdOf(located.applicationId) ?? (seal ? String(seal.applicationId) : ''),
226
+ organizationId: storedIdOf(located.organizationId) ?? null,
227
+ userId: storedIdOf(located.userId) ?? null,
228
+ }, (row) => storedIdOf(row[params.field]) === params.sealId, {
229
+ resourcesRegistry: this.resourcesRegistry, repositoriesRegistry: this.repositoriesRegistry,
230
+ executionContextCreator: this.executionContextCreator, authorizationsService: this.authorizationsService,
231
+ }, notFound);
232
+ return { params, seal, retained: !seal };
233
+ }
234
+ /**
235
+ * The seal that attests a file and that its own record still names, then the caller admitted through the READ of the
236
+ * record the FILE belongs to (`authorizeFileParentRead`, over `authorizeRecordReadAsCaller`). A live seal first, latest
237
+ * decision first; then one its approver's erasure retained, known by id and subject only. A seal its record does not
238
+ * name (an approved effect that failed) is passed over. Every refusal is the same 404, the files door's.
239
+ */
240
+ async admitFile(req) {
241
+ const { fileId } = ApprovalSealOnFileParamsSchema.parse(req.params);
242
+ const found = await this.attestingSealOf(fileId);
243
+ if (!found || !this.fileFieldDetection)
244
+ throw this.fileNotFound(fileId);
245
+ const fileRow = await this.store.readFile(fileId);
246
+ let binding;
247
+ try {
248
+ binding = createFileShareBinding(fileRow);
249
+ }
250
+ catch {
251
+ throw this.fileNotFound(fileId);
252
+ }
253
+ await authorizeFileParentRead(req, binding, {
254
+ resourcesRegistry: this.resourcesRegistry, repositoriesRegistry: this.repositoriesRegistry,
255
+ executionContextCreator: this.executionContextCreator, authorizationsService: this.authorizationsService,
256
+ errorBuilder: this.errorBuilder, fileFieldDetection: this.fileFieldDetection,
257
+ });
258
+ return { fileId, found };
259
+ }
260
+ async attestingSealOf(fileId) {
261
+ for (const seal of await this.store.attestingFile(fileId)) {
262
+ const subject = { resourceType: seal.linkedResourceType, resourceId: seal.linkedResourceId };
263
+ if (await this.recordNamesSeal(subject, seal._id))
264
+ return { sealId: seal._id, seal, subject };
265
+ }
266
+ for (const retained of await this.store.retainedAttestingFile(fileId)) {
267
+ const subject = { resourceType: retained.resourceType, resourceId: retained.resourceId };
268
+ if (await this.recordNamesSeal(subject, retained.sealId))
269
+ return { sealId: retained.sealId, seal: null, subject };
270
+ }
271
+ return null;
272
+ }
273
+ /**
274
+ * Whether the record a seal is about names it: the consumer stores a seal's id when the approved effect happens (a
275
+ * close's `signOffApprovalSealId`, or the reopening that set it aside), so a seal no field of its record names approved
276
+ * nothing that took effect. Read as the system; nothing of the record leaves.
277
+ */
278
+ async recordNamesSeal(subject, sealId) {
279
+ const resourceType = subject.resourceType;
280
+ if (!this.resourceIsRegistered(resourceType))
281
+ return false;
282
+ const record = await this.repositoriesRegistry.readUnscopedByKey(resourceType, { _id: subject.resourceId });
283
+ return record !== null && valueNamesId(record, sealId, RECORD_SEAL_REFERENCE_DEPTH);
284
+ }
285
+ fileNotFound(fileId) {
286
+ return this.errorBuilder.buildError(ErrorType.NOT_FOUND, undefined, {
287
+ customMessageReference: ErrorCustomMessageReference.FILE_NOT_FOUND, context: { fileId },
288
+ });
289
+ }
290
+ /** Whether the registry knows a resource type, asked without letting an unknown name throw a 500. */
291
+ resourceIsRegistered(resourceType) {
292
+ try {
293
+ return !!this.resourcesRegistry.getResourceConfiguration(resourceType);
294
+ }
295
+ catch {
296
+ return false;
297
+ }
298
+ }
299
+ /**
300
+ * The approval's currency over its record (#2914), as the operator's door computes it. Undefined when it cannot be
301
+ * asked here (the presentation cannot be read, or no system access is bound): the view then says nothing about
302
+ * currency, which is never read as current.
303
+ */
304
+ async currencyOf(seal) {
305
+ const systemAccess = this.systemAccess;
306
+ if (!systemAccess)
307
+ return undefined;
308
+ const read = await readApprovalSealPresentation(seal, {
309
+ readFileRow: (fileId) => this.store.readFile(fileId), files: this.files, contexts: this.executionContextCreator,
310
+ });
311
+ if (read.outcome === ApprovalSealPresentationReadOutcome.TAMPERED || read.outcome === ApprovalSealPresentationReadOutcome.UNREADABLE) {
312
+ this.logger.error('[ApprovalSeals] A seal\'s presentation could not be read for its record\'s view; its currency is not shown', {
313
+ sealId: seal._id, fileId: read.fileId, outcome: read.outcome, ...(read.outcome === ApprovalSealPresentationReadOutcome.UNREADABLE ? { reason: read.reason } : {}),
314
+ });
315
+ return undefined;
316
+ }
317
+ try {
318
+ return await assessApprovalSealCurrency(seal, read.outcome === ApprovalSealPresentationReadOutcome.CONTENT ? read.content : undefined, {
319
+ readRecord: (resourceType, resourceId) => systemAccess.readAsSystem(resourceType, { _id: resourceId }, { suppressSystemAudit: true })
320
+ .catch((error) => {
321
+ if (isWildoBackendError(error) && error.type === ErrorType.NOT_FOUND)
322
+ return null;
323
+ throw error;
324
+ }),
325
+ });
326
+ }
327
+ catch (error) {
328
+ this.logger.error('[ApprovalSeals] A seal\'s currency could not be computed for its record\'s view; it is not shown', { sealId: seal._id, error: describeCaughtError(error) });
329
+ return undefined;
330
+ }
331
+ }
332
+ /** The `files` row of the copy a seal names, when it is linked to the seal and servable; null otherwise. */
333
+ async servableCopyRow(seal) {
334
+ const copy = seal.manifestedCopy;
335
+ const fileId = copy !== null && typeof copy === 'object' ? copy.fileId : undefined;
336
+ if (typeof fileId !== 'string')
337
+ return null;
338
+ const row = await this.store.readFile(fileId);
339
+ return row && row.linkedResourceId === seal._id && FILE_DOWNLOADABLE_STATUSES.has(row.status) ? row : null;
340
+ }
341
+ async manifestedCopyServable(seal) {
342
+ return seal.status === ApprovalSealStatus.SEALED && !!this.files && (await this.servableCopyRow(seal)) !== null;
343
+ }
344
+ notFound(params, reason) {
345
+ return this.errorBuilder.buildError(ErrorType.NOT_FOUND, undefined, {
346
+ customMessageReference: ErrorCustomMessageReference.NOT_FOUND, context: { reason, sealId: params.sealId },
347
+ });
348
+ }
349
+ };
350
+ ApprovalSealsOnRecordControllerBackendService = __decorate([
351
+ injectable(),
352
+ __param(0, inject(SAAS_SERVICE_TYPES.Logger)),
353
+ __param(1, inject(SAAS_SERVICE_TYPES.ErrorBuilderService)),
354
+ __param(2, inject(SAAS_SERVICE_TYPES.ErrorHandlerService)),
355
+ __param(3, inject(SAAS_SERVICE_TYPES.RepositoriesRegistryHandlerService)),
356
+ __param(4, inject(SAAS_SERVICE_TYPES.ResourceRegistryService)),
357
+ __param(5, inject(SAAS_SERVICE_TYPES.ExecutionContextCreatorBackendService)),
358
+ __param(6, inject(SAAS_SERVICE_TYPES.AuthorizationsService)),
359
+ __param(7, inject(SAAS_SERVICE_TYPES.SystemAccessService)),
360
+ __param(7, optional()),
361
+ __param(8, inject(SAAS_SERVICE_TYPES.FilesBackendService)),
362
+ __param(8, optional()),
363
+ __param(9, inject(SAAS_SERVICE_TYPES.AppConfigService)),
364
+ __param(9, optional()),
365
+ __param(10, inject(SAAS_SERVICE_TYPES.FileFieldDetectionService)),
366
+ __param(10, optional()),
367
+ __metadata("design:paramtypes", [LoggerService,
368
+ ErrorBuilderBackendService,
369
+ ErrorHandlerService,
370
+ RepositoriesRegistryHandlerBackendService, Function, Function, Function, Object, Object, Object, Function])
371
+ ], ApprovalSealsOnRecordControllerBackendService);
372
+ export { ApprovalSealsOnRecordControllerBackendService };
373
+ //# sourceMappingURL=approval-seals-on-record.backend.controller.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"approval-seals-on-record.backend.controller.js","sourceRoot":"","sources":["../../../../src/compliance/approval-seals/approval-seals-on-record.backend.controller.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * # The record's view of its approval seal (#2913)\n *\n * - `GET /approval-seals/:sealId/records/:resourceType/:resourceId/:field` → how the record shows the seal its `field`\n * stores: who approved, when, with which meaning, whether it is still current over the record, its deliverable.\n * - `…/manifested-copy` → the deliverable's manifested copy (#2907), as a PDF.\n * - `GET /approval-seals/files/:fileId` (+ `/manifested-copy`) → the seal that attests a FILE, the deliverable a person\n * approved by its hash: admitted through the READ of the record the file belongs to (the files door's own\n * `authorizeFileParentRead`, itself `authorizeRecordReadAsCaller`), and only for a seal its own record still names.\n * - `GET /approval-seals/decisions/:decisionRef` → the seal of the signed-in person's own answer to a decision, for the\n * decision cards and the inbox: the decider reads the seal of their act, nobody else.\n *\n * ## Who is admitted, and to what\n *\n * Exactly the callers the RECORD's own READ operation admits, with their own credentials, through the routes that\n * operation declares (`authorizeRecordReadAsCaller`, the admission a file in a record's field already uses). On top of\n * that, three facts must hold, and any that does not answers the same 404 as a record that does not exist:\n *\n * 1. the record's `field` holds the seal's id: a seal is shown only where the consumer stored it, which it does when the\n * approved effect happened, so a seal whose effect failed (a close that moved while it was being signed) never reads\n * as approving the record;\n * 2. the seal is ABOUT that record (`linkedResourceType` / `linkedResourceId`): a field cannot be pointed at another\n * record's seal;\n * 3. for the copy, the seal still owns a servable copy whose bytes match the hash recorded for them.\n *\n * Read-only, and nothing is recorded on the trail: the view hands the record's reader the approver's printed name, which\n * a record that stores a decider (`closes.signedOffByUserId`) already shows them, and the record's own READ is the\n * access decision. Currency names fields, never values.\n *\n * assurance-control: WILDO.AI.HUMAN_OVERSIGHT, WILDO.AUDIT.EVIDENCE_INTEGRITY — the person's approval of a record is shown where the record is read, with whether it still describes the record, computed from the seal, never assumed.\n * assurance-control: WILDO.ACCESS.LEAST_PRIVILEGE — a seal is shown only to a caller the record's own READ admits, and only for the seal that record stores.\n */\nimport { createHash } from 'crypto';\nimport { injectable, inject, optional } from 'inversify';\nimport type { Request, Response } from 'express';\nimport {\n ApprovalSealOfDecisionParamsSchema,\n ApprovalSealOnFileParamsSchema,\n ApprovalSealOnRecordParamsSchema,\n ApprovalSealStatus,\n BackendApiType,\n ErrorCustomMessageReference,\n ErrorType,\n FILE_DOWNLOADABLE_STATUSES,\n HttpMethod,\n ManualControllerRouteKey,\n ResourcePrimaryScope,\n isWildoBackendError,\n type ApprovalSeal,\n type ApprovalSealOnRecordParams,\n type ApprovalSealSubjectCurrencyAssessment,\n type File,\n type FileStatus,\n type ResourceType,\n} from '@wildo-ai/saas-models';\n\nimport { SAAS_SERVICE_TYPES } from '~wildo-backend-lib/inject/models/service-types';\nimport { LoggerService } from '~wildo-backend-lib/monitoring/logger/services/logger.service';\nimport { ErrorBuilderBackendService } from '~wildo-backend-lib/errors/services/error-builder.backend.service';\nimport { ErrorHandlerService } from '~wildo-backend-lib/errors/services/error-handler.backend.service';\nimport { AbstractControllerBackendService } from '~wildo-backend-lib/base-classes/services/controllers/abstract-controller.backend.service';\nimport { RepositoriesRegistryHandlerBackendService } from '~wildo-backend-lib/base-classes/services/repositories/repositories-registry-handler.backend.service';\nimport type { ResourcesRegistryBackendService } from '~wildo-backend-lib/resources/services/resources-registry.backend.service';\nimport type { ExecutionContextCreatorBackendService } from '~wildo-backend-lib/runtime/services/execution-context-creator.backend.service';\nimport type { AuthorizationsBackendService } from '~wildo-backend-lib/security/authorizations/services/authorizations.backend.service';\nimport type { SystemAccessBackendService } from '~wildo-backend-lib/base-classes/services/services/system-access.backend.service';\nimport type { FilesBackendService } from '~wildo-backend-lib/files/services/files.backend.service';\nimport type { AppConfigurationService } from '~wildo-backend-lib/config/services/app-configuration.backend.service';\nimport { describeCaughtError } from '~wildo-backend-lib/errors/utils/error-log-level-classification.backend.utils';\nimport { authorizeFileParentRead, authorizeRecordReadAsCaller } from '~wildo-backend-lib/files/utils/file-parent-read-context.backend';\nimport { createFileShareBinding, type FileShareBinding } from '~wildo-backend-lib/files/utils/file-share-binding.backend';\nimport type { FileFieldDetectionBackendService } from '~wildo-backend-lib/files/services/file-field-detection.backend.service';\nimport { ApprovalSealRelayStore } from './approval-seal-relay-store.backend';\nimport { assessApprovalSealCurrency } from './approval-seal-currency.backend';\nimport { ApprovalSealPresentationReadOutcome, readApprovalSealPresentation } from './approval-seal-presentation-read.backend';\nimport { approvalSealPublicCheckUrlOf } from './approval-seal-public-check.backend';\nimport { approvalSealOnRecordViewOf } from './approval-seal-on-record.backend';\n\n/** A stored id as a string: ids are strings in SQL and ObjectIds in Mongo, and a populated object is never an id. */\nfunction storedIdOf(value: unknown): string | undefined {\n if (typeof value === 'string') return value;\n if (value && typeof value === 'object' && 'toHexString' in value && typeof (value as { toHexString: unknown }).toHexString === 'function') {\n return (value as { toHexString: () => string }).toHexString();\n }\n return undefined;\n}\n\n/**\n * How deep in a record a seal's id is looked for: a top-level field (`signOffApprovalSealId`), or a member of an entry of\n * a top-level array (`reopenings[].previousSignOffApprovalSealId`). Bounded, so a record's size bounds the search.\n */\nconst RECORD_SEAL_REFERENCE_DEPTH = 2;\n\n/** Whether a stored value names `id`, within `depth` levels of objects and arrays. */\nfunction valueNamesId(value: unknown, id: string, depth: number): boolean {\n if (storedIdOf(value) === id) return true;\n if (depth <= 0 || value === null || typeof value !== 'object' || value instanceof Date) return false;\n const members = Array.isArray(value) ? value : Object.values(value as Record<string, unknown>);\n return members.some((member) => valueNamesId(member, id, Array.isArray(value) ? depth : depth - 1));\n}\n\n/** The seal that attests a file and that its record names; `seal` is null for one its approver's erasure retained. */\ninterface AttestingSeal {\n readonly sealId: string;\n readonly seal: ApprovalSeal | null;\n readonly subject: { readonly resourceType: string; readonly resourceId: string };\n}\n\n/** The seal a request asks about, found and checked against the record it names; see the file header. */\ninterface AdmittedSeal {\n readonly params: ApprovalSealOnRecordParams;\n /** The live seal; null when it is retained after its approver's erasure. */\n readonly seal: ApprovalSeal | null;\n readonly retained: boolean;\n}\n\n@injectable()\nexport class ApprovalSealsOnRecordControllerBackendService extends AbstractControllerBackendService {\n private readonly store: ApprovalSealRelayStore;\n\n constructor(\n @inject(SAAS_SERVICE_TYPES.Logger) logger: LoggerService,\n @inject(SAAS_SERVICE_TYPES.ErrorBuilderService) errorBuilder: ErrorBuilderBackendService,\n @inject(SAAS_SERVICE_TYPES.ErrorHandlerService) errorHandler: ErrorHandlerService,\n @inject(SAAS_SERVICE_TYPES.RepositoriesRegistryHandlerService) private readonly repositoriesRegistry: RepositoriesRegistryHandlerBackendService,\n @inject(SAAS_SERVICE_TYPES.ResourceRegistryService) private readonly resourcesRegistry: ResourcesRegistryBackendService,\n @inject(SAAS_SERVICE_TYPES.ExecutionContextCreatorBackendService) private readonly executionContextCreator: ExecutionContextCreatorBackendService,\n @inject(SAAS_SERVICE_TYPES.AuthorizationsService) private readonly authorizationsService: AuthorizationsBackendService,\n /** The currency check reads the record as the system, as the operator's door does, so both compare the same values. */\n @inject(SAAS_SERVICE_TYPES.SystemAccessService) @optional() private readonly systemAccess?: Pick<SystemAccessBackendService, 'readAsSystem'>,\n /** A presentation moved to a frozen file, and the manifested copy, are read through the files service. */\n @inject(SAAS_SERVICE_TYPES.FilesBackendService) @optional() private readonly files?: Pick<FilesBackendService, 'readFileBytes'>,\n @inject(SAAS_SERVICE_TYPES.AppConfigService) @optional() private readonly appConfig?: Pick<AppConfigurationService, 'config' | 'isInitialized'>,\n /** A file's view admits its reader through the files door's own check of the field the file is in. */\n @inject(SAAS_SERVICE_TYPES.FileFieldDetectionService) @optional() private readonly fileFieldDetection?: FileFieldDetectionBackendService,\n ) {\n super(logger, errorBuilder, errorHandler, 'ApprovalSealsOnRecordController');\n this.store = new ApprovalSealRelayStore(repositoriesRegistry);\n }\n\n protected setupRoutes(): void {\n this.mountManualRoute(ManualControllerRouteKey.APPROVAL_SEAL_ON_RECORD, HttpMethod.GET, (req, res) => this.handleView(req, res));\n this.mountManualRoute(ManualControllerRouteKey.APPROVAL_SEAL_ON_RECORD_MANIFESTED_COPY, HttpMethod.GET, (req, res) => this.handleManifestedCopy(req, res));\n this.mountManualRoute(ManualControllerRouteKey.APPROVAL_SEAL_ON_FILE, HttpMethod.GET, (req, res) => this.handleFileView(req, res));\n this.mountManualRoute(ManualControllerRouteKey.APPROVAL_SEAL_ON_FILE_MANIFESTED_COPY, HttpMethod.GET, (req, res) => this.handleFileManifestedCopy(req, res));\n this.mountManualRoute(ManualControllerRouteKey.APPROVAL_SEAL_OF_DECISION, HttpMethod.GET, (req, res) => this.handleDecisionView(req, res));\n }\n\n /** GET …/:field — the record's view of the seal it stores. */\n private async handleView(req: Request, res: Response): Promise<void> {\n const { params, seal, retained } = await this.admit(req);\n const view = await this.viewOf(params.sealId, seal, retained, { resourceType: params.resourceType, resourceId: params.resourceId }, true);\n res.set('Cache-Control', 'no-store');\n res.json(view);\n }\n\n /** GET …/:field/manifested-copy — the copy, checked against its recorded hash before a byte leaves. */\n private async handleManifestedCopy(req: Request, res: Response): Promise<void> {\n const { params, seal } = await this.admit(req);\n await this.sendManifestedCopy(seal, res, () => this.notFound(params, 'approval_seal_manifested_copy_not_servable'));\n }\n\n /**\n * GET /approval-seals/files/:fileId — the seal that attests a file (#2913): the deliverable a person approved by its hash.\n * Without currency, which is a statement about the record the approval is about, and which a reader of the file may not\n * be able to read.\n */\n private async handleFileView(req: Request, res: Response): Promise<void> {\n const { found } = await this.admitFile(req);\n const view = await this.viewOf(found.sealId, found.seal, found.seal === null, found.subject, false);\n res.set('Cache-Control', 'no-store');\n res.json(view);\n }\n\n /** GET /approval-seals/files/:fileId/manifested-copy — that file's manifested copy. */\n private async handleFileManifestedCopy(req: Request, res: Response): Promise<void> {\n const { fileId, found } = await this.admitFile(req);\n await this.sendManifestedCopy(found.seal, res, () => this.fileNotFound(fileId));\n }\n\n /**\n * GET /approval-seals/decisions/:decisionRef — the seal of the signed-in person's own answer to a decision (#2913). The\n * person who decided may always see the seal of their act (it is in their subject export); nobody else is answered.\n *\n * assurance-control: WILDO.ACCESS.LEAST_PRIVILEGE — a decision's seal is answered to the person who decided, and to nobody else through this door.\n */\n private async handleDecisionView(req: Request, res: Response): Promise<void> {\n const { decisionRef } = ApprovalSealOfDecisionParamsSchema.parse(req.params);\n const executionContext = await this.executionContextCreator.createExecutionContextForManualController(req);\n const userId = (executionContext.initiatorIds as { userId?: string } | undefined)?.userId;\n const notFound = () => this.errorBuilder.buildError(ErrorType.NOT_FOUND, executionContext, {\n customMessageReference: ErrorCustomMessageReference.NOT_FOUND, context: { reason: 'approval_seal_of_decision_not_found', decisionRef },\n });\n if (!userId) throw notFound();\n const seal = await this.store.latestOfDecisionBy(decisionRef, userId);\n if (!seal) throw notFound();\n const view = await this.viewOf(seal._id, seal, false, { resourceType: seal.linkedResourceType, resourceId: seal.linkedResourceId }, true);\n res.set('Cache-Control', 'no-store');\n res.json(view);\n }\n\n /** What a reader is told about one seal: its successor, its currency when asked for, its copy, its public check. */\n private async viewOf(\n sealId: string,\n seal: ApprovalSeal | null,\n retained: boolean,\n subject: { readonly resourceType: string; readonly resourceId: string },\n withCurrency: boolean,\n ): Promise<ReturnType<typeof approvalSealOnRecordViewOf>> {\n const successor = seal ? await this.store.successorOf(sealId, subject) : null;\n const currency = seal && withCurrency ? await this.currencyOf(seal) : undefined;\n const manifestedCopyServable = seal ? await this.manifestedCopyServable(seal) : false;\n const publicCheckUrl = approvalSealPublicCheckUrlOf(this.appConfig?.isInitialized()\n ? this.appConfig.config.runtime?.endPoints?.[BackendApiType.MAIN_BACKEND_API]?.publicUrl\n : undefined);\n return approvalSealOnRecordViewOf({\n sealId, seal, retained, successor, manifestedCopyServable,\n ...(currency ? { currency } : {}), ...(publicCheckUrl ? { publicCheckUrl } : {}),\n });\n }\n\n /** Send a SEALED seal's manifested copy, checked against its recorded hash before a byte leaves. */\n private async sendManifestedCopy(seal: ApprovalSeal | null, res: Response, notFound: () => unknown): Promise<void> {\n if (!seal || seal.status !== ApprovalSealStatus.SEALED || !this.files) throw notFound();\n const fileRow = await this.servableCopyRow(seal);\n if (!fileRow) throw notFound();\n const context = await this.executionContextCreator.createForInternalSystemOperation();\n const bytes = await this.files.readFileBytes(fileRow as unknown as File, context);\n const recorded = typeof fileRow.contentSha256 === 'string' ? fileRow.contentSha256 : undefined;\n if (!recorded || createHash('sha256').update(bytes).digest('hex') !== recorded) {\n this.logger.error('[ApprovalSeals] A manifested copy does not match the hash recorded for it; it is not served', { sealId: seal._id, fileId: fileRow.fileId });\n throw this.errorBuilder.buildError(ErrorType.CONFLICT, undefined, {\n customMessageReference: ErrorCustomMessageReference.CONFLICT, context: { reason: 'approval_seal_manifested_copy_mismatch', sealId: seal._id },\n });\n }\n res.setHeader('Content-Type', 'application/pdf');\n res.setHeader('Content-Disposition', `attachment; filename=\"approval-${seal._id}.pdf\"`);\n res.setHeader('Cache-Control', 'no-store');\n res.setHeader('X-Content-Type-Options', 'nosniff');\n res.end(bytes);\n }\n\n /**\n * Find the seal, check it is about the record the request names, and admit the caller through that record's READ, the\n * record's field holding the seal's id. Every refusal is the same 404.\n */\n private async admit(req: Request): Promise<AdmittedSeal> {\n const params = ApprovalSealOnRecordParamsSchema.parse(req.params);\n const notFound = () => this.notFound(params, 'approval_seal_not_shown_on_record');\n const seal = await this.store.read(params.sealId);\n const subject = seal\n ? { resourceType: seal.linkedResourceType, resourceId: seal.linkedResourceId }\n : await this.store.retainedSubjectOf(params.sealId);\n if (!subject || subject.resourceType !== params.resourceType || subject.resourceId !== params.resourceId) throw notFound();\n const resourceType = params.resourceType as ResourceType;\n if (!this.resourceIsRegistered(resourceType)) throw notFound();\n const configuration = this.resourcesRegistry.getResourceConfiguration(resourceType);\n // The scope the record lives in, from the record itself: it only shapes the routes its READ is tried through.\n const located = await this.repositoriesRegistry.readUnscopedByKey<Record<string, unknown>>(resourceType, { _id: params.resourceId });\n if (!located) throw notFound();\n const primaryScope = configuration.resourcePrimaryScope as ResourcePrimaryScope;\n await authorizeRecordReadAsCaller(req, {\n resourceType: params.resourceType,\n resourceId: params.resourceId,\n primaryScope,\n applicationId: storedIdOf(located.applicationId) ?? (seal ? String(seal.applicationId) : ''),\n organizationId: storedIdOf(located.organizationId) ?? null,\n userId: storedIdOf(located.userId) ?? null,\n }, (row) => storedIdOf(row[params.field]) === params.sealId, {\n resourcesRegistry: this.resourcesRegistry, repositoriesRegistry: this.repositoriesRegistry,\n executionContextCreator: this.executionContextCreator, authorizationsService: this.authorizationsService,\n }, notFound);\n return { params, seal, retained: !seal };\n }\n\n /**\n * The seal that attests a file and that its own record still names, then the caller admitted through the READ of the\n * record the FILE belongs to (`authorizeFileParentRead`, over `authorizeRecordReadAsCaller`). A live seal first, latest\n * decision first; then one its approver's erasure retained, known by id and subject only. A seal its record does not\n * name (an approved effect that failed) is passed over. Every refusal is the same 404, the files door's.\n */\n private async admitFile(req: Request): Promise<{ readonly fileId: string; readonly found: AttestingSeal }> {\n const { fileId } = ApprovalSealOnFileParamsSchema.parse(req.params);\n const found = await this.attestingSealOf(fileId);\n if (!found || !this.fileFieldDetection) throw this.fileNotFound(fileId);\n const fileRow = await this.store.readFile(fileId);\n let binding: FileShareBinding;\n try {\n binding = createFileShareBinding(fileRow as unknown as File);\n } catch {\n throw this.fileNotFound(fileId);\n }\n await authorizeFileParentRead(req, binding, {\n resourcesRegistry: this.resourcesRegistry, repositoriesRegistry: this.repositoriesRegistry,\n executionContextCreator: this.executionContextCreator, authorizationsService: this.authorizationsService,\n errorBuilder: this.errorBuilder, fileFieldDetection: this.fileFieldDetection,\n });\n return { fileId, found };\n }\n\n private async attestingSealOf(fileId: string): Promise<AttestingSeal | null> {\n for (const seal of await this.store.attestingFile(fileId)) {\n const subject = { resourceType: seal.linkedResourceType, resourceId: seal.linkedResourceId };\n if (await this.recordNamesSeal(subject, seal._id)) return { sealId: seal._id, seal, subject };\n }\n for (const retained of await this.store.retainedAttestingFile(fileId)) {\n const subject = { resourceType: retained.resourceType, resourceId: retained.resourceId };\n if (await this.recordNamesSeal(subject, retained.sealId)) return { sealId: retained.sealId, seal: null, subject };\n }\n return null;\n }\n\n /**\n * Whether the record a seal is about names it: the consumer stores a seal's id when the approved effect happens (a\n * close's `signOffApprovalSealId`, or the reopening that set it aside), so a seal no field of its record names approved\n * nothing that took effect. Read as the system; nothing of the record leaves.\n */\n private async recordNamesSeal(subject: { readonly resourceType: string; readonly resourceId: string }, sealId: string): Promise<boolean> {\n const resourceType = subject.resourceType as ResourceType;\n if (!this.resourceIsRegistered(resourceType)) return false;\n const record = await this.repositoriesRegistry.readUnscopedByKey<Record<string, unknown>>(resourceType, { _id: subject.resourceId });\n return record !== null && valueNamesId(record, sealId, RECORD_SEAL_REFERENCE_DEPTH);\n }\n\n private fileNotFound(fileId: string): unknown {\n return this.errorBuilder.buildError(ErrorType.NOT_FOUND, undefined, {\n customMessageReference: ErrorCustomMessageReference.FILE_NOT_FOUND, context: { fileId },\n });\n }\n\n /** Whether the registry knows a resource type, asked without letting an unknown name throw a 500. */\n private resourceIsRegistered(resourceType: ResourceType): boolean {\n try {\n return !!this.resourcesRegistry.getResourceConfiguration(resourceType);\n } catch {\n return false;\n }\n }\n\n /**\n * The approval's currency over its record (#2914), as the operator's door computes it. Undefined when it cannot be\n * asked here (the presentation cannot be read, or no system access is bound): the view then says nothing about\n * currency, which is never read as current.\n */\n private async currencyOf(seal: ApprovalSeal): Promise<ApprovalSealSubjectCurrencyAssessment | undefined> {\n const systemAccess = this.systemAccess;\n if (!systemAccess) return undefined;\n const read = await readApprovalSealPresentation(seal, {\n readFileRow: (fileId) => this.store.readFile(fileId), files: this.files, contexts: this.executionContextCreator,\n });\n if (read.outcome === ApprovalSealPresentationReadOutcome.TAMPERED || read.outcome === ApprovalSealPresentationReadOutcome.UNREADABLE) {\n this.logger.error('[ApprovalSeals] A seal\\'s presentation could not be read for its record\\'s view; its currency is not shown', {\n sealId: seal._id, fileId: read.fileId, outcome: read.outcome, ...(read.outcome === ApprovalSealPresentationReadOutcome.UNREADABLE ? { reason: read.reason } : {}),\n });\n return undefined;\n }\n try {\n return await assessApprovalSealCurrency(seal, read.outcome === ApprovalSealPresentationReadOutcome.CONTENT ? read.content : undefined, {\n readRecord: (resourceType, resourceId) => systemAccess.readAsSystem<Record<string, unknown>>(resourceType as ResourceType, { _id: resourceId }, { suppressSystemAudit: true } as never)\n .catch((error: unknown) => {\n if (isWildoBackendError(error) && error.type === ErrorType.NOT_FOUND) return null;\n throw error;\n }),\n });\n } catch (error) {\n this.logger.error('[ApprovalSeals] A seal\\'s currency could not be computed for its record\\'s view; it is not shown', { sealId: seal._id, error: describeCaughtError(error) });\n return undefined;\n }\n }\n\n /** The `files` row of the copy a seal names, when it is linked to the seal and servable; null otherwise. */\n private async servableCopyRow(seal: ApprovalSeal): Promise<Record<string, unknown> | null> {\n const copy = seal.manifestedCopy as unknown;\n const fileId = copy !== null && typeof copy === 'object' ? (copy as { fileId?: unknown }).fileId : undefined;\n if (typeof fileId !== 'string') return null;\n const row = await this.store.readFile(fileId);\n return row && row.linkedResourceId === seal._id && FILE_DOWNLOADABLE_STATUSES.has(row.status as FileStatus) ? row : null;\n }\n\n private async manifestedCopyServable(seal: ApprovalSeal): Promise<boolean> {\n return seal.status === ApprovalSealStatus.SEALED && !!this.files && (await this.servableCopyRow(seal)) !== null;\n }\n\n private notFound(params: Pick<ApprovalSealOnRecordParams, 'sealId'>, reason: string): unknown {\n return this.errorBuilder.buildError(ErrorType.NOT_FOUND, undefined, {\n customMessageReference: ErrorCustomMessageReference.NOT_FOUND, context: { reason, sealId: params.sealId },\n });\n }\n}\n"]}
@@ -1,4 +1,4 @@
1
- import { HumanDecision_SentCardContent, type HumanDecision_Answer, type HumanDecision_PendingCard, type HumanDecision_Presentation, type ApprovalSealRecordPresentation, type HumanDecision_PresentedDeliverable, type HumanDecision_SealedPresentation, type HumanDecision_Statement, type HumanDecisionPresentationSource } from '@wildo-ai/saas-models';
1
+ import { HumanDecision_SentCardContent, type HumanDecision_Answer, type HumanDecision_PendingCard, type HumanDecision_Presentation, type ApprovalSealRecordPresentation, type HumanDecision_PresentedDeliverable, type HumanDecision_PresentedRecord, type HumanDecision_SealedPresentation, type HumanDecision_Statement, type HumanDecisionPresentationSource } from '@wildo-ai/saas-models';
2
2
  import type { AppConfigurationService } from '../../config/services/app-configuration.backend.service';
3
3
  /**
4
4
  * Domain separation for the presentation-id key, derived from the decision-presentation purpose key (#2893; until then,
@@ -22,7 +22,9 @@ export declare function decisionPresentationIdOf(presentation: HumanDecision_Pre
22
22
  * {@link resolveDecisionDeliverables}, joins the presentation and only the presentation: the card has no field of its own
23
23
  * for it, so nothing reads it anywhere but where the id covers it.
24
24
  */
25
- export declare function withDecisionPresentation<TCard extends HumanDecisionPresentationSource>(card: TCard, key: Buffer, deliverables?: readonly HumanDecision_PresentedDeliverable[]): TCard & Required<Pick<HumanDecision_PendingCard, 'presentation' | 'presentationId'>>;
25
+ export declare function withDecisionPresentation<TCard extends HumanDecisionPresentationSource>(card: TCard, key: Buffer, deliverables?: readonly HumanDecision_PresentedDeliverable[],
26
+ /** #2914: the record the call acts on, with the fields its approval covers ({@link resolveDecisionSubjectRecord}). */
27
+ subjectRecord?: HumanDecision_PresentedRecord): TCard & Required<Pick<HumanDecision_PendingCard, 'presentation' | 'presentationId'>>;
26
28
  /**
27
29
  * Domain separation for a file deliverable's salt (#2904), derived under the presentation-id key.
28
30
  *
@@ -88,6 +90,31 @@ export declare function resolveDecisionDeliverables(params: {
88
90
  /** Told when a read failed, so a NOT_BOUND that hides an outage is still seen. */
89
91
  readonly onReadFailure?: (error: unknown) => void;
90
92
  }): Promise<ResolvedDecisionDeliverables>;
93
+ /**
94
+ * The record a tool approval is about, with the fields its tool declares the approval covers (#2914,
95
+ * `FunctionTool.sealedRecordFields`): the row named by the call's `rowIdArgument`, read as the system when the card is
96
+ * built, its declared values projected like presented arguments. The announcement, the history, the inbox and the act all
97
+ * resolve it here, so they agree on the presentation and its id: a card read before one of those values changed carries
98
+ * another id, and its answer is refused as stale.
99
+ *
100
+ * Undefined, never a guess, when the tool declares no field set, the call names no row, the row cannot be read, or the
101
+ * row belongs to another organization than the decision (a card must not show another tenant's values). Each reads
102
+ * alike, so the card discloses nothing about a row its reader may not see.
103
+ *
104
+ * assurance-control: WILDO.AI.HUMAN_OVERSIGHT, WILDO.AUDIT.EVIDENCE_INTEGRITY — the record values an approval covers are shown to the person and sealed with their answer, so whether the approval still holds can be computed later.
105
+ */
106
+ export declare function resolveDecisionSubjectRecord(params: {
107
+ readonly sealedFields: readonly string[] | undefined;
108
+ readonly resourceType: string | undefined;
109
+ readonly rowIdArgument: string | undefined;
110
+ readonly toolArgs: Readonly<Record<string, unknown>> | undefined;
111
+ /** The organization the decision belongs to; absent for a decision of the application. */
112
+ readonly organizationId?: string;
113
+ /** Reads the row as the system; null when there is none. */
114
+ readonly readRow: (resourceType: string, rowId: string) => Promise<Readonly<Record<string, unknown>> | null>;
115
+ /** Told when a read failed, so an absent record that hides an outage is still seen. */
116
+ readonly onReadFailure?: (error: unknown) => void;
117
+ }): Promise<HumanDecision_PresentedRecord | undefined>;
91
118
  /** The presentation a seal binds: what was built, its id when one could be derived, and how its rendering is known. */
92
119
  export type SealedDecisionPresentation = HumanDecision_SealedPresentation;
93
120
  /** What checking an answer's echoed presentation came to. */