@pipeline-builder/api-core 4.1.13 → 4.1.15

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 (187) hide show
  1. package/README.md +3 -2
  2. package/lib/constants/index.d.ts +0 -1
  3. package/lib/constants/index.js +1 -2
  4. package/lib/errors/app-errors.d.ts +8 -3
  5. package/lib/errors/app-errors.js +10 -5
  6. package/lib/helpers/access-helpers.d.ts +4 -6
  7. package/lib/helpers/access-helpers.js +6 -8
  8. package/lib/helpers/index.d.ts +3 -3
  9. package/lib/helpers/index.js +4 -4
  10. package/lib/helpers/org-hierarchy-http.d.ts +11 -10
  11. package/lib/helpers/org-hierarchy-http.js +24 -35
  12. package/lib/helpers/org-hierarchy.d.ts +37 -3
  13. package/lib/helpers/org-hierarchy.js +45 -4
  14. package/lib/helpers/restore-helpers.d.ts +44 -24
  15. package/lib/helpers/restore-helpers.js +41 -52
  16. package/lib/helpers/sse-helpers.d.ts +29 -7
  17. package/lib/helpers/sse-helpers.js +36 -9
  18. package/lib/index.d.ts +16 -14
  19. package/lib/index.js +17 -15
  20. package/lib/middleware/assurance.d.ts +116 -0
  21. package/lib/middleware/assurance.js +176 -0
  22. package/lib/middleware/auth.d.ts +5 -427
  23. package/lib/middleware/auth.js +36 -789
  24. package/lib/middleware/ecosystem-guard.d.ts +47 -0
  25. package/lib/middleware/ecosystem-guard.js +60 -0
  26. package/lib/middleware/index.d.ts +11 -4
  27. package/lib/middleware/index.js +12 -5
  28. package/lib/middleware/jwt-verify.d.ts +52 -0
  29. package/lib/middleware/jwt-verify.js +105 -0
  30. package/lib/middleware/permission-gates.d.ts +133 -0
  31. package/lib/middleware/permission-gates.js +260 -0
  32. package/lib/middleware/revocation.d.ts +96 -0
  33. package/lib/middleware/revocation.js +162 -0
  34. package/lib/middleware/route-table.d.ts +11 -0
  35. package/lib/middleware/route-table.js +4 -1
  36. package/lib/middleware/service-tokens.d.ts +83 -0
  37. package/lib/middleware/service-tokens.js +158 -0
  38. package/lib/middleware/step-up.d.ts +2 -2
  39. package/lib/middleware/step-up.js +8 -7
  40. package/lib/middleware/system-org.d.ts +20 -0
  41. package/lib/middleware/system-org.js +25 -0
  42. package/lib/openapi/routes/plugin-routes.js +133 -10
  43. package/lib/openapi/schema-registry.js +2 -5
  44. package/lib/routes/index.d.ts +1 -1
  45. package/lib/routes/index.js +2 -2
  46. package/lib/services/api-key-exchange.js +7 -5
  47. package/lib/services/audit-spool.d.ts +34 -10
  48. package/lib/services/audit-spool.js +75 -34
  49. package/lib/services/cache-service.d.ts +7 -0
  50. package/lib/services/cache-service.js +1 -1
  51. package/lib/services/circuit-breaker.d.ts +14 -1
  52. package/lib/services/circuit-breaker.js +36 -10
  53. package/lib/services/compliance-client.d.ts +10 -2
  54. package/lib/services/compliance-client.js +17 -10
  55. package/lib/services/compliance-event-subscriber.js +6 -6
  56. package/lib/services/durable-event-bus.js +8 -11
  57. package/lib/services/ecosystem-notify-client.d.ts +32 -0
  58. package/lib/services/ecosystem-notify-client.js +71 -0
  59. package/lib/services/env-redis.d.ts +4 -1
  60. package/lib/services/env-redis.js +32 -3
  61. package/lib/services/http-client.d.ts +19 -0
  62. package/lib/services/http-client.js +98 -19
  63. package/lib/services/index.d.ts +25 -24
  64. package/lib/services/index.js +25 -22
  65. package/lib/services/jwks-cache.js +5 -4
  66. package/lib/services/leader-lock.d.ts +22 -5
  67. package/lib/services/leader-lock.js +61 -6
  68. package/lib/services/quota.d.ts +9 -4
  69. package/lib/services/quota.js +34 -40
  70. package/lib/services/remote-audit-client.d.ts +28 -32
  71. package/lib/services/remote-audit-client.js +166 -49
  72. package/lib/services/retry-forever.js +5 -11
  73. package/lib/services/retry-strategy.d.ts +1 -1
  74. package/lib/services/retry-strategy.js +5 -4
  75. package/lib/services/scheduler.d.ts +8 -3
  76. package/lib/services/scheduler.js +17 -5
  77. package/lib/services/service-boot.d.ts +14 -13
  78. package/lib/services/service-boot.js +18 -15
  79. package/lib/services/service-keys.d.ts +1 -1
  80. package/lib/services/service-keys.js +12 -18
  81. package/lib/services/sse-ticket-store.d.ts +9 -1
  82. package/lib/services/sse-ticket-store.js +24 -9
  83. package/lib/services/system-notification.d.ts +27 -0
  84. package/lib/services/system-notification.js +50 -0
  85. package/lib/services/token-revocation.d.ts +50 -1
  86. package/lib/services/token-revocation.js +103 -3
  87. package/lib/types/audit-events.d.ts +6 -6
  88. package/lib/types/audit-events.js +1 -1
  89. package/lib/types/common.d.ts +32 -13
  90. package/lib/types/common.js +2 -1
  91. package/lib/types/ecosystem-notifications.d.ts +142 -0
  92. package/lib/types/ecosystem-notifications.js +210 -0
  93. package/lib/types/ecosystem.d.ts +77 -0
  94. package/lib/types/ecosystem.js +119 -0
  95. package/lib/types/error-codes.d.ts +81 -0
  96. package/lib/types/error-codes.js +108 -1
  97. package/lib/types/feature-flags.d.ts +21 -1
  98. package/lib/types/feature-flags.js +36 -2
  99. package/lib/types/index.d.ts +14 -9
  100. package/lib/types/index.js +15 -10
  101. package/lib/types/permissions.d.ts +46 -8
  102. package/lib/types/permissions.js +75 -12
  103. package/lib/types/plugin-catalog.d.ts +35 -0
  104. package/lib/types/plugin-catalog.js +39 -0
  105. package/lib/types/plugin-health.d.ts +102 -0
  106. package/lib/types/plugin-health.js +184 -0
  107. package/lib/types/quota-tiers.d.ts +15 -0
  108. package/lib/types/quota-tiers.js +23 -3
  109. package/lib/types/wire-vocabulary.d.ts +102 -0
  110. package/lib/types/wire-vocabulary.js +94 -0
  111. package/lib/utils/alias-resolver.js +2 -2
  112. package/lib/utils/audit.d.ts +5 -2
  113. package/lib/utils/audit.js +6 -3
  114. package/lib/utils/compliance-attributes.js +3 -4
  115. package/lib/utils/concurrency.d.ts +7 -0
  116. package/lib/utils/concurrency.js +13 -1
  117. package/lib/utils/env.d.ts +3 -4
  118. package/lib/utils/env.js +4 -5
  119. package/lib/utils/identity.d.ts +13 -4
  120. package/lib/utils/identity.js +18 -10
  121. package/lib/utils/index.d.ts +13 -10
  122. package/lib/utils/index.js +13 -10
  123. package/lib/utils/jwk.d.ts +4 -0
  124. package/lib/utils/jwk.js +13 -1
  125. package/lib/utils/logger.d.ts +1 -1
  126. package/lib/utils/logger.js +1 -1
  127. package/lib/utils/object.d.ts +11 -0
  128. package/lib/utils/object.js +24 -1
  129. package/lib/utils/params.d.ts +8 -22
  130. package/lib/utils/params.js +15 -43
  131. package/lib/utils/plugin-compliance.d.ts +98 -0
  132. package/lib/utils/plugin-compliance.js +78 -0
  133. package/lib/utils/proof-of-work.d.ts +69 -0
  134. package/lib/utils/proof-of-work.js +139 -0
  135. package/lib/utils/response.d.ts +16 -6
  136. package/lib/utils/response.js +18 -15
  137. package/lib/utils/retention.d.ts +17 -0
  138. package/lib/utils/retention.js +30 -0
  139. package/lib/utils/secret-encryption.d.ts +7 -7
  140. package/lib/utils/secret-encryption.js +9 -10
  141. package/lib/utils/secret-rotation.js +2 -2
  142. package/lib/utils/sensitive-patterns.js +4 -5
  143. package/lib/utils/service-registry.d.ts +15 -0
  144. package/lib/utils/service-registry.js +24 -0
  145. package/lib/utils/ssrf.d.ts +11 -2
  146. package/lib/utils/ssrf.js +112 -33
  147. package/lib/validation/ai-schemas.js +3 -2
  148. package/lib/validation/common-schemas.d.ts +0 -9
  149. package/lib/validation/common-schemas.js +1 -7
  150. package/lib/validation/dockerfile-static.d.ts +26 -0
  151. package/lib/validation/dockerfile-static.js +210 -0
  152. package/lib/validation/index.d.ts +13 -4
  153. package/lib/validation/index.js +14 -5
  154. package/lib/validation/message-schemas.js +6 -4
  155. package/lib/validation/pipeline-schemas.d.ts +8 -2
  156. package/lib/validation/pipeline-schemas.js +30 -4
  157. package/lib/validation/plugin-base-images.d.ts +20 -0
  158. package/lib/validation/plugin-base-images.js +17 -0
  159. package/lib/validation/plugin-catalog-detect.d.ts +86 -0
  160. package/lib/validation/plugin-catalog-detect.js +237 -0
  161. package/lib/validation/plugin-catalog-metadata.d.ts +141 -0
  162. package/lib/validation/plugin-catalog-metadata.js +162 -0
  163. package/lib/validation/plugin-heuristics.d.ts +41 -0
  164. package/lib/validation/plugin-heuristics.js +211 -0
  165. package/lib/validation/plugin-lint.d.ts +18 -0
  166. package/lib/validation/plugin-lint.js +136 -0
  167. package/lib/validation/plugin-name-confusable.d.ts +15 -0
  168. package/lib/validation/plugin-name-confusable.js +81 -0
  169. package/lib/validation/plugin-schemas.d.ts +40 -26
  170. package/lib/validation/plugin-schemas.js +26 -23
  171. package/lib/validation/plugin-spec-schema.d.ts +112 -0
  172. package/lib/validation/plugin-spec-schema.js +163 -0
  173. package/lib/validation/plugin-template-contract.d.ts +64 -0
  174. package/lib/validation/plugin-template-contract.js +113 -0
  175. package/package.json +69 -4
  176. package/lib/testing/mock-api-core.d.ts +0 -150
  177. package/lib/testing/mock-api-core.js +0 -246
  178. package/lib/testing/mock-drizzle.d.ts +0 -6
  179. package/lib/testing/mock-drizzle.js +0 -57
  180. package/lib/testing/route-coverage.d.ts +0 -64
  181. package/lib/testing/route-coverage.js +0 -183
  182. package/lib/testing/service-tokens.d.ts +0 -66
  183. package/lib/testing/service-tokens.js +0 -137
  184. package/lib/testing/tier-mock.d.ts +0 -44
  185. package/lib/testing/tier-mock.js +0 -65
  186. package/lib/testing/user-tokens.d.ts +0 -75
  187. package/lib/testing/user-tokens.js +0 -115
package/lib/index.js CHANGED
@@ -21,7 +21,7 @@
21
21
  * **Utilities**
22
22
  * - createLogger — Winston-based structured logger factory
23
23
  * - sendSuccess, sendError, sendPaginatedNested, sendBadRequest, sendInternalError — HTTP response helpers
24
- * - getParam, getOrgId — request parameter extraction
24
+ * - getParam — request parameter extraction
25
25
  * - parseQueryInt, parseQueryString — query string parsing
26
26
  * - getIdentity — identity extraction from requests
27
27
  * - actorId — the audit actor for a route context (one `system` sentinel)
@@ -54,25 +54,27 @@
54
54
  * PUBLIC SURFACE POLICY
55
55
  *
56
56
  * The root re-exports the ten sub-barrels, and each SUB-BARREL is the allow-list
57
- * for its own area — that is where a module is either `export *`'d or narrowed
58
- * to the names services may use. Internals are excluded there rather than here,
59
- * so the exclusion sits next to the code that defines them and cannot be missed
60
- * by someone adding a module.
57
+ * for its own area: every re-export NAMES what it exports (no `export *`), and
58
+ * a name is on the list only because some other package — a service, the
59
+ * frontend, a test, a deploy script — actually imports it. Internals are
60
+ * excluded there rather than here, so the exclusion sits next to the code that
61
+ * defines them. Adding an export to a module does NOT publish it; add it to
62
+ * the sub-barrel when a consumer needs it.
61
63
  *
62
- * Currently narrowed (the rest is the intended surface):
63
- * - `services/retry-strategy` — decision functions only; `parseRetryAfter` and
64
- * `addJitter` are backoff internals.
65
- * - `services/circuit-breaker` — `CircuitOpenError` + `resetCircuitBreakers`;
66
- * the `CircuitBreaker` class is wired by the shared HTTP client, not by
67
- * services.
64
+ * Deliberately internal, for example:
65
+ * - `services/retry-strategy` — decision functions only; `parseRetryAfter`,
66
+ * `addJitter` and the DEFAULT_* constants are backoff internals.
67
+ * - `services/circuit-breaker` — `resetCircuitBreakers` only; the
68
+ * `CircuitBreaker` class is wired by the shared HTTP client, not by services.
68
69
  * - `services/service-keys` — verify/inspect only; signing, the key bundle and
69
70
  * `_resetServiceKeysForTests` stay inside.
70
71
  * - `utils/jwk` — constants, shapes, decoders and the two signer-facing helpers
71
72
  * platform needs; `jwkThumbprint` / `publicKeyFromJwk` stay inside.
72
73
  *
73
- * api-core's own modules and its `testing/` helpers reach the excluded symbols
74
- * by DEEP import (`@pipeline-builder/api-core/lib/services/service-keys.js`),
75
- * which is also how the unit tests reach them.
74
+ * api-core's own modules reach the excluded symbols by relative import; unit
75
+ * tests reach them by DEEP import
76
+ * (`@pipeline-builder/api-core/lib/services/service-keys.js`). Test helpers are
77
+ * the separate `@pipeline-builder/api-core/testing` entry, never this barrel.
76
78
  */
77
79
  // Types
78
80
  export * from './types/index.js';
@@ -94,4 +96,4 @@ export * from './errors/index.js';
94
96
  export * from './validation/index.js';
95
97
  // OpenAPI
96
98
  export * from './openapi/index.js';
97
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsK0NBQStDO0FBQy9DLHNDQUFzQztBQUV0Qzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQWlERztBQUVIOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQXVCRztBQUVILFFBQVE7QUFDUixjQUFjLGtCQUFrQixDQUFDO0FBRWpDLFlBQVk7QUFDWixjQUFjLHNCQUFzQixDQUFDO0FBRXJDLFFBQVE7QUFDUixjQUFjLGtCQUFrQixDQUFDO0FBRWpDLFVBQVU7QUFDVixjQUFjLG9CQUFvQixDQUFDO0FBRW5DLFdBQVc7QUFDWCxjQUFjLHFCQUFxQixDQUFDO0FBRXBDLGFBQWE7QUFDYixjQUFjLHVCQUF1QixDQUFDO0FBRXRDLFNBQVM7QUFDVCxjQUFjLG1CQUFtQixDQUFDO0FBRWxDLFNBQVM7QUFDVCxjQUFjLG1CQUFtQixDQUFDO0FBRWxDLGFBQWE7QUFDYixjQUFjLHVCQUF1QixDQUFDO0FBRXRDLFVBQVU7QUFDVixjQUFjLG9CQUFvQixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLy8gQ29weXJpZ2h0IDIwMjYgUGlwZWxpbmUgQnVpbGRlciBDb250cmlidXRvcnNcbi8vIFNQRFgtTGljZW5zZS1JZGVudGlmaWVyOiBBcGFjaGUtMi4wXG5cbi8qKlxuICogQG1vZHVsZSBAcGlwZWxpbmUtYnVpbGRlci9hcGktY29yZVxuICpcbiAqIENvcmUgQVBJIHV0aWxpdGllcyBzaGFyZWQgYWNyb3NzIGFsbCBzZXJ2aWNlcy5cbiAqXG4gKiAqKk1pZGRsZXdhcmUqKlxuICogLSByZXF1aXJlQXV0aCwgcmVxdWlyZVN5c3RlbUFkbWluLCByZXF1aXJlRmVhdHVyZSDigJQgSldUIGF1dGhlbnRpY2F0aW9uXG4gKiAtIGlzU3lzdGVtQWRtaW4sIGlzU2VydmljZVByaW5jaXBhbCDigJQgYXV0aG9yaXphdGlvbiBoZWxwZXJzIChpc1N5c3RlbU9yZ0lkIHJlbWFpbnMgYXMgYSBjb250ZW50LW93bmVyIGNoZWNrLCBub3QgYSBwcml2aWxlZ2UgZ2F0ZSlcbiAqIC0gc2lnblNlcnZpY2VUb2tlbiwgZ2V0U2VydmljZUF1dGhIZWFkZXIg4oCUIGludGVyLXNlcnZpY2UgSldUIG1pbnRpbmdcbiAqXG4gKiAqKlR5cGVzKipcbiAqIC0gRXJyb3JDb2RlLCBFcnJvckNvZGVTdGF0dXMg4oCUIHN0YW5kYXJkaXplZCBlcnJvciBjb2RlIGVudW0gYW5kIHN0YXR1cyBtYXBwaW5nXG4gKiAtIFJlcXVlc3RJZGVudGl0eSDigJQgcGFyc2VkIEpXVCBpZGVudGl0eVxuICogLSBTZXJ2aWNlQ29uZmlnLCBSZXF1ZXN0T3B0aW9ucyDigJQgSFRUUCBjbGllbnQgdHlwZXNcbiAqIC0gUXVvdGFUeXBlLCBRdW90YUNoZWNrUmVzdWx0IOKAlCBxdW90YSBzZXJ2aWNlIHR5cGVzXG4gKiAtIFBpcGVsaW5lVHlwZSwgQ29tcHV0ZVR5cGUsIFZpc2liaWxpdHkg4oCUIHBpcGVsaW5lIGRvbWFpbiB0eXBlc1xuICogLSBGZWF0dXJlRmxhZ3Mg4oCUIGZlYXR1cmUgZmxhZyB0eXBlc1xuICpcbiAqICoqVXRpbGl0aWVzKipcbiAqIC0gY3JlYXRlTG9nZ2VyIOKAlCBXaW5zdG9uLWJhc2VkIHN0cnVjdHVyZWQgbG9nZ2VyIGZhY3RvcnlcbiAqIC0gc2VuZFN1Y2Nlc3MsIHNlbmRFcnJvciwgc2VuZFBhZ2luYXRlZE5lc3RlZCwgc2VuZEJhZFJlcXVlc3QsIHNlbmRJbnRlcm5hbEVycm9yIOKAlCBIVFRQIHJlc3BvbnNlIGhlbHBlcnNcbiAqIC0gZ2V0UGFyYW0sIGdldE9yZ0lkIOKAlCByZXF1ZXN0IHBhcmFtZXRlciBleHRyYWN0aW9uXG4gKiAtIHBhcnNlUXVlcnlJbnQsIHBhcnNlUXVlcnlTdHJpbmcg4oCUIHF1ZXJ5IHN0cmluZyBwYXJzaW5nXG4gKiAtIGdldElkZW50aXR5IOKAlCBpZGVudGl0eSBleHRyYWN0aW9uIGZyb20gcmVxdWVzdHNcbiAqIC0gYWN0b3JJZCDigJQgdGhlIGF1ZGl0IGFjdG9yIGZvciBhIHJvdXRlIGNvbnRleHQgKG9uZSBgc3lzdGVtYCBzZW50aW5lbClcbiAqIC0gZXJyb3JNZXNzYWdlIOKAlCBzYWZlIGVycm9yLXRvLXN0cmluZyBjb252ZXJzaW9uXG4gKlxuICogKipDb25zdGFudHMqKlxuICogLSBIVFRQIHN0YXR1cyBjb2RlcywgQUkgcHJvdmlkZXIgaWRlbnRpZmllcnMsIHRpbWUgY29uc3RhbnRzXG4gKlxuICogKipTZXJ2aWNlcyoqXG4gKiAtIEludGVybmFsSHR0cENsaWVudCwgY3JlYXRlU2FmZUNsaWVudCDigJQgaW50ZXJuYWwgc2VydmljZS10by1zZXJ2aWNlIEhUVFAgY2xpZW50XG4gKiAtIGNyZWF0ZVF1b3RhU2VydmljZSDigJQgcXVvdGEgZW5mb3JjZW1lbnQgY2xpZW50IGZhY3RvcnlcbiAqIC0gQ2FjaGVTZXJ2aWNlIOKAlCBpbi1tZW1vcnkgVFRMIGNhY2hlXG4gKiAtIENvbXBsaWFuY2VDbGllbnQg4oCUIGNvbXBsaWFuY2Ugc2VydmljZSBjbGllbnRcbiAqIC0gRW50aXR5RXZlbnRFbWl0dGVyIOKAlCBkb21haW4gZXZlbnQgcHViL3N1YlxuICogLSByZXRyeUZvcmV2ZXIg4oCUIGNhcHBlZC1iYWNrb2ZmIHdhaXQgZm9yIGEgZGVwZW5kZW5jeSB0aGF0IG11c3QgY29tZSB1cFxuICpcbiAqICoqRXJyb3JzKipcbiAqIC0gQXBwRXJyb3IsIE5vdEZvdW5kRXJyb3IsIEZvcmJpZGRlbkVycm9yIOKAlCB0eXBlZCBIVFRQIGVycm9yIGNsYXNzZXNcbiAqXG4gKiAqKlZhbGlkYXRpb24qKlxuICogLSBab2QtYmFzZWQgcmVxdWVzdCB2YWxpZGF0aW9uIHNjaGVtYXMgYW5kIG1pZGRsZXdhcmVcbiAqXG4gKiAqKlJvdXRlcyoqXG4gKiAtIEhlYWx0aCBjaGVjayByb3V0ZSBmYWN0b3J5XG4gKlxuICogKipPcGVuQVBJKipcbiAqIC0gU2NoZW1hIHJlZ2lzdHJ5IGFuZCBzcGVjIGdlbmVyYXRpb25cbiAqL1xuXG4vKipcbiAqIFBVQkxJQyBTVVJGQUNFIFBPTElDWVxuICpcbiAqIFRoZSByb290IHJlLWV4cG9ydHMgdGhlIHRlbiBzdWItYmFycmVscywgYW5kIGVhY2ggU1VCLUJBUlJFTCBpcyB0aGUgYWxsb3ctbGlzdFxuICogZm9yIGl0cyBvd24gYXJlYSDigJQgdGhhdCBpcyB3aGVyZSBhIG1vZHVsZSBpcyBlaXRoZXIgYGV4cG9ydCAqYCdkIG9yIG5hcnJvd2VkXG4gKiB0byB0aGUgbmFtZXMgc2VydmljZXMgbWF5IHVzZS4gSW50ZXJuYWxzIGFyZSBleGNsdWRlZCB0aGVyZSByYXRoZXIgdGhhbiBoZXJlLFxuICogc28gdGhlIGV4Y2x1c2lvbiBzaXRzIG5leHQgdG8gdGhlIGNvZGUgdGhhdCBkZWZpbmVzIHRoZW0gYW5kIGNhbm5vdCBiZSBtaXNzZWRcbiAqIGJ5IHNvbWVvbmUgYWRkaW5nIGEgbW9kdWxlLlxuICpcbiAqIEN1cnJlbnRseSBuYXJyb3dlZCAodGhlIHJlc3QgaXMgdGhlIGludGVuZGVkIHN1cmZhY2UpOlxuICogLSBgc2VydmljZXMvcmV0cnktc3RyYXRlZ3lgIOKAlCBkZWNpc2lvbiBmdW5jdGlvbnMgb25seTsgYHBhcnNlUmV0cnlBZnRlcmAgYW5kXG4gKiAgIGBhZGRKaXR0ZXJgIGFyZSBiYWNrb2ZmIGludGVybmFscy5cbiAqIC0gYHNlcnZpY2VzL2NpcmN1aXQtYnJlYWtlcmAg4oCUIGBDaXJjdWl0T3BlbkVycm9yYCArIGByZXNldENpcmN1aXRCcmVha2Vyc2A7XG4gKiAgIHRoZSBgQ2lyY3VpdEJyZWFrZXJgIGNsYXNzIGlzIHdpcmVkIGJ5IHRoZSBzaGFyZWQgSFRUUCBjbGllbnQsIG5vdCBieVxuICogICBzZXJ2aWNlcy5cbiAqIC0gYHNlcnZpY2VzL3NlcnZpY2Uta2V5c2Ag4oCUIHZlcmlmeS9pbnNwZWN0IG9ubHk7IHNpZ25pbmcsIHRoZSBrZXkgYnVuZGxlIGFuZFxuICogICBgX3Jlc2V0U2VydmljZUtleXNGb3JUZXN0c2Agc3RheSBpbnNpZGUuXG4gKiAtIGB1dGlscy9qd2tgIOKAlCBjb25zdGFudHMsIHNoYXBlcywgZGVjb2RlcnMgYW5kIHRoZSB0d28gc2lnbmVyLWZhY2luZyBoZWxwZXJzXG4gKiAgIHBsYXRmb3JtIG5lZWRzOyBgandrVGh1bWJwcmludGAgLyBgcHVibGljS2V5RnJvbUp3a2Agc3RheSBpbnNpZGUuXG4gKlxuICogYXBpLWNvcmUncyBvd24gbW9kdWxlcyBhbmQgaXRzIGB0ZXN0aW5nL2AgaGVscGVycyByZWFjaCB0aGUgZXhjbHVkZWQgc3ltYm9sc1xuICogYnkgREVFUCBpbXBvcnQgKGBAcGlwZWxpbmUtYnVpbGRlci9hcGktY29yZS9saWIvc2VydmljZXMvc2VydmljZS1rZXlzLmpzYCksXG4gKiB3aGljaCBpcyBhbHNvIGhvdyB0aGUgdW5pdCB0ZXN0cyByZWFjaCB0aGVtLlxuICovXG5cbi8vIFR5cGVzXG5leHBvcnQgKiBmcm9tICcuL3R5cGVzL2luZGV4LmpzJztcblxuLy8gQ29uc3RhbnRzXG5leHBvcnQgKiBmcm9tICcuL2NvbnN0YW50cy9pbmRleC5qcyc7XG5cbi8vIFV0aWxzXG5leHBvcnQgKiBmcm9tICcuL3V0aWxzL2luZGV4LmpzJztcblxuLy8gSGVscGVyc1xuZXhwb3J0ICogZnJvbSAnLi9oZWxwZXJzL2luZGV4LmpzJztcblxuLy8gU2VydmljZXNcbmV4cG9ydCAqIGZyb20gJy4vc2VydmljZXMvaW5kZXguanMnO1xuXG4vLyBNaWRkbGV3YXJlXG5leHBvcnQgKiBmcm9tICcuL21pZGRsZXdhcmUvaW5kZXguanMnO1xuXG4vLyBSb3V0ZXNcbmV4cG9ydCAqIGZyb20gJy4vcm91dGVzL2luZGV4LmpzJztcblxuLy8gRXJyb3JzXG5leHBvcnQgKiBmcm9tICcuL2Vycm9ycy9pbmRleC5qcyc7XG5cbi8vIFZhbGlkYXRpb25cbmV4cG9ydCAqIGZyb20gJy4vdmFsaWRhdGlvbi9pbmRleC5qcyc7XG5cbi8vIE9wZW5BUElcbmV4cG9ydCAqIGZyb20gJy4vb3BlbmFwaS9pbmRleC5qcyc7XG4iXX0=
99
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsK0NBQStDO0FBQy9DLHNDQUFzQztBQUV0Qzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQWlERztBQUVIOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBeUJHO0FBRUgsUUFBUTtBQUNSLGNBQWMsa0JBQWtCLENBQUM7QUFFakMsWUFBWTtBQUNaLGNBQWMsc0JBQXNCLENBQUM7QUFFckMsUUFBUTtBQUNSLGNBQWMsa0JBQWtCLENBQUM7QUFFakMsVUFBVTtBQUNWLGNBQWMsb0JBQW9CLENBQUM7QUFFbkMsV0FBVztBQUNYLGNBQWMscUJBQXFCLENBQUM7QUFFcEMsYUFBYTtBQUNiLGNBQWMsdUJBQXVCLENBQUM7QUFFdEMsU0FBUztBQUNULGNBQWMsbUJBQW1CLENBQUM7QUFFbEMsU0FBUztBQUNULGNBQWMsbUJBQW1CLENBQUM7QUFFbEMsYUFBYTtBQUNiLGNBQWMsdUJBQXVCLENBQUM7QUFFdEMsVUFBVTtBQUNWLGNBQWMsb0JBQW9CLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvLyBDb3B5cmlnaHQgMjAyNiBQaXBlbGluZSBCdWlsZGVyIENvbnRyaWJ1dG9yc1xuLy8gU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IEFwYWNoZS0yLjBcblxuLyoqXG4gKiBAbW9kdWxlIEBwaXBlbGluZS1idWlsZGVyL2FwaS1jb3JlXG4gKlxuICogQ29yZSBBUEkgdXRpbGl0aWVzIHNoYXJlZCBhY3Jvc3MgYWxsIHNlcnZpY2VzLlxuICpcbiAqICoqTWlkZGxld2FyZSoqXG4gKiAtIHJlcXVpcmVBdXRoLCByZXF1aXJlU3lzdGVtQWRtaW4sIHJlcXVpcmVGZWF0dXJlIOKAlCBKV1QgYXV0aGVudGljYXRpb25cbiAqIC0gaXNTeXN0ZW1BZG1pbiwgaXNTZXJ2aWNlUHJpbmNpcGFsIOKAlCBhdXRob3JpemF0aW9uIGhlbHBlcnMgKGlzU3lzdGVtT3JnSWQgcmVtYWlucyBhcyBhIGNvbnRlbnQtb3duZXIgY2hlY2ssIG5vdCBhIHByaXZpbGVnZSBnYXRlKVxuICogLSBzaWduU2VydmljZVRva2VuLCBnZXRTZXJ2aWNlQXV0aEhlYWRlciDigJQgaW50ZXItc2VydmljZSBKV1QgbWludGluZ1xuICpcbiAqICoqVHlwZXMqKlxuICogLSBFcnJvckNvZGUsIEVycm9yQ29kZVN0YXR1cyDigJQgc3RhbmRhcmRpemVkIGVycm9yIGNvZGUgZW51bSBhbmQgc3RhdHVzIG1hcHBpbmdcbiAqIC0gUmVxdWVzdElkZW50aXR5IOKAlCBwYXJzZWQgSldUIGlkZW50aXR5XG4gKiAtIFNlcnZpY2VDb25maWcsIFJlcXVlc3RPcHRpb25zIOKAlCBIVFRQIGNsaWVudCB0eXBlc1xuICogLSBRdW90YVR5cGUsIFF1b3RhQ2hlY2tSZXN1bHQg4oCUIHF1b3RhIHNlcnZpY2UgdHlwZXNcbiAqIC0gUGlwZWxpbmVUeXBlLCBDb21wdXRlVHlwZSwgVmlzaWJpbGl0eSDigJQgcGlwZWxpbmUgZG9tYWluIHR5cGVzXG4gKiAtIEZlYXR1cmVGbGFncyDigJQgZmVhdHVyZSBmbGFnIHR5cGVzXG4gKlxuICogKipVdGlsaXRpZXMqKlxuICogLSBjcmVhdGVMb2dnZXIg4oCUIFdpbnN0b24tYmFzZWQgc3RydWN0dXJlZCBsb2dnZXIgZmFjdG9yeVxuICogLSBzZW5kU3VjY2Vzcywgc2VuZEVycm9yLCBzZW5kUGFnaW5hdGVkTmVzdGVkLCBzZW5kQmFkUmVxdWVzdCwgc2VuZEludGVybmFsRXJyb3Ig4oCUIEhUVFAgcmVzcG9uc2UgaGVscGVyc1xuICogLSBnZXRQYXJhbSDigJQgcmVxdWVzdCBwYXJhbWV0ZXIgZXh0cmFjdGlvblxuICogLSBwYXJzZVF1ZXJ5SW50LCBwYXJzZVF1ZXJ5U3RyaW5nIOKAlCBxdWVyeSBzdHJpbmcgcGFyc2luZ1xuICogLSBnZXRJZGVudGl0eSDigJQgaWRlbnRpdHkgZXh0cmFjdGlvbiBmcm9tIHJlcXVlc3RzXG4gKiAtIGFjdG9ySWQg4oCUIHRoZSBhdWRpdCBhY3RvciBmb3IgYSByb3V0ZSBjb250ZXh0IChvbmUgYHN5c3RlbWAgc2VudGluZWwpXG4gKiAtIGVycm9yTWVzc2FnZSDigJQgc2FmZSBlcnJvci10by1zdHJpbmcgY29udmVyc2lvblxuICpcbiAqICoqQ29uc3RhbnRzKipcbiAqIC0gSFRUUCBzdGF0dXMgY29kZXMsIEFJIHByb3ZpZGVyIGlkZW50aWZpZXJzLCB0aW1lIGNvbnN0YW50c1xuICpcbiAqICoqU2VydmljZXMqKlxuICogLSBJbnRlcm5hbEh0dHBDbGllbnQsIGNyZWF0ZVNhZmVDbGllbnQg4oCUIGludGVybmFsIHNlcnZpY2UtdG8tc2VydmljZSBIVFRQIGNsaWVudFxuICogLSBjcmVhdGVRdW90YVNlcnZpY2Ug4oCUIHF1b3RhIGVuZm9yY2VtZW50IGNsaWVudCBmYWN0b3J5XG4gKiAtIENhY2hlU2VydmljZSDigJQgaW4tbWVtb3J5IFRUTCBjYWNoZVxuICogLSBDb21wbGlhbmNlQ2xpZW50IOKAlCBjb21wbGlhbmNlIHNlcnZpY2UgY2xpZW50XG4gKiAtIEVudGl0eUV2ZW50RW1pdHRlciDigJQgZG9tYWluIGV2ZW50IHB1Yi9zdWJcbiAqIC0gcmV0cnlGb3JldmVyIOKAlCBjYXBwZWQtYmFja29mZiB3YWl0IGZvciBhIGRlcGVuZGVuY3kgdGhhdCBtdXN0IGNvbWUgdXBcbiAqXG4gKiAqKkVycm9ycyoqXG4gKiAtIEFwcEVycm9yLCBOb3RGb3VuZEVycm9yLCBGb3JiaWRkZW5FcnJvciDigJQgdHlwZWQgSFRUUCBlcnJvciBjbGFzc2VzXG4gKlxuICogKipWYWxpZGF0aW9uKipcbiAqIC0gWm9kLWJhc2VkIHJlcXVlc3QgdmFsaWRhdGlvbiBzY2hlbWFzIGFuZCBtaWRkbGV3YXJlXG4gKlxuICogKipSb3V0ZXMqKlxuICogLSBIZWFsdGggY2hlY2sgcm91dGUgZmFjdG9yeVxuICpcbiAqICoqT3BlbkFQSSoqXG4gKiAtIFNjaGVtYSByZWdpc3RyeSBhbmQgc3BlYyBnZW5lcmF0aW9uXG4gKi9cblxuLyoqXG4gKiBQVUJMSUMgU1VSRkFDRSBQT0xJQ1lcbiAqXG4gKiBUaGUgcm9vdCByZS1leHBvcnRzIHRoZSB0ZW4gc3ViLWJhcnJlbHMsIGFuZCBlYWNoIFNVQi1CQVJSRUwgaXMgdGhlIGFsbG93LWxpc3RcbiAqIGZvciBpdHMgb3duIGFyZWE6IGV2ZXJ5IHJlLWV4cG9ydCBOQU1FUyB3aGF0IGl0IGV4cG9ydHMgKG5vIGBleHBvcnQgKmApLCBhbmRcbiAqIGEgbmFtZSBpcyBvbiB0aGUgbGlzdCBvbmx5IGJlY2F1c2Ugc29tZSBvdGhlciBwYWNrYWdlIOKAlCBhIHNlcnZpY2UsIHRoZVxuICogZnJvbnRlbmQsIGEgdGVzdCwgYSBkZXBsb3kgc2NyaXB0IOKAlCBhY3R1YWxseSBpbXBvcnRzIGl0LiBJbnRlcm5hbHMgYXJlXG4gKiBleGNsdWRlZCB0aGVyZSByYXRoZXIgdGhhbiBoZXJlLCBzbyB0aGUgZXhjbHVzaW9uIHNpdHMgbmV4dCB0byB0aGUgY29kZSB0aGF0XG4gKiBkZWZpbmVzIHRoZW0uIEFkZGluZyBhbiBleHBvcnQgdG8gYSBtb2R1bGUgZG9lcyBOT1QgcHVibGlzaCBpdDsgYWRkIGl0IHRvXG4gKiB0aGUgc3ViLWJhcnJlbCB3aGVuIGEgY29uc3VtZXIgbmVlZHMgaXQuXG4gKlxuICogRGVsaWJlcmF0ZWx5IGludGVybmFsLCBmb3IgZXhhbXBsZTpcbiAqIC0gYHNlcnZpY2VzL3JldHJ5LXN0cmF0ZWd5YCDigJQgZGVjaXNpb24gZnVuY3Rpb25zIG9ubHk7IGBwYXJzZVJldHJ5QWZ0ZXJgLFxuICogICBgYWRkSml0dGVyYCBhbmQgdGhlIERFRkFVTFRfKiBjb25zdGFudHMgYXJlIGJhY2tvZmYgaW50ZXJuYWxzLlxuICogLSBgc2VydmljZXMvY2lyY3VpdC1icmVha2VyYCDigJQgYHJlc2V0Q2lyY3VpdEJyZWFrZXJzYCBvbmx5OyB0aGVcbiAqICAgYENpcmN1aXRCcmVha2VyYCBjbGFzcyBpcyB3aXJlZCBieSB0aGUgc2hhcmVkIEhUVFAgY2xpZW50LCBub3QgYnkgc2VydmljZXMuXG4gKiAtIGBzZXJ2aWNlcy9zZXJ2aWNlLWtleXNgIOKAlCB2ZXJpZnkvaW5zcGVjdCBvbmx5OyBzaWduaW5nLCB0aGUga2V5IGJ1bmRsZSBhbmRcbiAqICAgYF9yZXNldFNlcnZpY2VLZXlzRm9yVGVzdHNgIHN0YXkgaW5zaWRlLlxuICogLSBgdXRpbHMvandrYCDigJQgY29uc3RhbnRzLCBzaGFwZXMsIGRlY29kZXJzIGFuZCB0aGUgdHdvIHNpZ25lci1mYWNpbmcgaGVscGVyc1xuICogICBwbGF0Zm9ybSBuZWVkczsgYGp3a1RodW1icHJpbnRgIC8gYHB1YmxpY0tleUZyb21Kd2tgIHN0YXkgaW5zaWRlLlxuICpcbiAqIGFwaS1jb3JlJ3Mgb3duIG1vZHVsZXMgcmVhY2ggdGhlIGV4Y2x1ZGVkIHN5bWJvbHMgYnkgcmVsYXRpdmUgaW1wb3J0OyB1bml0XG4gKiB0ZXN0cyByZWFjaCB0aGVtIGJ5IERFRVAgaW1wb3J0XG4gKiAoYEBwaXBlbGluZS1idWlsZGVyL2FwaS1jb3JlL2xpYi9zZXJ2aWNlcy9zZXJ2aWNlLWtleXMuanNgKS4gVGVzdCBoZWxwZXJzIGFyZVxuICogdGhlIHNlcGFyYXRlIGBAcGlwZWxpbmUtYnVpbGRlci9hcGktY29yZS90ZXN0aW5nYCBlbnRyeSwgbmV2ZXIgdGhpcyBiYXJyZWwuXG4gKi9cblxuLy8gVHlwZXNcbmV4cG9ydCAqIGZyb20gJy4vdHlwZXMvaW5kZXguanMnO1xuXG4vLyBDb25zdGFudHNcbmV4cG9ydCAqIGZyb20gJy4vY29uc3RhbnRzL2luZGV4LmpzJztcblxuLy8gVXRpbHNcbmV4cG9ydCAqIGZyb20gJy4vdXRpbHMvaW5kZXguanMnO1xuXG4vLyBIZWxwZXJzXG5leHBvcnQgKiBmcm9tICcuL2hlbHBlcnMvaW5kZXguanMnO1xuXG4vLyBTZXJ2aWNlc1xuZXhwb3J0ICogZnJvbSAnLi9zZXJ2aWNlcy9pbmRleC5qcyc7XG5cbi8vIE1pZGRsZXdhcmVcbmV4cG9ydCAqIGZyb20gJy4vbWlkZGxld2FyZS9pbmRleC5qcyc7XG5cbi8vIFJvdXRlc1xuZXhwb3J0ICogZnJvbSAnLi9yb3V0ZXMvaW5kZXguanMnO1xuXG4vLyBFcnJvcnNcbmV4cG9ydCAqIGZyb20gJy4vZXJyb3JzL2luZGV4LmpzJztcblxuLy8gVmFsaWRhdGlvblxuZXhwb3J0ICogZnJvbSAnLi92YWxpZGF0aW9uL2luZGV4LmpzJztcblxuLy8gT3BlbkFQSVxuZXhwb3J0ICogZnJvbSAnLi9vcGVuYXBpL2luZGV4LmpzJztcbiJdfQ==
@@ -0,0 +1,116 @@
1
+ /**
2
+ * Session-assurance gates: minimum AAL / max auth age for a person's session,
3
+ * and the org-admin MFA policy.
4
+ */
5
+ import type { Request, Response, NextFunction } from 'express';
6
+ import { type OrgAdminAssuranceMachines } from './route-table.js';
7
+ import { type AssuranceLevel, type JwtPayload } from '../types/common.js';
8
+ import { ErrorCode } from '../types/error-codes.js';
9
+ /**
10
+ * Whether a set of verified claims speaks for a PERSON's own session — the only
11
+ * kind of credential an assurance level can be asserted about.
12
+ *
13
+ * Excludes all three machine shapes: an internal service principal, an org
14
+ * service account, and any exchanged access key (`token_use: 'api_key'`, i.e. a
15
+ * PAT or a service-account key), each of which authenticates a program holding a
16
+ * secret rather than a person presenting factors.
17
+ */
18
+ export declare function isHumanPrincipal(claims: Pick<JwtPayload, 'principalType' | 'token_use'> | undefined): boolean;
19
+ /** What an assurance gate demands. */
20
+ export interface AssuranceOptions {
21
+ minAssurance: AssuranceLevel;
22
+ maxAge?: number;
23
+ /**
24
+ * A NAMED carve-out for requests that cannot possibly satisfy the level and
25
+ * must still be served — today only one exists: the bootstrap-administrator
26
+ * window, where a fresh install's single admin has no factor yet and the setup
27
+ * calls that create the install's automation credential would otherwise be
28
+ * unreachable (see platform's `isBootstrapSetupRequest`).
29
+ *
30
+ * It is deliberately shaped as `{ reason, when }` rather than a bare predicate:
31
+ * the reason is counted (`assurance_exempted_total{reason}`) and published on
32
+ * the route table as `assuranceExempt`, so an exemption cannot be added without
33
+ * showing up in the generated table a reviewer reads. The gate keeps its
34
+ * `minAssurance` tag either way — the route still requires MFA-grade for
35
+ * everyone the carve-out does not name.
36
+ */
37
+ exempt?: {
38
+ reason: string;
39
+ when: (req: Request) => boolean;
40
+ };
41
+ }
42
+ /**
43
+ * Standalone assurance gate, for a service whose own `requireAuth` is not
44
+ * api-core's (platform's is its own, because it reads Mongo). Compose it AFTER
45
+ * authentication: `router.get('/x', requireAuth, requireAssurance({ minAssurance: 2 }), h)`.
46
+ *
47
+ * Identical rules and identical refusals to `requireAuth({ minAssurance })` —
48
+ * that option simply calls the same check inline, so there is one implementation
49
+ * of "is this session MFA-grade" in the codebase.
50
+ */
51
+ export declare function requireAssurance(options: AssuranceOptions): (req: Request, res: Response, next: NextFunction) => void;
52
+ /**
53
+ * Handler-level twin of {@link requireAssurance}, for a route where only SOME
54
+ * requests must be MFA-grade — e.g. a policy route on which TIGHTENING a setting
55
+ * stays open to a single-factor session (so an admin without MFA can adopt it)
56
+ * but LOOSENING it does not. Same rules and the same three refusals. Returns
57
+ * true when the request was REFUSED (a response has been sent).
58
+ */
59
+ export declare function refuseWeakSession(req: Request, res: Response, options: AssuranceOptions): boolean;
60
+ /** What {@link requireOrgAdminAssurance} demands of a route. */
61
+ export interface OrgAdminAssuranceOptions {
62
+ /** What a machine credential (PAT, service account, machine session) meets on
63
+ * this route while the policy is on — see {@link OrgAdminAssuranceMachines}.
64
+ * Required, so every route states its decision rather than inheriting one. */
65
+ machines: OrgAdminAssuranceMachines;
66
+ }
67
+ /** `details.reason` on a refusal by {@link requireOrgAdminAssurance}, so the UI
68
+ * can say it is the ORG's policy asking, not the route's own requirement. */
69
+ export declare const ORG_ADMIN_MFA_REASON = "org_admin_policy";
70
+ /**
71
+ * The refusal {@link requireOrgAdminAssurance} would send for these claims, or
72
+ * `undefined` when the request may proceed. Exported so a handler whose policy
73
+ * applies to only PART of a route (e.g. `generate-token`, where opening a new
74
+ * machine credential is gated but renewing an existing one is not) applies the
75
+ * exact same rule rather than a copy of it.
76
+ */
77
+ export declare function orgAdminAssuranceRefusal(claims: JwtPayload | undefined, options: OrgAdminAssuranceOptions): {
78
+ status: number;
79
+ code: ErrorCode;
80
+ message: string;
81
+ reason: 'machine_principal' | 'weak_session';
82
+ } | undefined;
83
+ /**
84
+ * Apply {@link orgAdminAssuranceRefusal} and send its refusal. Returns true when
85
+ * the request was REFUSED (a response has been sent). For handler-level use;
86
+ * routes use {@link requireOrgAdminAssurance}.
87
+ */
88
+ export declare function refuseForOrgAdminAssurance(req: Request, res: Response, options: OrgAdminAssuranceOptions): boolean;
89
+ /**
90
+ * The org policy "administrative actions require MFA" (`adminActionsRequireMfa`).
91
+ *
92
+ * Unlike {@link requireAssurance}, the requirement is the ORG's choice, not the
93
+ * route's: the gate reads the `org_admin_aal` claim, set at token issue from the
94
+ * active org's policy (strictest across its ancestors), so a service that cannot
95
+ * read the org document still enforces it. With the policy off the gate is a
96
+ * no-op; with it on, a single-factor session is refused 401 `MFA_REQUIRED`
97
+ * (with `details.reason: 'org_admin_policy'`) — the same code the client already
98
+ * turns into "enrol a factor / sign in with it", never a sign-out.
99
+ *
100
+ * `machines` is decided per route (see {@link OrgAdminAssuranceMachines}): the
101
+ * policy is about how strongly a PERSON's session was opened, so automation that
102
+ * legitimately drives a route keeps working (`allow`), while a route that mints
103
+ * a further credential refuses machines outright (`refuse`, 403
104
+ * `HUMAN_SESSION_REQUIRED` — the same refusal `requireAssurance` gives them).
105
+ */
106
+ export declare function requireOrgAdminAssurance(options: OrgAdminAssuranceOptions): (req: Request, res: Response, next: NextFunction) => void;
107
+ /**
108
+ * Enforce `minAssurance` / `maxAge` on a verified token. Returns true when the
109
+ * request was REFUSED (a response has been sent).
110
+ *
111
+ * Three distinct refusals, because the client's next move differs for each:
112
+ * a machine must stop (403), a weak session must gain a factor (401
113
+ * `MFA_REQUIRED` → enrolment, never a sign-out), and a stale one must simply
114
+ * authenticate again (401 `REAUTH_REQUIRED`).
115
+ */
116
+ export declare function refuseForAssurance(options: AssuranceOptions, decoded: JwtPayload, req: Request, res: Response): boolean;
@@ -0,0 +1,176 @@
1
+ // Copyright 2026 Pipeline Builder Contributors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ import { tagRouteGate } from './route-table.js';
4
+ import { HttpStatus } from '../constants/http-status.js';
5
+ import { serviceIdentity } from '../services/service-keys.js';
6
+ import {} from '../types/common.js';
7
+ import { ErrorCode } from '../types/error-codes.js';
8
+ import { emitCounter } from '../utils/metric-emitter.js';
9
+ import { sendError } from '../utils/response.js';
10
+ import { recordAuthzDenial } from './permission-gates.js';
11
+ /**
12
+ * Whether a set of verified claims speaks for a PERSON's own session — the only
13
+ * kind of credential an assurance level can be asserted about.
14
+ *
15
+ * Excludes all three machine shapes: an internal service principal, an org
16
+ * service account, and any exchanged access key (`token_use: 'api_key'`, i.e. a
17
+ * PAT or a service-account key), each of which authenticates a program holding a
18
+ * secret rather than a person presenting factors.
19
+ */
20
+ export function isHumanPrincipal(claims) {
21
+ return claims?.principalType === 'user' && claims.token_use === 'access';
22
+ }
23
+ /**
24
+ * Standalone assurance gate, for a service whose own `requireAuth` is not
25
+ * api-core's (platform's is its own, because it reads Mongo). Compose it AFTER
26
+ * authentication: `router.get('/x', requireAuth, requireAssurance({ minAssurance: 2 }), h)`.
27
+ *
28
+ * Identical rules and identical refusals to `requireAuth({ minAssurance })` —
29
+ * that option simply calls the same check inline, so there is one implementation
30
+ * of "is this session MFA-grade" in the codebase.
31
+ */
32
+ export function requireAssurance(options) {
33
+ return tagRouteGate((req, res, next) => {
34
+ if (!req.user) {
35
+ return sendError(res, HttpStatus.UNAUTHORIZED, 'Authentication required', ErrorCode.UNAUTHORIZED);
36
+ }
37
+ // A named exemption is checked AFTER authentication and before the level, so
38
+ // it can never admit an unauthenticated caller, and it is counted every time
39
+ // it fires — an exemption nobody can see is how a gate quietly stops being one.
40
+ if (options.exempt?.when(req) === true) {
41
+ emitCounter('assurance_exempted_total', { service: serviceIdentity(), reason: options.exempt.reason });
42
+ return next();
43
+ }
44
+ if (refuseForAssurance(options, req.user, req, res))
45
+ return;
46
+ next();
47
+ }, {
48
+ kind: 'assurance',
49
+ minAssurance: options.minAssurance,
50
+ ...(options.maxAge !== undefined ? { maxAge: options.maxAge } : {}),
51
+ ...(options.exempt ? { exempt: options.exempt.reason } : {}),
52
+ });
53
+ }
54
+ /**
55
+ * Handler-level twin of {@link requireAssurance}, for a route where only SOME
56
+ * requests must be MFA-grade — e.g. a policy route on which TIGHTENING a setting
57
+ * stays open to a single-factor session (so an admin without MFA can adopt it)
58
+ * but LOOSENING it does not. Same rules and the same three refusals. Returns
59
+ * true when the request was REFUSED (a response has been sent).
60
+ */
61
+ export function refuseWeakSession(req, res, options) {
62
+ if (!req.user) {
63
+ sendError(res, HttpStatus.UNAUTHORIZED, 'Authentication required', ErrorCode.UNAUTHORIZED);
64
+ return true;
65
+ }
66
+ return refuseForAssurance(options, req.user, req, res);
67
+ }
68
+ /** `details.reason` on a refusal by {@link requireOrgAdminAssurance}, so the UI
69
+ * can say it is the ORG's policy asking, not the route's own requirement. */
70
+ export const ORG_ADMIN_MFA_REASON = 'org_admin_policy';
71
+ /**
72
+ * The refusal {@link requireOrgAdminAssurance} would send for these claims, or
73
+ * `undefined` when the request may proceed. Exported so a handler whose policy
74
+ * applies to only PART of a route (e.g. `generate-token`, where opening a new
75
+ * machine credential is gated but renewing an existing one is not) applies the
76
+ * exact same rule rather than a copy of it.
77
+ */
78
+ export function orgAdminAssuranceRefusal(claims, options) {
79
+ // The claim is only ever `2`, and only when the policy is on (see JwtPayload).
80
+ if (!claims || claims.org_admin_aal !== 2)
81
+ return undefined;
82
+ if (!isHumanPrincipal(claims)) {
83
+ if (options.machines === 'allow')
84
+ return undefined;
85
+ return {
86
+ status: HttpStatus.FORBIDDEN,
87
+ code: ErrorCode.HUMAN_SESSION_REQUIRED,
88
+ reason: 'machine_principal',
89
+ message: 'Your organization requires two-factor authentication for this action — API keys and service accounts cannot perform it',
90
+ };
91
+ }
92
+ if ((claims.aal ?? 1) >= 2)
93
+ return undefined;
94
+ return {
95
+ status: HttpStatus.UNAUTHORIZED,
96
+ code: ErrorCode.MFA_REQUIRED,
97
+ reason: 'weak_session',
98
+ message: 'Your organization requires two-factor authentication for administrative actions — sign in again with a passkey or an authenticator code',
99
+ };
100
+ }
101
+ /**
102
+ * Apply {@link orgAdminAssuranceRefusal} and send its refusal. Returns true when
103
+ * the request was REFUSED (a response has been sent). For handler-level use;
104
+ * routes use {@link requireOrgAdminAssurance}.
105
+ */
106
+ export function refuseForOrgAdminAssurance(req, res, options) {
107
+ const refusal = orgAdminAssuranceRefusal(req.user, options);
108
+ if (!refusal)
109
+ return false;
110
+ emitCounter('mfa_enforcement_refused_total', { service: serviceIdentity(), reason: `org_admin_${refusal.reason}` });
111
+ recordAuthzDenial(req, 'assurance:org-admin');
112
+ sendError(res, refusal.status, refusal.message, refusal.code, { reason: ORG_ADMIN_MFA_REASON });
113
+ return true;
114
+ }
115
+ /**
116
+ * The org policy "administrative actions require MFA" (`adminActionsRequireMfa`).
117
+ *
118
+ * Unlike {@link requireAssurance}, the requirement is the ORG's choice, not the
119
+ * route's: the gate reads the `org_admin_aal` claim, set at token issue from the
120
+ * active org's policy (strictest across its ancestors), so a service that cannot
121
+ * read the org document still enforces it. With the policy off the gate is a
122
+ * no-op; with it on, a single-factor session is refused 401 `MFA_REQUIRED`
123
+ * (with `details.reason: 'org_admin_policy'`) — the same code the client already
124
+ * turns into "enrol a factor / sign in with it", never a sign-out.
125
+ *
126
+ * `machines` is decided per route (see {@link OrgAdminAssuranceMachines}): the
127
+ * policy is about how strongly a PERSON's session was opened, so automation that
128
+ * legitimately drives a route keeps working (`allow`), while a route that mints
129
+ * a further credential refuses machines outright (`refuse`, 403
130
+ * `HUMAN_SESSION_REQUIRED` — the same refusal `requireAssurance` gives them).
131
+ */
132
+ export function requireOrgAdminAssurance(options) {
133
+ return tagRouteGate((req, res, next) => {
134
+ if (!req.user) {
135
+ return sendError(res, HttpStatus.UNAUTHORIZED, 'Authentication required', ErrorCode.UNAUTHORIZED);
136
+ }
137
+ if (refuseForOrgAdminAssurance(req, res, options))
138
+ return;
139
+ next();
140
+ }, { kind: 'orgAdminAssurance', machines: options.machines });
141
+ }
142
+ /**
143
+ * Enforce `minAssurance` / `maxAge` on a verified token. Returns true when the
144
+ * request was REFUSED (a response has been sent).
145
+ *
146
+ * Three distinct refusals, because the client's next move differs for each:
147
+ * a machine must stop (403), a weak session must gain a factor (401
148
+ * `MFA_REQUIRED` → enrolment, never a sign-out), and a stale one must simply
149
+ * authenticate again (401 `REAUTH_REQUIRED`).
150
+ */
151
+ export function refuseForAssurance(options, decoded, req, res) {
152
+ const minAssurance = options.minAssurance;
153
+ const refuse = (reason, status, message, code) => {
154
+ emitCounter('mfa_enforcement_refused_total', { service: serviceIdentity(), reason });
155
+ recordAuthzDenial(req, `assurance:${minAssurance}`);
156
+ sendError(res, status, message, code);
157
+ return true;
158
+ };
159
+ if (!isHumanPrincipal(decoded)) {
160
+ return refuse('machine_principal', HttpStatus.FORBIDDEN, 'This action requires a person signed in with two-factor authentication — API keys and service accounts cannot perform it', ErrorCode.HUMAN_SESSION_REQUIRED);
161
+ }
162
+ if ((decoded.aal ?? 1) < minAssurance) {
163
+ return refuse('weak_session', HttpStatus.UNAUTHORIZED, 'This action requires two-factor authentication — sign in again with a passkey or an authenticator code', ErrorCode.MFA_REQUIRED);
164
+ }
165
+ if (options.maxAge !== undefined) {
166
+ const authTime = decoded.auth_time;
167
+ // No `auth_time` is not "recent enough by default" — `hasValidIdentityClaims`
168
+ // already requires it on a user token, so an absent one means a token this
169
+ // gate cannot reason about. Fail closed.
170
+ if (typeof authTime !== 'number' || Math.floor(Date.now() / 1000) - authTime > options.maxAge) {
171
+ return refuse('stale_session', HttpStatus.UNAUTHORIZED, 'This action requires a recent sign-in — please authenticate again', ErrorCode.REAUTH_REQUIRED);
172
+ }
173
+ }
174
+ return false;
175
+ }
176
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXNzdXJhbmNlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL21pZGRsZXdhcmUvYXNzdXJhbmNlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLCtDQUErQztBQUMvQyxzQ0FBc0M7QUFRdEMsT0FBTyxFQUFFLFlBQVksRUFBa0MsTUFBTSxrQkFBa0IsQ0FBQztBQUNoRixPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sNkJBQTZCLENBQUM7QUFDekQsT0FBTyxFQUFFLGVBQWUsRUFBRSxNQUFNLDZCQUE2QixDQUFDO0FBQzlELE9BQU8sRUFBd0MsTUFBTSxvQkFBb0IsQ0FBQztBQUMxRSxPQUFPLEVBQUUsU0FBUyxFQUFFLE1BQU0seUJBQXlCLENBQUM7QUFDcEQsT0FBTyxFQUFFLFdBQVcsRUFBRSxNQUFNLDRCQUE0QixDQUFDO0FBQ3pELE9BQU8sRUFBRSxTQUFTLEVBQUUsTUFBTSxzQkFBc0IsQ0FBQztBQUNqRCxPQUFPLEVBQUUsaUJBQWlCLEVBQUUsTUFBTSx1QkFBdUIsQ0FBQztBQUMxRDs7Ozs7Ozs7R0FRRztBQUNILE1BQU0sVUFBVSxnQkFBZ0IsQ0FBQyxNQUFtRTtJQUNsRyxPQUFPLE1BQU0sRUFBRSxhQUFhLEtBQUssTUFBTSxJQUFJLE1BQU0sQ0FBQyxTQUFTLEtBQUssUUFBUSxDQUFDO0FBQzNFLENBQUM7QUF1QkQ7Ozs7Ozs7O0dBUUc7QUFDSCxNQUFNLFVBQVUsZ0JBQWdCLENBQUMsT0FBeUI7SUFDeEQsT0FBTyxZQUFZLENBQUMsQ0FBQyxHQUFZLEVBQUUsR0FBYSxFQUFFLElBQWtCLEVBQVEsRUFBRTtRQUM1RSxJQUFJLENBQUMsR0FBRyxDQUFDLElBQUksRUFBRSxDQUFDO1lBQ2QsT0FBTyxTQUFTLENBQUMsR0FBRyxFQUFFLFVBQVUsQ0FBQyxZQUFZLEVBQUUseUJBQXlCLEVBQUUsU0FBUyxDQUFDLFlBQVksQ0FBQyxDQUFDO1FBQ3BHLENBQUM7UUFDRCw2RUFBNkU7UUFDN0UsNkVBQTZFO1FBQzdF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Q25FLFNBQVMsQ0FBQyxlQUFlLENBQzFCLENBQUM7UUFDSixDQUFDO0lBQ0gsQ0FBQztJQUNELE9BQU8sS0FBSyxDQUFDO0FBQ2YsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8vIENvcHlyaWdodCAyMDI2IFBpcGVsaW5lIEJ1aWxkZXIgQ29udHJpYnV0b3JzXG4vLyBTUERYLUxpY2Vuc2UtSWRlbnRpZmllcjogQXBhY2hlLTIuMFxuXG4vKipcbiAqIFNlc3Npb24tYXNzdXJhbmNlIGdhdGVzOiBtaW5pbXVtIEFBTCAvIG1heCBhdXRoIGFnZSBmb3IgYSBwZXJzb24ncyBzZXNzaW9uLFxuICogYW5kIHRoZSBvcmctYWRtaW4gTUZBIHBvbGljeS5cbiAqL1xuXG5pbXBvcnQgdHlwZSB7IFJlcXVlc3QsIFJlc3BvbnNlLCBOZXh0RnVuY3Rpb24gfSBmcm9tICdleHByZXNzJztcbmltcG9ydCB7IHRhZ1JvdXRlR2F0ZSwgdHlwZSBPcmdBZG1pbkFzc3VyYW5jZU1hY2hpbmVzIH0gZnJvbSAnLi9yb3V0ZS10YWJsZS5qcyc7XG5pbXBvcnQgeyBIdHRwU3RhdHVzIH0gZnJvbSAnLi4vY29uc3RhbnRzL2h0dHAtc3RhdHVzLmpzJztcbmltcG9ydCB7IHNlcnZpY2VJZGVudGl0eSB9IGZyb20gJy4uL3NlcnZpY2VzL3NlcnZpY2Uta2V5cy5qcyc7XG5pbXBvcnQgeyB0eXBlIEFzc3VyYW5jZUxldmVsLCB0eXBlIEp3dFBheWxvYWQgfSBmcm9tICcuLi90eXBlcy9jb21tb24uanMnO1xuaW1wb3J0IHsgRXJyb3JDb2RlIH0gZnJvbSAnLi4vdHlwZXMvZXJyb3ItY29kZXMuanMnO1xuaW1wb3J0IHsgZW1pdENvdW50ZXIgfSBmcm9tICcuLi91dGlscy9tZXRyaWMtZW1pdHRlci5qcyc7XG5pbXBvcnQgeyBzZW5kRXJyb3IgfSBmcm9tICcuLi91dGlscy9yZXNwb25zZS5qcyc7XG5pbXBvcnQgeyByZWNvcmRBdXRoekRlbmlhbCB9IGZyb20gJy4vcGVybWlzc2lvbi1nYXRlcy5qcyc7XG4vKipcbiAqIFdoZXRoZXIgYSBzZXQgb2YgdmVyaWZpZWQgY2xhaW1zIHNwZWFrcyBmb3IgYSBQRVJTT04ncyBvd24gc2Vzc2lvbiDigJQgdGhlIG9ubHlcbiAqIGtpbmQgb2YgY3JlZGVudGlhbCBhbiBhc3N1cmFuY2UgbGV2ZWwgY2FuIGJlIGFzc2VydGVkIGFib3V0LlxuICpcbiAqIEV4Y2x1ZGVzIGFsbCB0aHJlZSBtYWNoaW5lIHNoYXBlczogYW4gaW50ZXJuYWwgc2VydmljZSBwcmluY2lwYWwsIGFuIG9yZ1xuICogc2VydmljZSBhY2NvdW50LCBhbmQgYW55IGV4Y2hhbmdlZCBhY2Nlc3Mga2V5IChgdG9rZW5fdXNlOiAnYXBpX2tleSdgLCBpLmUuIGFcbiAqIFBBVCBvciBhIHNlcnZpY2UtYWNjb3VudCBrZXkpLCBlYWNoIG9mIHdoaWNoIGF1dGhlbnRpY2F0ZXMgYSBwcm9ncmFtIGhvbGRpbmcgYVxuICogc2VjcmV0IHJhdGhlciB0aGFuIGEgcGVyc29uIHByZXNlbnRpbmcgZmFjdG9ycy5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGlzSHVtYW5QcmluY2lwYWwoY2xhaW1zOiBQaWNrPEp3dFBheWxvYWQsICdwcmluY2lwYWxUeXBlJyB8ICd0b2tlbl91c2UnPiB8IHVuZGVmaW5lZCk6IGJvb2xlYW4ge1xuICByZXR1cm4gY2xhaW1zPy5wcmluY2lwYWxUeXBlID09PSAndXNlcicgJiYgY2xhaW1zLnRva2VuX3VzZSA9PT0gJ2FjY2Vzcyc7XG59XG5cbi8qKiBXaGF0IGFuIGFzc3VyYW5jZSBnYXRlIGRlbWFuZHMuICovXG5leHBvcnQgaW50ZXJmYWNlIEFzc3VyYW5jZU9wdGlvbnMge1xuICBtaW5Bc3N1cmFuY2U6IEFzc3VyYW5jZUxldmVsO1xuICBtYXhBZ2U/OiBudW1iZXI7XG4gIC8qKlxuICAgKiBBIE5BTUVEIGNhcnZlLW91dCBmb3IgcmVxdWVzdHMgdGhhdCBjYW5ub3QgcG9zc2libHkgc2F0aXNmeSB0aGUgbGV2ZWwgYW5kXG4gICAqIG11c3Qgc3RpbGwgYmUgc2VydmVkIOKAlCB0b2RheSBvbmx5IG9uZSBleGlzdHM6IHRoZSBib290c3RyYXAtYWRtaW5pc3RyYXRvclxuICAgKiB3aW5kb3csIHdoZXJlIGEgZnJlc2ggaW5zdGFsbCdzIHNpbmdsZSBhZG1pbiBoYXMgbm8gZmFjdG9yIHlldCBhbmQgdGhlIHNldHVwXG4gICAqIGNhbGxzIHRoYXQgY3JlYXRlIHRoZSBpbnN0YWxsJ3MgYXV0b21hdGlvbiBjcmVkZW50aWFsIHdvdWxkIG90aGVyd2lzZSBiZVxuICAgKiB1bnJlYWNoYWJsZSAoc2VlIHBsYXRmb3JtJ3MgYGlzQm9vdHN0cmFwU2V0dXBSZXF1ZXN0YCkuXG4gICAqXG4gICAqIEl0IGlzIGRlbGliZXJhdGVseSBzaGFwZWQgYXMgYHsgcmVhc29uLCB3aGVuIH1gIHJhdGhlciB0aGFuIGEgYmFyZSBwcmVkaWNhdGU6XG4gICAqIHRoZSByZWFzb24gaXMgY291bnRlZCAoYGFzc3VyYW5jZV9leGVtcHRlZF90b3RhbHtyZWFzb259YCkgYW5kIHB1Ymxpc2hlZCBvblxuICAgKiB0aGUgcm91dGUgdGFibGUgYXMgYGFzc3VyYW5jZUV4ZW1wdGAsIHNvIGFuIGV4ZW1wdGlvbiBjYW5ub3QgYmUgYWRkZWQgd2l0aG91dFxuICAgKiBzaG93aW5nIHVwIGluIHRoZSBnZW5lcmF0ZWQgdGFibGUgYSByZXZpZXdlciByZWFkcy4gVGhlIGdhdGUga2VlcHMgaXRzXG4gICAqIGBtaW5Bc3N1cmFuY2VgIHRhZyBlaXRoZXIgd2F5IOKAlCB0aGUgcm91dGUgc3RpbGwgcmVxdWlyZXMgTUZBLWdyYWRlIGZvclxuICAgKiBldmVyeW9uZSB0aGUgY2FydmUtb3V0IGRvZXMgbm90IG5hbWUuXG4gICAqL1xuICBleGVtcHQ/OiB7IHJlYXNvbjogc3RyaW5nOyB3aGVuOiAocmVxOiBSZXF1ZXN0KSA9PiBib29sZWFuIH07XG59XG5cbi8qKlxuICogU3RhbmRhbG9uZSBhc3N1cmFuY2UgZ2F0ZSwgZm9yIGEgc2VydmljZSB3aG9zZSBvd24gYHJlcXVpcmVBdXRoYCBpcyBub3RcbiAqIGFwaS1jb3JlJ3MgKHBsYXRmb3JtJ3MgaXMgaXRzIG93biwgYmVjYXVzZSBpdCByZWFkcyBNb25nbykuIENvbXBvc2UgaXQgQUZURVJcbiAqIGF1dGhlbnRpY2F0aW9uOiBgcm91dGVyLmdldCgnL3gnLCByZXF1aXJlQXV0aCwgcmVxdWlyZUFzc3VyYW5jZSh7IG1pbkFzc3VyYW5jZTogMiB9KSwgaClgLlxuICpcbiAqIElkZW50aWNhbCBydWxlcyBhbmQgaWRlbnRpY2FsIHJlZnVzYWxzIHRvIGByZXF1aXJlQXV0aCh7IG1pbkFzc3VyYW5jZSB9KWAg4oCUXG4gKiB0aGF0IG9wdGlvbiBzaW1wbHkgY2FsbHMgdGhlIHNhbWUgY2hlY2sgaW5saW5lLCBzbyB0aGVyZSBpcyBvbmUgaW1wbGVtZW50YXRpb25cbiAqIG9mIFwiaXMgdGhpcyBzZXNzaW9uIE1GQS1ncmFkZVwiIGluIHRoZSBjb2RlYmFzZS5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHJlcXVpcmVBc3N1cmFuY2Uob3B0aW9uczogQXNzdXJhbmNlT3B0aW9ucykge1xuICByZXR1cm4gdGFnUm91dGVHYXRlKChyZXE6IFJlcXVlc3QsIHJlczogUmVzcG9uc2UsIG5leHQ6IE5leHRGdW5jdGlvbik6IHZvaWQgPT4ge1xuICAgIGlmICghcmVxLnVzZXIpIHtcbiAgICAgIHJldHVybiBzZW5kRXJyb3IocmVzLCBIdHRwU3RhdHVzLlVOQVVUSE9SSVpFRCwgJ0F1dGhlbnRpY2F0aW9uIHJlcXVpcmVkJywgRXJyb3JDb2RlLlVOQVVUSE9SSVpFRCk7XG4gICAgfVxuICAgIC8vIEEgbmFtZWQgZXhlbXB0aW9uIGlzIGNoZWNrZWQgQUZURVIgYXV0aGVudGljYXRpb24gYW5kIGJlZm9yZSB0aGUgbGV2ZWwsIHNvXG4gICAgLy8gaXQgY2FuIG5ldmVyIGFkbWl0IGFuIHVuYXV0aGVudGljYXRlZCBjYWxsZXIsIGFuZCBpdCBpcyBjb3VudGVkIGV2ZXJ5IHRpbWVcbiAgICAvLyBpdCBmaXJlcyDigJQgYW4gZXhlbXB0aW9uIG5vYm9keSBjYW4gc2VlIGlzIGhvdyBhIGdhdGUgcXVpZXRseSBzdG9wcyBiZWluZyBvbmUuXG4gICAgaWYgKG9wdGlvbnMuZXhlbXB0Py53aGVuKHJlcSkgPT09IHRydWUpIHtcbiAgICAgIGVtaXRDb3VudGVyKCdhc3N1cmFuY2VfZXhlbXB0ZWRfdG90YWwnLCB7IHNlcnZpY2U6IHNlcnZpY2VJZGVudGl0eSgpLCByZWFzb246IG9wdGlvbnMuZXhlbXB0LnJlYXNvbiB9KTtcbiAgICAgIHJldHVybiBuZXh0KCk7XG4gICAgfVxuICAgIGlmIChyZWZ1c2VGb3JBc3N1cmFuY2Uob3B0aW9ucywgcmVxLnVzZXIgYXMgSnd0UGF5bG9hZCwgcmVxLCByZXMpKSByZXR1cm47XG4gICAgbmV4dCgpO1xuICB9LCB7XG4gICAga2luZDogJ2Fzc3VyYW5jZScsXG4gICAgbWluQXNzdXJhbmNlOiBvcHRpb25zLm1pbkFzc3VyYW5jZSxcbiAgICAuLi4ob3B0aW9ucy5tYXhBZ2UgIT09IHVuZGVmaW5lZCA/IHsgbWF4QWdlOiBvcHRpb25zLm1heEFnZSB9IDoge30pLFxuICAgIC4uLihvcHRpb25zLmV4ZW1wdCA/IHsgZXhlbXB0OiBvcHRpb25zLmV4ZW1wdC5yZWFzb24gfSA6IHt9KSxcbiAgfSk7XG59XG5cbi8qKlxuICogSGFuZGxlci1sZXZlbCB0d2luIG9mIHtAbGluayByZXF1aXJlQXNzdXJhbmNlfSwgZm9yIGEgcm91dGUgd2hlcmUgb25seSBTT01FXG4gKiByZXF1ZXN0cyBtdXN0IGJlIE1GQS1ncmFkZSDigJQgZS5nLiBhIHBvbGljeSByb3V0ZSBvbiB3aGljaCBUSUdIVEVOSU5HIGEgc2V0dGluZ1xuICogc3RheXMgb3BlbiB0byBhIHNpbmdsZS1mYWN0b3Igc2Vzc2lvbiAoc28gYW4gYWRtaW4gd2l0aG91dCBNRkEgY2FuIGFkb3B0IGl0KVxuICogYnV0IExPT1NFTklORyBpdCBkb2VzIG5vdC4gU2FtZSBydWxlcyBhbmQgdGhlIHNhbWUgdGhyZWUgcmVmdXNhbHMuIFJldHVybnNcbiAqIHRydWUgd2hlbiB0aGUgcmVxdWVzdCB3YXMgUkVGVVNFRCAoYSByZXNwb25zZSBoYXMgYmVlbiBzZW50KS5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHJlZnVzZVdlYWtTZXNzaW9uKHJlcTogUmVxdWVzdCwgcmVzOiBSZXNwb25zZSwgb3B0aW9uczogQXNzdXJhbmNlT3B0aW9ucyk6IGJvb2xlYW4ge1xuICBpZiAoIXJlcS51c2VyKSB7XG4gICAgc2VuZEVycm9yKHJlcywgSHR0cFN0YXR1cy5VTkFVVEhPUklaRUQsICdBdXRoZW50aWNhdGlvbiByZXF1aXJlZCcsIEVycm9yQ29kZS5VTkFVVEhPUklaRUQpO1xuICAgIHJldHVybiB0cnVlO1xuICB9XG4gIHJldHVybiByZWZ1c2VGb3JBc3N1cmFuY2Uob3B0aW9ucywgcmVxLnVzZXIgYXMgSnd0UGF5bG9hZCwgcmVxLCByZXMpO1xufVxuXG4vKiogV2hhdCB7QGxpbmsgcmVxdWlyZU9yZ0FkbWluQXNzdXJhbmNlfSBkZW1hbmRzIG9mIGEgcm91dGUuICovXG5leHBvcnQgaW50ZXJmYWNlIE9yZ0FkbWluQXNzdXJhbmNlT3B0aW9ucyB7XG4gIC8qKiBXaGF0IGEgbWFjaGluZSBjcmVkZW50aWFsIChQQVQsIHNlcnZpY2UgYWNjb3VudCwgbWFjaGluZSBzZXNzaW9uKSBtZWV0cyBvblxuICAgKiAgdGhpcyByb3V0ZSB3aGlsZSB0aGUgcG9saWN5IGlzIG9uIOKAlCBzZWUge0BsaW5rIE9yZ0FkbWluQXNzdXJhbmNlTWFjaGluZXN9LlxuICAgKiAgUmVxdWlyZWQsIHNvIGV2ZXJ5IHJvdXRlIHN0YXRlcyBpdHMgZGVjaXNpb24gcmF0aGVyIHRoYW4gaW5oZXJpdGluZyBvbmUuICovXG4gIG1hY2hpbmVzOiBPcmdBZG1pbkFzc3VyYW5jZU1hY2hpbmVzO1xufVxuXG4vKiogYGRldGFpbHMucmVhc29uYCBvbiBhIHJlZnVzYWwgYnkge0BsaW5rIHJlcXVpcmVPcmdBZG1pbkFzc3VyYW5jZX0sIHNvIHRoZSBVSVxuICogIGNhbiBzYXkgaXQgaXMgdGhlIE9SRydzIHBvbGljeSBhc2tpbmcsIG5vdCB0aGUgcm91dGUncyBvd24gcmVxdWlyZW1lbnQuICovXG5leHBvcnQgY29uc3QgT1JHX0FETUlOX01GQV9SRUFTT04gPSAnb3JnX2FkbWluX3BvbGljeSc7XG5cbi8qKlxuICogVGhlIHJlZnVzYWwge0BsaW5rIHJlcXVpcmVPcmdBZG1pbkFzc3VyYW5jZX0gd291bGQgc2VuZCBmb3IgdGhlc2UgY2xhaW1zLCBvclxuICogYHVuZGVmaW5lZGAgd2hlbiB0aGUgcmVxdWVzdCBtYXkgcHJvY2VlZC4gRXhwb3J0ZWQgc28gYSBoYW5kbGVyIHdob3NlIHBvbGljeVxuICogYXBwbGllcyB0byBvbmx5IFBBUlQgb2YgYSByb3V0ZSAoZS5nLiBgZ2VuZXJhdGUtdG9rZW5gLCB3aGVyZSBvcGVuaW5nIGEgbmV3XG4gKiBtYWNoaW5lIGNyZWRlbnRpYWwgaXMgZ2F0ZWQgYnV0IHJlbmV3aW5nIGFuIGV4aXN0aW5nIG9uZSBpcyBub3QpIGFwcGxpZXMgdGhlXG4gKiBleGFjdCBzYW1lIHJ1bGUgcmF0aGVyIHRoYW4gYSBjb3B5IG9mIGl0LlxuICovXG5leHBvcnQgZnVuY3Rpb24gb3JnQWRtaW5Bc3N1cmFuY2VSZWZ1c2FsKFxuICBjbGFpbXM6IEp3dFBheWxvYWQgfCB1bmRlZmluZWQsXG4gIG9wdGlvbnM6IE9yZ0FkbWluQXNzdXJhbmNlT3B0aW9ucyxcbik6IHsgc3RhdHVzOiBudW1iZXI7IGNvZGU6IEVycm9yQ29kZTsgbWVzc2FnZTogc3RyaW5nOyByZWFzb246ICdtYWNoaW5lX3ByaW5jaXBhbCcgfCAnd2Vha19zZXNzaW9uJyB9IHwgdW5kZWZpbmVkIHtcbiAgLy8gVGhlIGNsYWltIGlzIG9ubHkgZXZlciBgMmAsIGFuZCBvbmx5IHdoZW4gdGhlIHBvbGljeSBpcyBvbiAoc2VlIEp3dFBheWxvYWQpLlxuICBpZiAoIWNsYWltcyB8fCBjbGFpbXMub3JnX2FkbWluX2FhbCAhPT0gMikgcmV0dXJuIHVuZGVmaW5lZDtcbiAgaWYgKCFpc0h1bWFuUHJpbmNpcGFsKGNsYWltcykpIHtcbiAgICBpZiAob3B0aW9ucy5tYWNoaW5lcyA9PT0gJ2FsbG93JykgcmV0dXJuIHVuZGVmaW5lZDtcbiAgICByZXR1cm4ge1xuICAgICAgc3RhdHVzOiBIdHRwU3RhdHVzLkZPUkJJRERFTixcbiAgICAgIGNvZGU6IEVycm9yQ29kZS5IVU1BTl9TRVNTSU9OX1JFUVVJUkVELFxuICAgICAgcmVhc29uOiAnbWFjaGluZV9wcmluY2lwYWwnLFxuICAgICAgbWVzc2FnZTogJ1lvdXIgb3JnYW5pemF0aW9uIHJlcXVpcmVzIHR3by1mYWN0b3IgYXV0aGVudGljYXRpb24gZm9yIHRoaXMgYWN0aW9uIOKAlCBBUEkga2V5cyBhbmQgc2VydmljZSBhY2NvdW50cyBjYW5ub3QgcGVyZm9ybSBpdCcsXG4gICAgfTtcbiAgfVxuICBpZiAoKGNsYWltcy5hYWwgPz8gMSkgPj0gMikgcmV0dXJuIHVuZGVmaW5lZDtcbiAgcmV0dXJuIHtcbiAgICBzdGF0dXM6IEh0dHBTdGF0dXMuVU5BVVRIT1JJWkVELFxuICAgIGNvZGU6IEVycm9yQ29kZS5NRkFfUkVRVUlSRUQsXG4gICAgcmVhc29uOiAnd2Vha19zZXNzaW9uJyxcbiAgICBtZXNzYWdlOiAnWW91ciBvcmdhbml6YXRpb24gcmVxdWlyZXMgdHdvLWZhY3RvciBhdXRoZW50aWNhdGlvbiBmb3IgYWRtaW5pc3RyYXRpdmUgYWN0aW9ucyDigJQgc2lnbiBpbiBhZ2FpbiB3aXRoIGEgcGFzc2tleSBvciBhbiBhdXRoZW50aWNhdG9yIGNvZGUnLFxuICB9O1xufVxuXG4vKipcbiAqIEFwcGx5IHtAbGluayBvcmdBZG1pbkFzc3VyYW5jZVJlZnVzYWx9IGFuZCBzZW5kIGl0cyByZWZ1c2FsLiBSZXR1cm5zIHRydWUgd2hlblxuICogdGhlIHJlcXVlc3Qgd2FzIFJFRlVTRUQgKGEgcmVzcG9uc2UgaGFzIGJlZW4gc2VudCkuIEZvciBoYW5kbGVyLWxldmVsIHVzZTtcbiAqIHJvdXRlcyB1c2Uge0BsaW5rIHJlcXVpcmVPcmdBZG1pbkFzc3VyYW5jZX0uXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiByZWZ1c2VGb3JPcmdBZG1pbkFzc3VyYW5jZShyZXE6IFJlcXVlc3QsIHJlczogUmVzcG9uc2UsIG9wdGlvbnM6IE9yZ0FkbWluQXNzdXJhbmNlT3B0aW9ucyk6IGJvb2xlYW4ge1xuICBjb25zdCByZWZ1c2FsID0gb3JnQWRtaW5Bc3N1cmFuY2VSZWZ1c2FsKHJlcS51c2VyIGFzIEp3dFBheWxvYWQgfCB1bmRlZmluZWQsIG9wdGlvbnMpO1xuICBpZiAoIXJlZnVzYWwpIHJldHVybiBmYWxzZTtcbiAgZW1pdENvdW50ZXIoJ21mYV9lbmZvcmNlbWVudF9yZWZ1c2VkX3RvdGFsJywgeyBzZXJ2aWNlOiBzZXJ2aWNlSWRlbnRpdHkoKSwgcmVhc29uOiBgb3JnX2FkbWluXyR7cmVmdXNhbC5yZWFzb259YCB9KTtcbiAgcmVjb3JkQXV0aHpEZW5pYWwocmVxLCAnYXNzdXJhbmNlOm9yZy1hZG1pbicpO1xuICBzZW5kRXJyb3IocmVzLCByZWZ1c2FsLnN0YXR1cywgcmVmdXNhbC5tZXNzYWdlLCByZWZ1c2FsLmNvZGUsIHsgcmVhc29uOiBPUkdfQURNSU5fTUZBX1JFQVNPTiB9KTtcbiAgcmV0dXJuIHRydWU7XG59XG5cbi8qKlxuICogVGhlIG9yZyBwb2xpY3kgXCJhZG1pbmlzdHJhdGl2ZSBhY3Rpb25zIHJlcXVpcmUgTUZBXCIgKGBhZG1pbkFjdGlvbnNSZXF1aXJlTWZhYCkuXG4gKlxuICogVW5saWtlIHtAbGluayByZXF1aXJlQXNzdXJhbmNlfSwgdGhlIHJlcXVpcmVtZW50IGlzIHRoZSBPUkcncyBjaG9pY2UsIG5vdCB0aGVcbiAqIHJvdXRlJ3M6IHRoZSBnYXRlIHJlYWRzIHRoZSBgb3JnX2FkbWluX2FhbGAgY2xhaW0sIHNldCBhdCB0b2tlbiBpc3N1ZSBmcm9tIHRoZVxuICogYWN0aXZlIG9yZydzIHBvbGljeSAoc3RyaWN0ZXN0IGFjcm9zcyBpdHMgYW5jZXN0b3JzKSwgc28gYSBzZXJ2aWNlIHRoYXQgY2Fubm90XG4gKiByZWFkIHRoZSBvcmcgZG9jdW1lbnQgc3RpbGwgZW5mb3JjZXMgaXQuIFdpdGggdGhlIHBvbGljeSBvZmYgdGhlIGdhdGUgaXMgYVxuICogbm8tb3A7IHdpdGggaXQgb24sIGEgc2luZ2xlLWZhY3RvciBzZXNzaW9uIGlzIHJlZnVzZWQgNDAxIGBNRkFfUkVRVUlSRURgXG4gKiAod2l0aCBgZGV0YWlscy5yZWFzb246ICdvcmdfYWRtaW5fcG9saWN5J2ApIOKAlCB0aGUgc2FtZSBjb2RlIHRoZSBjbGllbnQgYWxyZWFkeVxuICogdHVybnMgaW50byBcImVucm9sIGEgZmFjdG9yIC8gc2lnbiBpbiB3aXRoIGl0XCIsIG5ldmVyIGEgc2lnbi1vdXQuXG4gKlxuICogYG1hY2hpbmVzYCBpcyBkZWNpZGVkIHBlciByb3V0ZSAoc2VlIHtAbGluayBPcmdBZG1pbkFzc3VyYW5jZU1hY2hpbmVzfSk6IHRoZVxuICogcG9saWN5IGlzIGFib3V0IGhvdyBzdHJvbmdseSBhIFBFUlNPTidzIHNlc3Npb24gd2FzIG9wZW5lZCwgc28gYXV0b21hdGlvbiB0aGF0XG4gKiBsZWdpdGltYXRlbHkgZHJpdmVzIGEgcm91dGUga2VlcHMgd29ya2luZyAoYGFsbG93YCksIHdoaWxlIGEgcm91dGUgdGhhdCBtaW50c1xuICogYSBmdXJ0aGVyIGNyZWRlbnRpYWwgcmVmdXNlcyBtYWNoaW5lcyBvdXRyaWdodCAoYHJlZnVzZWAsIDQwM1xuICogYEhVTUFOX1NFU1NJT05fUkVRVUlSRURgIOKAlCB0aGUgc2FtZSByZWZ1c2FsIGByZXF1aXJlQXNzdXJhbmNlYCBnaXZlcyB0aGVtKS5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHJlcXVpcmVPcmdBZG1pbkFzc3VyYW5jZShvcHRpb25zOiBPcmdBZG1pbkFzc3VyYW5jZU9wdGlvbnMpIHtcbiAgcmV0dXJuIHRhZ1JvdXRlR2F0ZSgocmVxOiBSZXF1ZXN0LCByZXM6IFJlc3BvbnNlLCBuZXh0OiBOZXh0RnVuY3Rpb24pOiB2b2lkID0+IHtcbiAgICBpZiAoIXJlcS51c2VyKSB7XG4gICAgICByZXR1cm4gc2VuZEVycm9yKHJlcywgSHR0cFN0YXR1cy5VTkFVVEhPUklaRUQsICdBdXRoZW50aWNhdGlvbiByZXF1aXJlZCcsIEVycm9yQ29kZS5VTkFVVEhPUklaRUQpO1xuICAgIH1cbiAgICBpZiAocmVmdXNlRm9yT3JnQWRtaW5Bc3N1cmFuY2UocmVxLCByZXMsIG9wdGlvbnMpKSByZXR1cm47XG4gICAgbmV4dCgpO1xuICB9LCB7IGtpbmQ6ICdvcmdBZG1pbkFzc3VyYW5jZScsIG1hY2hpbmVzOiBvcHRpb25zLm1hY2hpbmVzIH0pO1xufVxuXG4vKipcbiAqIEVuZm9yY2UgYG1pbkFzc3VyYW5jZWAgLyBgbWF4QWdlYCBvbiBhIHZlcmlmaWVkIHRva2VuLiBSZXR1cm5zIHRydWUgd2hlbiB0aGVcbiAqIHJlcXVlc3Qgd2FzIFJFRlVTRUQgKGEgcmVzcG9uc2UgaGFzIGJlZW4gc2VudCkuXG4gKlxuICogVGhyZWUgZGlzdGluY3QgcmVmdXNhbHMsIGJlY2F1c2UgdGhlIGNsaWVudCdzIG5leHQgbW92ZSBkaWZmZXJzIGZvciBlYWNoOlxuICogYSBtYWNoaW5lIG11c3Qgc3RvcCAoNDAzKSwgYSB3ZWFrIHNlc3Npb24gbXVzdCBnYWluIGEgZmFjdG9yICg0MDFcbiAqIGBNRkFfUkVRVUlSRURgIOKGkiBlbnJvbG1lbnQsIG5ldmVyIGEgc2lnbi1vdXQpLCBhbmQgYSBzdGFsZSBvbmUgbXVzdCBzaW1wbHlcbiAqIGF1dGhlbnRpY2F0ZSBhZ2FpbiAoNDAxIGBSRUFVVEhfUkVRVUlSRURgKS5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHJlZnVzZUZvckFzc3VyYW5jZShcbiAgb3B0aW9uczogQXNzdXJhbmNlT3B0aW9ucyxcbiAgZGVjb2RlZDogSnd0UGF5bG9hZCxcbiAgcmVxOiBSZXF1ZXN0LFxuICByZXM6IFJlc3BvbnNlLFxuKTogYm9vbGVhbiB7XG4gIGNvbnN0IG1pbkFzc3VyYW5jZSA9IG9wdGlvbnMubWluQXNzdXJhbmNlO1xuICBjb25zdCByZWZ1c2UgPSAocmVhc29uOiBzdHJpbmcsIHN0YXR1czogbnVtYmVyLCBtZXNzYWdlOiBzdHJpbmcsIGNvZGU6IEVycm9yQ29kZSk6IHRydWUgPT4ge1xuICAgIGVtaXRDb3VudGVyKCdtZmFfZW5mb3JjZW1lbnRfcmVmdXNlZF90b3RhbCcsIHsgc2VydmljZTogc2VydmljZUlkZW50aXR5KCksIHJlYXNvbiB9KTtcbiAgICByZWNvcmRBdXRoekRlbmlhbChyZXEsIGBhc3N1cmFuY2U6JHttaW5Bc3N1cmFuY2V9YCk7XG4gICAgc2VuZEVycm9yKHJlcywgc3RhdHVzLCBtZXNzYWdlLCBjb2RlKTtcbiAgICByZXR1cm4gdHJ1ZTtcbiAgfTtcblxuICBpZiAoIWlzSHVtYW5QcmluY2lwYWwoZGVjb2RlZCkpIHtcbiAgICByZXR1cm4gcmVmdXNlKFxuICAgICAgJ21hY2hpbmVfcHJpbmNpcGFsJywgSHR0cFN0YXR1cy5GT1JCSURERU4sXG4gICAgICAnVGhpcyBhY3Rpb24gcmVxdWlyZXMgYSBwZXJzb24gc2lnbmVkIGluIHdpdGggdHdvLWZhY3RvciBhdXRoZW50aWNhdGlvbiDigJQgQVBJIGtleXMgYW5kIHNlcnZpY2UgYWNjb3VudHMgY2Fubm90IHBlcmZvcm0gaXQnLFxuICAgICAgRXJyb3JDb2RlLkhVTUFOX1NFU1NJT05fUkVRVUlSRUQsXG4gICAgKTtcbiAgfVxuICBpZiAoKGRlY29kZWQuYWFsID8/IDEpIDwgbWluQXNzdXJhbmNlKSB7XG4gICAgcmV0dXJuIHJlZnVzZShcbiAgICAgICd3ZWFrX3Nlc3Npb24nLCBIdHRwU3RhdHVzLlVOQVVUSE9SSVpFRCxcbiAgICAgICdUaGlzIGFjdGlvbiByZXF1aXJlcyB0d28tZmFjdG9yIGF1dGhlbnRpY2F0aW9uIOKAlCBzaWduIGluIGFnYWluIHdpdGggYSBwYXNza2V5IG9yIGFuIGF1dGhlbnRpY2F0b3IgY29kZScsXG4gICAgICBFcnJvckNvZGUuTUZBX1JFUVVJUkVELFxuICAgICk7XG4gIH1cbiAgaWYgKG9wdGlvbnMubWF4QWdlICE9PSB1bmRlZmluZWQpIHtcbiAgICBjb25zdCBhdXRoVGltZSA9IGRlY29kZWQuYXV0aF90aW1lO1xuICAgIC8vIE5vIGBhdXRoX3RpbWVgIGlzIG5vdCBcInJlY2VudCBlbm91Z2ggYnkgZGVmYXVsdFwiIOKAlCBgaGFzVmFsaWRJZGVudGl0eUNsYWltc2BcbiAgICAvLyBhbHJlYWR5IHJlcXVpcmVzIGl0IG9uIGEgdXNlciB0b2tlbiwgc28gYW4gYWJzZW50IG9uZSBtZWFucyBhIHRva2VuIHRoaXNcbiAgICAvLyBnYXRlIGNhbm5vdCByZWFzb24gYWJvdXQuIEZhaWwgY2xvc2VkLlxuICAgIGlmICh0eXBlb2YgYXV0aFRpbWUgIT09ICdudW1iZXInIHx8IE1hdGguZmxvb3IoRGF0ZS5ub3coKSAvIDEwMDApIC0gYXV0aFRpbWUgPiBvcHRpb25zLm1heEFnZSkge1xuICAgICAgcmV0dXJuIHJlZnVzZShcbiAgICAgICAgJ3N0YWxlX3Nlc3Npb24nLCBIdHRwU3RhdHVzLlVOQVVUSE9SSVpFRCxcbiAgICAgICAgJ1RoaXMgYWN0aW9uIHJlcXVpcmVzIGEgcmVjZW50IHNpZ24taW4g4oCUIHBsZWFzZSBhdXRoZW50aWNhdGUgYWdhaW4nLFxuICAgICAgICBFcnJvckNvZGUuUkVBVVRIX1JFUVVJUkVELFxuICAgICAgKTtcbiAgICB9XG4gIH1cbiAgcmV0dXJuIGZhbHNlO1xufVxuXG4iXX0=