@pipeline-builder/api-core 4.1.13 → 4.1.14
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.
- package/README.md +3 -2
- package/lib/constants/index.d.ts +0 -1
- package/lib/constants/index.js +1 -2
- package/lib/errors/app-errors.d.ts +8 -3
- package/lib/errors/app-errors.js +10 -5
- package/lib/helpers/access-helpers.d.ts +4 -6
- package/lib/helpers/access-helpers.js +6 -8
- package/lib/helpers/index.d.ts +3 -3
- package/lib/helpers/index.js +4 -4
- package/lib/helpers/org-hierarchy-http.d.ts +11 -10
- package/lib/helpers/org-hierarchy-http.js +24 -35
- package/lib/helpers/org-hierarchy.d.ts +37 -3
- package/lib/helpers/org-hierarchy.js +45 -4
- package/lib/helpers/restore-helpers.d.ts +44 -24
- package/lib/helpers/restore-helpers.js +41 -52
- package/lib/helpers/sse-helpers.d.ts +29 -7
- package/lib/helpers/sse-helpers.js +36 -9
- package/lib/index.d.ts +16 -14
- package/lib/index.js +17 -15
- package/lib/middleware/assurance.d.ts +116 -0
- package/lib/middleware/assurance.js +176 -0
- package/lib/middleware/auth.d.ts +5 -427
- package/lib/middleware/auth.js +36 -789
- package/lib/middleware/ecosystem-guard.d.ts +47 -0
- package/lib/middleware/ecosystem-guard.js +60 -0
- package/lib/middleware/index.d.ts +11 -4
- package/lib/middleware/index.js +12 -5
- package/lib/middleware/jwt-verify.d.ts +52 -0
- package/lib/middleware/jwt-verify.js +105 -0
- package/lib/middleware/permission-gates.d.ts +133 -0
- package/lib/middleware/permission-gates.js +260 -0
- package/lib/middleware/revocation.d.ts +96 -0
- package/lib/middleware/revocation.js +162 -0
- package/lib/middleware/route-table.d.ts +11 -0
- package/lib/middleware/route-table.js +4 -1
- package/lib/middleware/service-tokens.d.ts +83 -0
- package/lib/middleware/service-tokens.js +158 -0
- package/lib/middleware/step-up.d.ts +2 -2
- package/lib/middleware/step-up.js +8 -7
- package/lib/middleware/system-org.d.ts +20 -0
- package/lib/middleware/system-org.js +25 -0
- package/lib/openapi/routes/plugin-routes.js +133 -10
- package/lib/openapi/schema-registry.js +2 -5
- package/lib/routes/index.d.ts +1 -1
- package/lib/routes/index.js +2 -2
- package/lib/services/api-key-exchange.js +7 -5
- package/lib/services/audit-spool.d.ts +34 -10
- package/lib/services/audit-spool.js +75 -34
- package/lib/services/cache-service.d.ts +7 -0
- package/lib/services/cache-service.js +1 -1
- package/lib/services/circuit-breaker.d.ts +14 -1
- package/lib/services/circuit-breaker.js +36 -10
- package/lib/services/compliance-client.d.ts +10 -2
- package/lib/services/compliance-client.js +17 -10
- package/lib/services/compliance-event-subscriber.js +6 -6
- package/lib/services/durable-event-bus.js +8 -11
- package/lib/services/ecosystem-notify-client.d.ts +32 -0
- package/lib/services/ecosystem-notify-client.js +71 -0
- package/lib/services/env-redis.d.ts +4 -1
- package/lib/services/env-redis.js +32 -3
- package/lib/services/http-client.d.ts +19 -0
- package/lib/services/http-client.js +98 -19
- package/lib/services/index.d.ts +25 -24
- package/lib/services/index.js +25 -22
- package/lib/services/jwks-cache.js +5 -4
- package/lib/services/leader-lock.d.ts +22 -5
- package/lib/services/leader-lock.js +61 -6
- package/lib/services/quota.d.ts +9 -4
- package/lib/services/quota.js +34 -40
- package/lib/services/remote-audit-client.d.ts +28 -32
- package/lib/services/remote-audit-client.js +166 -49
- package/lib/services/retry-forever.js +5 -11
- package/lib/services/retry-strategy.d.ts +1 -1
- package/lib/services/retry-strategy.js +5 -4
- package/lib/services/scheduler.d.ts +8 -3
- package/lib/services/scheduler.js +17 -5
- package/lib/services/service-boot.d.ts +14 -13
- package/lib/services/service-boot.js +18 -15
- package/lib/services/service-keys.d.ts +1 -1
- package/lib/services/service-keys.js +12 -18
- package/lib/services/sse-ticket-store.d.ts +9 -1
- package/lib/services/sse-ticket-store.js +24 -9
- package/lib/services/system-notification.d.ts +27 -0
- package/lib/services/system-notification.js +50 -0
- package/lib/services/token-revocation.d.ts +50 -1
- package/lib/services/token-revocation.js +103 -3
- package/lib/types/audit-events.d.ts +6 -6
- package/lib/types/audit-events.js +1 -1
- package/lib/types/common.d.ts +32 -13
- package/lib/types/common.js +2 -1
- package/lib/types/ecosystem-notifications.d.ts +142 -0
- package/lib/types/ecosystem-notifications.js +210 -0
- package/lib/types/ecosystem.d.ts +77 -0
- package/lib/types/ecosystem.js +119 -0
- package/lib/types/error-codes.d.ts +81 -0
- package/lib/types/error-codes.js +108 -1
- package/lib/types/feature-flags.d.ts +21 -1
- package/lib/types/feature-flags.js +36 -2
- package/lib/types/index.d.ts +14 -9
- package/lib/types/index.js +15 -10
- package/lib/types/permissions.d.ts +46 -8
- package/lib/types/permissions.js +75 -12
- package/lib/types/plugin-catalog.d.ts +35 -0
- package/lib/types/plugin-catalog.js +39 -0
- package/lib/types/plugin-health.d.ts +102 -0
- package/lib/types/plugin-health.js +184 -0
- package/lib/types/quota-tiers.d.ts +15 -0
- package/lib/types/quota-tiers.js +23 -3
- package/lib/types/wire-vocabulary.d.ts +102 -0
- package/lib/types/wire-vocabulary.js +94 -0
- package/lib/utils/alias-resolver.js +2 -2
- package/lib/utils/audit.d.ts +5 -2
- package/lib/utils/audit.js +6 -3
- package/lib/utils/compliance-attributes.js +3 -4
- package/lib/utils/concurrency.d.ts +7 -0
- package/lib/utils/concurrency.js +13 -1
- package/lib/utils/env.d.ts +3 -4
- package/lib/utils/env.js +4 -5
- package/lib/utils/identity.d.ts +13 -4
- package/lib/utils/identity.js +18 -10
- package/lib/utils/index.d.ts +13 -10
- package/lib/utils/index.js +13 -10
- package/lib/utils/jwk.d.ts +4 -0
- package/lib/utils/jwk.js +13 -1
- package/lib/utils/logger.d.ts +1 -1
- package/lib/utils/logger.js +1 -1
- package/lib/utils/object.d.ts +11 -0
- package/lib/utils/object.js +24 -1
- package/lib/utils/params.d.ts +8 -22
- package/lib/utils/params.js +15 -43
- package/lib/utils/plugin-compliance.d.ts +98 -0
- package/lib/utils/plugin-compliance.js +78 -0
- package/lib/utils/proof-of-work.d.ts +69 -0
- package/lib/utils/proof-of-work.js +139 -0
- package/lib/utils/response.d.ts +16 -6
- package/lib/utils/response.js +18 -15
- package/lib/utils/retention.d.ts +17 -0
- package/lib/utils/retention.js +30 -0
- package/lib/utils/secret-encryption.d.ts +7 -7
- package/lib/utils/secret-encryption.js +9 -10
- package/lib/utils/secret-rotation.js +2 -2
- package/lib/utils/sensitive-patterns.js +4 -5
- package/lib/utils/service-registry.d.ts +15 -0
- package/lib/utils/service-registry.js +24 -0
- package/lib/utils/ssrf.d.ts +11 -2
- package/lib/utils/ssrf.js +112 -33
- package/lib/validation/ai-schemas.js +3 -2
- package/lib/validation/common-schemas.d.ts +0 -9
- package/lib/validation/common-schemas.js +1 -7
- package/lib/validation/dockerfile-static.d.ts +26 -0
- package/lib/validation/dockerfile-static.js +210 -0
- package/lib/validation/index.d.ts +13 -4
- package/lib/validation/index.js +14 -5
- package/lib/validation/message-schemas.js +6 -4
- package/lib/validation/pipeline-schemas.d.ts +8 -2
- package/lib/validation/pipeline-schemas.js +30 -4
- package/lib/validation/plugin-base-images.d.ts +20 -0
- package/lib/validation/plugin-base-images.js +17 -0
- package/lib/validation/plugin-catalog-detect.d.ts +86 -0
- package/lib/validation/plugin-catalog-detect.js +237 -0
- package/lib/validation/plugin-catalog-metadata.d.ts +141 -0
- package/lib/validation/plugin-catalog-metadata.js +162 -0
- package/lib/validation/plugin-heuristics.d.ts +41 -0
- package/lib/validation/plugin-heuristics.js +211 -0
- package/lib/validation/plugin-lint.d.ts +18 -0
- package/lib/validation/plugin-lint.js +136 -0
- package/lib/validation/plugin-name-confusable.d.ts +15 -0
- package/lib/validation/plugin-name-confusable.js +81 -0
- package/lib/validation/plugin-schemas.d.ts +40 -26
- package/lib/validation/plugin-schemas.js +26 -23
- package/lib/validation/plugin-spec-schema.d.ts +112 -0
- package/lib/validation/plugin-spec-schema.js +163 -0
- package/lib/validation/plugin-template-contract.d.ts +64 -0
- package/lib/validation/plugin-template-contract.js +113 -0
- package/package.json +69 -4
- package/lib/testing/mock-api-core.d.ts +0 -150
- package/lib/testing/mock-api-core.js +0 -246
- package/lib/testing/mock-drizzle.d.ts +0 -6
- package/lib/testing/mock-drizzle.js +0 -57
- package/lib/testing/route-coverage.d.ts +0 -64
- package/lib/testing/route-coverage.js +0 -183
- package/lib/testing/service-tokens.d.ts +0 -66
- package/lib/testing/service-tokens.js +0 -137
- package/lib/testing/tier-mock.d.ts +0 -44
- package/lib/testing/tier-mock.js +0 -65
- package/lib/testing/user-tokens.d.ts +0 -75
- 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
|
|
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
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
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
|
-
*
|
|
63
|
-
* - `services/retry-strategy` — decision functions only; `parseRetryAfter
|
|
64
|
-
* `addJitter` are backoff internals.
|
|
65
|
-
* - `services/circuit-breaker` — `
|
|
66
|
-
*
|
|
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
|
|
74
|
-
* by DEEP import
|
|
75
|
-
*
|
|
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,
|
|
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=
|