@pipeline-builder/api-core 4.1.3 → 4.1.4
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 -3
- package/lib/errors/app-errors.d.ts +0 -4
- package/lib/errors/app-errors.js +1 -8
- package/lib/helpers/restore-helpers.d.ts +3 -3
- package/lib/helpers/restore-helpers.js +4 -4
- package/lib/helpers/sse-helpers.d.ts +8 -1
- package/lib/helpers/sse-helpers.js +11 -3
- package/lib/middleware/auth.d.ts +174 -43
- package/lib/middleware/auth.js +384 -179
- package/lib/middleware/index.d.ts +1 -0
- package/lib/middleware/index.js +2 -1
- package/lib/middleware/route-table.d.ts +104 -0
- package/lib/middleware/route-table.js +230 -0
- package/lib/middleware/step-up.d.ts +48 -16
- package/lib/middleware/step-up.js +74 -36
- package/lib/services/api-key-exchange.d.ts +25 -0
- package/lib/services/api-key-exchange.js +219 -0
- package/lib/services/cache-service.d.ts +11 -26
- package/lib/services/cache-service.js +20 -74
- package/lib/services/compliance-event-subscriber.d.ts +5 -4
- package/lib/services/compliance-event-subscriber.js +8 -6
- package/lib/services/durable-event-bus.d.ts +22 -39
- package/lib/services/durable-event-bus.js +95 -43
- package/lib/services/env-redis.d.ts +27 -0
- package/lib/services/env-redis.js +63 -1
- package/lib/services/http-client.d.ts +10 -15
- package/lib/services/http-client.js +32 -27
- package/lib/services/index.d.ts +3 -0
- package/lib/services/index.js +4 -1
- package/lib/services/jwks-cache.d.ts +102 -0
- package/lib/services/jwks-cache.js +222 -0
- package/lib/services/quota.js +12 -4
- package/lib/services/remote-audit-client.d.ts +1 -1
- package/lib/services/remote-audit-client.js +32 -1
- package/lib/services/scheduler.d.ts +2 -3
- package/lib/services/scheduler.js +2 -2
- package/lib/services/service-boot.d.ts +4 -2
- package/lib/services/service-boot.js +7 -3
- package/lib/services/service-keys.d.ts +83 -0
- package/lib/services/service-keys.js +354 -0
- package/lib/services/sse-ticket-store.d.ts +55 -22
- package/lib/services/sse-ticket-store.js +122 -79
- package/lib/services/token-revocation.js +30 -18
- package/lib/testing/route-coverage.d.ts +64 -0
- package/lib/testing/route-coverage.js +183 -0
- package/lib/testing/service-tokens.d.ts +66 -0
- package/lib/testing/service-tokens.js +137 -0
- package/lib/testing/tier-mock.d.ts +2 -2
- package/lib/testing/tier-mock.js +3 -3
- package/lib/testing/user-tokens.d.ts +75 -0
- package/lib/testing/user-tokens.js +115 -0
- package/lib/types/catalog-metadata.d.ts +0 -2
- package/lib/types/catalog-metadata.js +1 -3
- package/lib/types/common.d.ts +103 -4
- package/lib/types/common.js +11 -1
- package/lib/types/error-codes.d.ts +15 -0
- package/lib/types/error-codes.js +22 -1
- package/lib/types/permissions.d.ts +35 -1
- package/lib/types/permissions.js +70 -2
- package/lib/utils/api-key.d.ts +34 -0
- package/lib/utils/api-key.js +75 -0
- package/lib/utils/env.d.ts +0 -2
- package/lib/utils/env.js +1 -6
- package/lib/utils/index.d.ts +3 -0
- package/lib/utils/index.js +4 -1
- package/lib/utils/jwk.d.ts +98 -0
- package/lib/utils/jwk.js +139 -0
- package/lib/utils/logger.d.ts +4 -1
- package/lib/utils/logger.js +1 -22
- package/lib/utils/secret-encryption.d.ts +7 -2
- package/lib/utils/secret-encryption.js +47 -4
- package/lib/utils/secret-rotation.d.ts +17 -0
- package/lib/utils/secret-rotation.js +54 -0
- package/lib/validation/common-schemas.js +5 -4
- package/package.json +20 -1
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@ Core server-side utilities (auth middleware, response helpers, error codes, quot
|
|
|
8
8
|
|
|
9
9
|
## Responsibilities
|
|
10
10
|
|
|
11
|
-
Provides the cross-cutting primitives every backend service depends on: JWT authentication and authorization middleware, inter-service token minting, standardized HTTP response and error helpers, request parameter/identity parsing, Zod validation schemas with an OpenAPI registry, a safe service-to-service HTTP client, quota enforcement types and client, structured Winston logging, an in-memory
|
|
11
|
+
Provides the cross-cutting primitives every backend service depends on: JWT authentication and authorization middleware, inter-service token minting, standardized HTTP response and error helpers, request parameter/identity parsing, Zod validation schemas with an OpenAPI registry, a safe service-to-service HTTP client, quota enforcement types and client, structured Winston logging, an in-memory cache with cross-replica invalidation, domain event pub/sub, and the static AI provider catalog.
|
|
12
12
|
|
|
13
13
|
## Key exports
|
|
14
14
|
|
|
@@ -29,7 +29,7 @@ Provides the cross-cutting primitives every backend service depends on: JWT auth
|
|
|
29
29
|
| `sendPaginated`, `sendPaginatedNested`, `parsePaginationParams` | Paginated response helpers |
|
|
30
30
|
| `extractDbError`, `errorMessage` | Safe DB-error and error-to-string extraction |
|
|
31
31
|
| `ErrorCode`, `getStatusForErrorCode` | Standard error code enum and HTTP status mapping |
|
|
32
|
-
| `AppError`, `NotFoundError`, `ForbiddenError`, `ValidationError`, `ConflictError
|
|
32
|
+
| `AppError`, `NotFoundError`, `ForbiddenError`, `ValidationError`, `ConflictError` | Typed HTTP error classes |
|
|
33
33
|
|
|
34
34
|
### Request parsing (`./utils`)
|
|
35
35
|
| Export | Purpose |
|
|
@@ -44,7 +44,7 @@ Provides the cross-cutting primitives every backend service depends on: JWT auth
|
|
|
44
44
|
| `InternalHttpClient`, `createSafeClient` | Service-to-service HTTP client (`ServiceConfig`, `RequestOptions`) |
|
|
45
45
|
| `createComplianceClient` / `ComplianceClient` | Typed compliance-service client built on the safe client |
|
|
46
46
|
| `QuotaService`, `createQuotaService`, `QuotaType`, `QuotaCheckResult`, `QuotaTier`, `QUOTA_TIERS`, `getTierLimits` | Quota enforcement client and tier presets |
|
|
47
|
-
| `CacheService`, `createCacheService` | In-memory TTL cache with
|
|
47
|
+
| `CacheService`, `createCacheService` | In-memory LRU TTL cache with cross-replica invalidation over Redis pub/sub |
|
|
48
48
|
| `entityEvents` | Process-local domain event pub/sub for entity changes |
|
|
49
49
|
|
|
50
50
|
### Logging, validation & OpenAPI
|
|
@@ -24,7 +24,3 @@ export declare class ValidationError extends AppError {
|
|
|
24
24
|
export declare class ConflictError extends AppError {
|
|
25
25
|
constructor(message: string);
|
|
26
26
|
}
|
|
27
|
-
/** 401 — Unauthorized / authentication required. */
|
|
28
|
-
export declare class UnauthorizedError extends AppError {
|
|
29
|
-
constructor(message: string);
|
|
30
|
-
}
|
package/lib/errors/app-errors.js
CHANGED
|
@@ -43,11 +43,4 @@ export class ConflictError extends AppError {
|
|
|
43
43
|
this.name = 'ConflictError';
|
|
44
44
|
}
|
|
45
45
|
}
|
|
46
|
-
|
|
47
|
-
export class UnauthorizedError extends AppError {
|
|
48
|
-
constructor(message) {
|
|
49
|
-
super(getStatusForErrorCode(ErrorCode.UNAUTHORIZED), ErrorCode.UNAUTHORIZED, message);
|
|
50
|
-
this.name = 'UnauthorizedError';
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXBwLWVycm9ycy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9lcnJvcnMvYXBwLWVycm9ycy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSwrQ0FBK0M7QUFDL0Msc0NBQXNDO0FBRXRDLE9BQU8sRUFBRSxTQUFTLEVBQUUscUJBQXFCLEVBQUUsTUFBTSx5QkFBeUIsQ0FBQztBQUUzRTs7O0dBR0c7QUFDSCxNQUFNLE9BQU8sUUFBUyxTQUFRLEtBQUs7SUFFZixVQUFVO0lBQ1YsSUFBSTtJQUZ0QixZQUNrQixVQUFrQixFQUNsQixJQUFlLEVBQy9CLE9BQWU7UUFFZixLQUFLLENBQUMsT0FBTyxDQUFDLENBQUM7MEJBSkMsVUFBVTtvQkFDVixJQUFJO1FBSXBCLElBQUksQ0FBQyxJQUFJLEdBQUcsVUFBVSxDQUFDO0lBQ3pCLENBQUM7Q0FDRjtBQUVELGdDQUFnQztBQUNoQyxNQUFNLE9BQU8sYUFBYyxTQUFRLFFBQVE7SUFDekMsWUFBWSxPQUFlO1FBQ3pCLEtBQUssQ0FBQyxxQkFBcUIsQ0FBQyxTQUFTLENBQUMsU0FBUyxDQUFDLEVBQUUsU0FBUyxDQUFDLFNBQVMsRUFBRSxPQUFPLENBQUMsQ0FBQztRQUNoRixJQUFJLENBQUMsSUFBSSxHQUFHLGVBQWUsQ0FBQztJQUM5QixDQUFDO0NBQ0Y7QUFFRCxzQ0FBc0M7QUFDdEMsTUFBTSxPQUFPLGNBQWUsU0FBUSxRQUFRO0lBQzFDLFlBQVksT0FBZTtRQUN6QixLQUFLLENBQUMscUJBQXFCLENBQUMsU0FBUyxDQUFDLHdCQUF3QixDQUFDLEVBQUUsU0FBUyxDQUFDLHdCQUF3QixFQUFFLE9BQU8sQ0FBQyxDQUFDO1FBQzlHLElBQUksQ0FBQyxJQUFJLEdBQUcsZ0JBQWdCLENBQUM7SUFDL0IsQ0FBQztDQUNGO0FBRUQsc0NBQXNDO0FBQ3RDLE1BQU0sT0FBTyxlQUFnQixTQUFRLFFBQVE7SUFDM0MsWUFBWSxPQUFlO1FBQ3pCLEtBQUssQ0FBQyxxQkFBcUIsQ0FBQyxTQUFTLENBQUMsZ0JBQWdCLENBQUMsRUFBRSxTQUFTLENBQUMsZ0JBQWdCLEVBQUUsT0FBTyxDQUFDLENBQUM7UUFDOUYsSUFBSSxDQUFDLElBQUksR0FBRyxpQkFBaUIsQ0FBQztJQUNoQyxDQUFDO0NBQ0Y7QUFFRCxrQ0FBa0M7QUFDbEMsTUFBTSxPQUFPLGFBQWMsU0FBUSxRQUFRO0lBQ3pDLFlBQVksT0FBZTtRQUN6QixLQUFLLENBQUMscUJBQXFCLENBQUMsU0FBUyxDQUFDLFFBQVEsQ0FBQyxFQUFFLFNBQVMsQ0FBQyxRQUFRLEVBQUUsT0FBTyxDQUFDLENBQUM7UUFDOUUsSUFBSSxDQUFDLElBQUksR0FBRyxlQUFlLENBQUM7SUFDOUIsQ0FBQztDQUNGO0FBRUQsb0RBQW9EO0FBQ3BELE1BQU0sT0FBTyxpQkFBa0IsU0FBUSxRQUFRO0lBQzdDLFlBQVksT0FBZTtRQUN6QixLQUFLLENBQUMscUJBQXFCLENBQUMsU0FBUyxDQUFDLFlBQVksQ0FBQyxFQUFFLFNBQVMsQ0FBQyxZQUFZLEVBQUUsT0FBTyxDQUFDLENBQUM7UUFDdEYsSUFBSSxDQUFDLElBQUksR0FBRyxtQkFBbUIsQ0FBQztJQUNsQyxDQUFDO0NBQ0YiLCJzb3VyY2VzQ29udGVudCI6WyIvLyBDb3B5cmlnaHQgMjAyNiBQaXBlbGluZSBCdWlsZGVyIENvbnRyaWJ1dG9yc1xuLy8gU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IEFwYWNoZS0yLjBcblxuaW1wb3J0IHsgRXJyb3JDb2RlLCBnZXRTdGF0dXNGb3JFcnJvckNvZGUgfSBmcm9tICcuLi90eXBlcy9lcnJvci1jb2Rlcy5qcyc7XG5cbi8qKlxuICogQmFzZSBhcHBsaWNhdGlvbiBlcnJvciB3aXRoIEhUVFAgc3RhdHVzIGNvZGUgYW5kIGVycm9yIGNvZGUuXG4gKiBFeHRlbmQgdGhpcyBmb3IgZG9tYWluLXNwZWNpZmljIGVycm9yIHR5cGVzLlxuICovXG5leHBvcnQgY2xhc3MgQXBwRXJyb3IgZXh0ZW5kcyBFcnJvciB7XG4gIGNvbnN0cnVjdG9yKFxuICAgIHB1YmxpYyByZWFkb25seSBzdGF0dXNDb2RlOiBudW1iZXIsXG4gICAgcHVibGljIHJlYWRvbmx5IGNvZGU6IEVycm9yQ29kZSxcbiAgICBtZXNzYWdlOiBzdHJpbmcsXG4gICkge1xuICAgIHN1cGVyKG1lc3NhZ2UpO1xuICAgIHRoaXMubmFtZSA9ICdBcHBFcnJvcic7XG4gIH1cbn1cblxuLyoqIDQwNCDigJQgUmVzb3VyY2Ugbm90IGZvdW5kLiAqL1xuZXhwb3J0IGNsYXNzIE5vdEZvdW5kRXJyb3IgZXh0ZW5kcyBBcHBFcnJvciB7XG4gIGNvbnN0cnVjdG9yKG1lc3NhZ2U6IHN0cmluZykge1xuICAgIHN1cGVyKGdldFN0YXR1c0ZvckVycm9yQ29kZShFcnJvckNvZGUuTk9UX0ZPVU5EKSwgRXJyb3JDb2RlLk5PVF9GT1VORCwgbWVzc2FnZSk7XG4gICAgdGhpcy5uYW1lID0gJ05vdEZvdW5kRXJyb3InO1xuICB9XG59XG5cbi8qKiA0MDMg4oCUIEluc3VmZmljaWVudCBwZXJtaXNzaW9ucy4gKi9cbmV4cG9ydCBjbGFzcyBGb3JiaWRkZW5FcnJvciBleHRlbmRzIEFwcEVycm9yIHtcbiAgY29uc3RydWN0b3IobWVzc2FnZTogc3RyaW5nKSB7XG4gICAgc3VwZXIoZ2V0U3RhdHVzRm9yRXJyb3JDb2RlKEVycm9yQ29kZS5JTlNVRkZJQ0lFTlRfUEVSTUlTU0lPTlMpLCBFcnJvckNvZGUuSU5TVUZGSUNJRU5UX1BFUk1JU1NJT05TLCBtZXNzYWdlKTtcbiAgICB0aGlzLm5hbWUgPSAnRm9yYmlkZGVuRXJyb3InO1xuICB9XG59XG5cbi8qKiA0MDAg4oCUIFZhbGlkYXRpb24gLyBiYWQgcmVxdWVzdC4gKi9cbmV4cG9ydCBjbGFzcyBWYWxpZGF0aW9uRXJyb3IgZXh0ZW5kcyBBcHBFcnJvciB7XG4gIGNvbnN0cnVjdG9yKG1lc3NhZ2U6IHN0cmluZykge1xuICAgIHN1cGVyKGdldFN0YXR1c0ZvckVycm9yQ29kZShFcnJvckNvZGUuVkFMSURBVElPTl9FUlJPUiksIEVycm9yQ29kZS5WQUxJREFUSU9OX0VSUk9SLCBtZXNzYWdlKTtcbiAgICB0aGlzLm5hbWUgPSAnVmFsaWRhdGlvbkVycm9yJztcbiAgfVxufVxuXG4vKiogNDA5IOKAlCBDb25mbGljdCAvIGR1cGxpY2F0ZS4gKi9cbmV4cG9ydCBjbGFzcyBDb25mbGljdEVycm9yIGV4dGVuZHMgQXBwRXJyb3Ige1xuICBjb25zdHJ1Y3RvcihtZXNzYWdlOiBzdHJpbmcpIHtcbiAgICBzdXBlcihnZXRTdGF0dXNGb3JFcnJvckNvZGUoRXJyb3JDb2RlLkNPTkZMSUNUKSwgRXJyb3JDb2RlLkNPTkZMSUNULCBtZXNzYWdlKTtcbiAgICB0aGlzLm5hbWUgPSAnQ29uZmxpY3RFcnJvcic7XG4gIH1cbn1cblxuLyoqIDQwMSDigJQgVW5hdXRob3JpemVkIC8gYXV0aGVudGljYXRpb24gcmVxdWlyZWQuICovXG5leHBvcnQgY2xhc3MgVW5hdXRob3JpemVkRXJyb3IgZXh0ZW5kcyBBcHBFcnJvciB7XG4gIGNvbnN0cnVjdG9yKG1lc3NhZ2U6IHN0cmluZykge1xuICAgIHN1cGVyKGdldFN0YXR1c0ZvckVycm9yQ29kZShFcnJvckNvZGUuVU5BVVRIT1JJWkVEKSwgRXJyb3JDb2RlLlVOQVVUSE9SSVpFRCwgbWVzc2FnZSk7XG4gICAgdGhpcy5uYW1lID0gJ1VuYXV0aG9yaXplZEVycm9yJztcbiAgfVxufVxuIl19
|
|
46
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXBwLWVycm9ycy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9lcnJvcnMvYXBwLWVycm9ycy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSwrQ0FBK0M7QUFDL0Msc0NBQXNDO0FBRXRDLE9BQU8sRUFBRSxTQUFTLEVBQUUscUJBQXFCLEVBQUUsTUFBTSx5QkFBeUIsQ0FBQztBQUUzRTs7O0dBR0c7QUFDSCxNQUFNLE9BQU8sUUFBUyxTQUFRLEtBQUs7SUFFZixVQUFVO0lBQ1YsSUFBSTtJQUZ0QixZQUNrQixVQUFrQixFQUNsQixJQUFlLEVBQy9CLE9BQWU7UUFFZixLQUFLLENBQUMsT0FBTyxDQUFDLENBQUM7MEJBSkMsVUFBVTtvQkFDVixJQUFJO1FBSXBCLElBQUksQ0FBQyxJQUFJLEdBQUcsVUFBVSxDQUFDO0lBQ3pCLENBQUM7Q0FDRjtBQUVELGdDQUFnQztBQUNoQyxNQUFNLE9BQU8sYUFBYyxTQUFRLFFBQVE7SUFDekMsWUFBWSxPQUFlO1FBQ3pCLEtBQUssQ0FBQyxxQkFBcUIsQ0FBQyxTQUFTLENBQUMsU0FBUyxDQUFDLEVBQUUsU0FBUyxDQUFDLFNBQVMsRUFBRSxPQUFPLENBQUMsQ0FBQztRQUNoRixJQUFJLENBQUMsSUFBSSxHQUFHLGVBQWUsQ0FBQztJQUM5QixDQUFDO0NBQ0Y7QUFFRCxzQ0FBc0M7QUFDdEMsTUFBTSxPQUFPLGNBQWUsU0FBUSxRQUFRO0lBQzFDLFlBQVksT0FBZTtRQUN6QixLQUFLLENBQUMscUJBQXFCLENBQUMsU0FBUyxDQUFDLHdCQUF3QixDQUFDLEVBQUUsU0FBUyxDQUFDLHdCQUF3QixFQUFFLE9BQU8sQ0FBQyxDQUFDO1FBQzlHLElBQUksQ0FBQyxJQUFJLEdBQUcsZ0JBQWdCLENBQUM7SUFDL0IsQ0FBQztDQUNGO0FBRUQsc0NBQXNDO0FBQ3RDLE1BQU0sT0FBTyxlQUFnQixTQUFRLFFBQVE7SUFDM0MsWUFBWSxPQUFlO1FBQ3pCLEtBQUssQ0FBQyxxQkFBcUIsQ0FBQyxTQUFTLENBQUMsZ0JBQWdCLENBQUMsRUFBRSxTQUFTLENBQUMsZ0JBQWdCLEVBQUUsT0FBTyxDQUFDLENBQUM7UUFDOUYsSUFBSSxDQUFDLElBQUksR0FBRyxpQkFBaUIsQ0FBQztJQUNoQyxDQUFDO0NBQ0Y7QUFFRCxrQ0FBa0M7QUFDbEMsTUFBTSxPQUFPLGFBQWMsU0FBUSxRQUFRO0lBQ3pDLFlBQVksT0FBZTtRQUN6QixLQUFLLENBQUMscUJBQXFCLENBQUMsU0FBUyxDQUFDLFFBQVEsQ0FBQyxFQUFFLFNBQVMsQ0FBQyxRQUFRLEVBQUUsT0FBTyxDQUFDLENBQUM7UUFDOUUsSUFBSSxDQUFDLElBQUksR0FBRyxlQUFlLENBQUM7SUFDOUIsQ0FBQztDQUNGIiwic291cmNlc0NvbnRlbnQiOlsiLy8gQ29weXJpZ2h0IDIwMjYgUGlwZWxpbmUgQnVpbGRlciBDb250cmlidXRvcnNcbi8vIFNQRFgtTGljZW5zZS1JZGVudGlmaWVyOiBBcGFjaGUtMi4wXG5cbmltcG9ydCB7IEVycm9yQ29kZSwgZ2V0U3RhdHVzRm9yRXJyb3JDb2RlIH0gZnJvbSAnLi4vdHlwZXMvZXJyb3ItY29kZXMuanMnO1xuXG4vKipcbiAqIEJhc2UgYXBwbGljYXRpb24gZXJyb3Igd2l0aCBIVFRQIHN0YXR1cyBjb2RlIGFuZCBlcnJvciBjb2RlLlxuICogRXh0ZW5kIHRoaXMgZm9yIGRvbWFpbi1zcGVjaWZpYyBlcnJvciB0eXBlcy5cbiAqL1xuZXhwb3J0IGNsYXNzIEFwcEVycm9yIGV4dGVuZHMgRXJyb3Ige1xuICBjb25zdHJ1Y3RvcihcbiAgICBwdWJsaWMgcmVhZG9ubHkgc3RhdHVzQ29kZTogbnVtYmVyLFxuICAgIHB1YmxpYyByZWFkb25seSBjb2RlOiBFcnJvckNvZGUsXG4gICAgbWVzc2FnZTogc3RyaW5nLFxuICApIHtcbiAgICBzdXBlcihtZXNzYWdlKTtcbiAgICB0aGlzLm5hbWUgPSAnQXBwRXJyb3InO1xuICB9XG59XG5cbi8qKiA0MDQg4oCUIFJlc291cmNlIG5vdCBmb3VuZC4gKi9cbmV4cG9ydCBjbGFzcyBOb3RGb3VuZEVycm9yIGV4dGVuZHMgQXBwRXJyb3Ige1xuICBjb25zdHJ1Y3RvcihtZXNzYWdlOiBzdHJpbmcpIHtcbiAgICBzdXBlcihnZXRTdGF0dXNGb3JFcnJvckNvZGUoRXJyb3JDb2RlLk5PVF9GT1VORCksIEVycm9yQ29kZS5OT1RfRk9VTkQsIG1lc3NhZ2UpO1xuICAgIHRoaXMubmFtZSA9ICdOb3RGb3VuZEVycm9yJztcbiAgfVxufVxuXG4vKiogNDAzIOKAlCBJbnN1ZmZpY2llbnQgcGVybWlzc2lvbnMuICovXG5leHBvcnQgY2xhc3MgRm9yYmlkZGVuRXJyb3IgZXh0ZW5kcyBBcHBFcnJvciB7XG4gIGNvbnN0cnVjdG9yKG1lc3NhZ2U6IHN0cmluZykge1xuICAgIHN1cGVyKGdldFN0YXR1c0ZvckVycm9yQ29kZShFcnJvckNvZGUuSU5TVUZGSUNJRU5UX1BFUk1JU1NJT05TKSwgRXJyb3JDb2RlLklOU1VGRklDSUVOVF9QRVJNSVNTSU9OUywgbWVzc2FnZSk7XG4gICAgdGhpcy5uYW1lID0gJ0ZvcmJpZGRlbkVycm9yJztcbiAgfVxufVxuXG4vKiogNDAwIOKAlCBWYWxpZGF0aW9uIC8gYmFkIHJlcXVlc3QuICovXG5leHBvcnQgY2xhc3MgVmFsaWRhdGlvbkVycm9yIGV4dGVuZHMgQXBwRXJyb3Ige1xuICBjb25zdHJ1Y3RvcihtZXNzYWdlOiBzdHJpbmcpIHtcbiAgICBzdXBlcihnZXRTdGF0dXNGb3JFcnJvckNvZGUoRXJyb3JDb2RlLlZBTElEQVRJT05fRVJST1IpLCBFcnJvckNvZGUuVkFMSURBVElPTl9FUlJPUiwgbWVzc2FnZSk7XG4gICAgdGhpcy5uYW1lID0gJ1ZhbGlkYXRpb25FcnJvcic7XG4gIH1cbn1cblxuLyoqIDQwOSDigJQgQ29uZmxpY3QgLyBkdXBsaWNhdGUuICovXG5leHBvcnQgY2xhc3MgQ29uZmxpY3RFcnJvciBleHRlbmRzIEFwcEVycm9yIHtcbiAgY29uc3RydWN0b3IobWVzc2FnZTogc3RyaW5nKSB7XG4gICAgc3VwZXIoZ2V0U3RhdHVzRm9yRXJyb3JDb2RlKEVycm9yQ29kZS5DT05GTElDVCksIEVycm9yQ29kZS5DT05GTElDVCwgbWVzc2FnZSk7XG4gICAgdGhpcy5uYW1lID0gJ0NvbmZsaWN0RXJyb3InO1xuICB9XG59XG4iXX0=
|
|
@@ -38,9 +38,9 @@ export interface PurgeableService<T extends RestorableEntity> {
|
|
|
38
38
|
* genuine tombstone (a live/active row loads as `null` here and 404s, so purge
|
|
39
39
|
* can never hard-delete a live row). Returns `{ existing, purgedId }` on success
|
|
40
40
|
* (the caller emits the entity-specific audit off `existing`), or `null` when it
|
|
41
|
-
* has ALREADY sent a response (400/404/403).
|
|
42
|
-
*
|
|
43
|
-
*
|
|
41
|
+
* has ALREADY sent a response (400/404/403). Step-up is the ROUTE's job: the
|
|
42
|
+
* pipeline, template and plugin services mount purge behind `requireStepUp`
|
|
43
|
+
* (sharing restore's chain so one token isn't consumed twice). DRYs the near-identical
|
|
44
44
|
* pipeline / pipeline_template purge routes.
|
|
45
45
|
*/
|
|
46
46
|
export declare function loadAndPurge<T extends RestorableEntity>(req: Request, res: Response, orgId: string, service: PurgeableService<T>, label: string, publishPermission: Permission, userId: string): Promise<{
|
|
@@ -45,9 +45,9 @@ export async function loadAndRestore(req, res, orgId, userId, service, label, pu
|
|
|
45
45
|
* genuine tombstone (a live/active row loads as `null` here and 404s, so purge
|
|
46
46
|
* can never hard-delete a live row). Returns `{ existing, purgedId }` on success
|
|
47
47
|
* (the caller emits the entity-specific audit off `existing`), or `null` when it
|
|
48
|
-
* has ALREADY sent a response (400/404/403).
|
|
49
|
-
*
|
|
50
|
-
*
|
|
48
|
+
* has ALREADY sent a response (400/404/403). Step-up is the ROUTE's job: the
|
|
49
|
+
* pipeline, template and plugin services mount purge behind `requireStepUp`
|
|
50
|
+
* (sharing restore's chain so one token isn't consumed twice). DRYs the near-identical
|
|
51
51
|
* pipeline / pipeline_template purge routes.
|
|
52
52
|
*/
|
|
53
53
|
export async function loadAndPurge(req, res, orgId, service, label, publishPermission, userId) {
|
|
@@ -75,4 +75,4 @@ export async function loadAndPurge(req, res, orgId, service, label, publishPermi
|
|
|
75
75
|
}
|
|
76
76
|
return { existing, purgedId };
|
|
77
77
|
}
|
|
78
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVzdG9yZS1oZWxwZXJzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2hlbHBlcnMvcmVzdG9yZS1oZWxwZXJzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLCtDQUErQztBQUMvQyxzQ0FBc0M7QUFHdEMsT0FBTyxFQUFFLDRCQUE0QixFQUFFLE1BQU0scUJBQXFCLENBQUM7QUFDbkUsT0FBTyxFQUFFLGtCQUFrQixFQUFFLE1BQU0sbUJBQW1CLENBQUM7QUFDdkQsT0FBTyxFQUFFLFNBQVMsRUFBRSxNQUFNLHlCQUF5QixDQUFDO0FBRXBELE9BQU8sRUFBRSxRQUFRLEVBQUUsTUFBTSxvQkFBb0IsQ0FBQztBQUM5QyxPQUFPLEVBQUUsY0FBYyxFQUFFLE1BQU0sc0JBQXNCLENBQUM7QUFpQnREOzs7Ozs7OztHQVFHO0FBQ0gsTUFBTSxDQUFDLEtBQUssVUFBVSxjQUFjLENBQ2xDLEdBQVksRUFDWixHQUFhLEVBQ2IsS0FBYSxFQUNiLE1BQWMsRUFDZCxPQUE2QixFQUM3QixLQUFhLEVBQ2IsaUJBQTZCO0lBRTdCLE1BQU0sRUFBRSxHQUFHLFFBQVEsQ0FBQyxHQUFHLENBQUMsTUFBTSxFQUFFLElBQUksQ0FBQyxDQUFDO0lBQ3RDLElBQUksQ0FBQyxFQUFFLEVBQUUsQ0FBQztRQUNSLGNBQWMsQ0FBQyxHQUFHLEVBQUUsR0FBRyxLQUFLLGtCQUFrQixFQUFFLFNBQVMsQ0FBQyxzQkFBc0IsQ0FBQyxDQUFDO1FBQ2xGLE9BQU8sSUFBSSxDQUFDO0lBQ2QsQ0FBQztJQUVELGdGQUFnRjtJQUNoRixNQUFNLFFBQVEsR0FBRyxNQUFNLE9BQU8sQ0FBQyxlQUFlLENBQUMsRUFBRSxFQUFFLEtBQUssQ0FBQyxDQUFDO0lBQzFELElBQUksQ0FBQyxRQUFRLEVBQUUsQ0FBQztRQUNkLGtCQUFrQixDQUFDLEdBQUcsRUFBRSxLQUFLLENBQUMsQ0FBQztRQUMvQixPQUFPLElBQUksQ0FBQztJQUNkLENBQUM7SUFFRCwrRUFBK0U7SUFDL0UsMkVBQTJFO0lBQzNFLDZFQUE2RTtJQUM3RSxJQUFJLENBQUMsNEJBQTRCLENBQUMsR0FBRyxFQUFFLEdBQUcsRUFBRSxRQUFRLEVBQUUsTUFBTSxJQUFJLEVBQUUsRUFBRSxpQkFBaUIsQ0FBQztRQUFFLE9BQU8sSUFBSSxDQUFDO0lBRXBHLE1BQU0sUUFBUSxHQUFHLE1BQU0sT0FBTyxDQUFDLE9BQU8sQ0FBQyxFQUFFLEVBQUUsS0FBSyxFQUFFLE1BQU0sSUFBSSxRQUFRLENBQUMsQ0FBQztJQUN0RSxJQUFJLENBQUMsUUFBUSxFQUFFLENBQUM7UUFDZCxrQkFBa0IsQ0FBQyxHQUFHLEVBQUUsS0FBSyxDQUFDLENBQUM7UUFDL0IsT0FBTyxJQUFJLENBQUM7SUFDZCxDQUFDO0lBRUQsT0FBTyxFQUFFLFFBQVEsRUFBRSxRQUFRLEVBQUUsQ0FBQztBQUNoQyxDQUFDO0FBUUQ7Ozs7Ozs7Ozs7O0dBV0c7QUFDSCxNQUFNLENBQUMsS0FBSyxVQUFVLFlBQVksQ0FDaEMsR0FBWSxFQUNaLEdBQWEsRUFDYixLQUFhLEVBQ2IsT0FBNEIsRUFDNUIsS0FBYSxFQUNiLGlCQUE2QixFQUM3QixNQUFjO0lBRWQsTUFBTSxFQUFFLEdBQUcsUUFBUSxDQUFDLEdBQUcsQ0FBQyxNQUFNLEVBQUUsSUFBSSxDQUFDLENBQUM7SUFDdEMsSUFBSSxDQUFDLEVBQUUsRUFBRSxDQUFDO1FBQ1IsY0FBYyxDQUFDLEdBQUcsRUFBRSxHQUFHLEtBQUssa0JBQWtCLEVBQUUsU0FBUyxDQUFDLHNCQUFzQixDQUFDLENBQUM7UUFDbEYsT0FBTyxJQUFJLENBQUM7SUFDZCxDQUFDO0lBRUQsZ0ZBQWdGO0lBQ2hGLHdFQUF3RTtJQUN4RSxNQUFNLFFBQVEsR0FBRyxNQUFNLE9BQU8sQ0FBQyxlQUFlLENBQUMsRUFBRSxFQUFFLEtBQUssQ0FBQyxDQUFDO0lBQzFELElBQUksQ0FBQyxRQUFRLEVBQUUsQ0FBQztRQUNkLGtCQUFrQixDQUFDLEdBQUcsRUFBRSxLQUFLLENBQUMsQ0FBQztRQUMvQixPQUFPLElBQUksQ0FBQztJQUNkLENBQUM7SUFFRCxnRUFBZ0U7SUFDaEUsSUFBSSxDQUFDLDRCQUE0QixDQUFDLEdBQUcsRUFBRSxHQUFHLEVBQUUsUUFBUSxFQUFFLE1BQU0sSUFBSSxFQUFFLEVBQUUsaUJBQWlCLENBQUM7UUFBRSxPQUFPLElBQUksQ0FBQztJQUVwRyw0RUFBNEU7SUFDNUUsNEVBQTRFO0lBQzVFLE1BQU0sUUFBUSxHQUFHLE1BQU0sT0FBTyxDQUFDLFNBQVMsQ0FBQyxFQUFFLEVBQUUsS0FBSyxDQUFDLENBQUM7SUFDcEQsSUFBSSxDQUFDLFFBQVEsRUFBRSxDQUFDO1FBQ2Qsa0JBQWtCLENBQUMsR0FBRyxFQUFFLEtBQUssQ0FBQyxDQUFDO1FBQy9CLE9BQU8sSUFBSSxDQUFDO0lBQ2QsQ0FBQztJQUVELE9BQU8sRUFBRSxRQUFRLEVBQUUsUUFBUSxFQUFFLENBQUM7QUFDaEMsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8vIENvcHlyaWdodCAyMDI2IFBpcGVsaW5lIEJ1aWxkZXIgQ29udHJpYnV0b3JzXG4vLyBTUERYLUxpY2Vuc2UtSWRlbnRpZmllcjogQXBhY2hlLTIuMFxuXG5pbXBvcnQgdHlwZSB7IFJlcXVlc3QsIFJlc3BvbnNlIH0gZnJvbSAnZXhwcmVzcyc7XG5pbXBvcnQgeyByZXF1aXJlVmlzaWJpbGl0eVdyaXRlQWNjZXNzIH0gZnJvbSAnLi9hY2Nlc3MtaGVscGVycy5qcyc7XG5pbXBvcnQgeyBzZW5kRW50aXR5Tm90Rm91bmQgfSBmcm9tICcuL2NydWQtaGVscGVycy5qcyc7XG5pbXBvcnQgeyBFcnJvckNvZGUgfSBmcm9tICcuLi90eXBlcy9lcnJvci1jb2Rlcy5qcyc7XG5pbXBvcnQgdHlwZSB7IFBlcm1pc3Npb24gfSBmcm9tICcuLi90eXBlcy9wZXJtaXNzaW9ucy5qcyc7XG5pbXBvcnQgeyBnZXRQYXJhbSB9IGZyb20gJy4uL3V0aWxzL3BhcmFtcy5qcyc7XG5pbXBvcnQgeyBzZW5kQmFkUmVxdWVzdCB9IGZyb20gJy4uL3V0aWxzL3Jlc3BvbnNlLmpzJztcblxuLyoqIE1pbmltYWwgc2hhcGUgYSByZXN0b3JlIHJvdXRlIG5lZWRzIGZyb20gdGhlIGVudGl0eSBpdCByZXN0b3Jlcy4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgUmVzdG9yYWJsZUVudGl0eSB7XG4gIG9yZ0lkOiBzdHJpbmc7XG4gIC8qKiBSdW5nIHRoZSB0b21ic3RvbmUgc2F0IGF0IOKAlCBkcml2ZXMgdGhlIHJlc3RvcmUvcHVyZ2UgYXV0aG9yaXR5IGNoZWNrLiAqL1xuICB2aXNpYmlsaXR5Pzogc3RyaW5nO1xuICAvKiogQXV0aG9yIOKAlCB0aGUgb25seSBub24tYWRtaW4gd2hvIG1heSByZXN0b3JlL3B1cmdlIGEgYHByaXZhdGVgIHJvdy4gKi9cbiAgY3JlYXRlZEJ5Pzogc3RyaW5nO1xufVxuXG4vKiogVGhlIHNsaWNlIG9mIGEgc2VydmljZSBhIHJlc3RvcmUgcm91dGUgdXNlcyAoc2F0aXNmaWVkIGJ5IGFueSBDcnVkU2VydmljZSkuICovXG5leHBvcnQgaW50ZXJmYWNlIFJlc3RvcmFibGVTZXJ2aWNlPFQgZXh0ZW5kcyBSZXN0b3JhYmxlRW50aXR5PiB7XG4gIGZpbmREZWxldGVkQnlJZChpZDogc3RyaW5nLCBvcmdJZD86IHN0cmluZyk6IFByb21pc2U8VCB8IG51bGw+O1xuICByZXN0b3JlKGlkOiBzdHJpbmcsIG9yZ0lkOiBzdHJpbmcsIHVzZXJJZDogc3RyaW5nKTogUHJvbWlzZTxUIHwgbnVsbD47XG59XG5cbi8qKlxuICogU2hhcmVkIHNrZWxldG9uIGZvciBhIHN0ZXAtdXAtZ2F0ZWQgZW50aXR5IHJlc3RvcmUgcm91dGU6IGV4dHJhY3QgYDppZGAsIGxvYWRcbiAqIHRoZSBvd24tb3JnIHRvbWJzdG9uZSwgZ2F0ZSBpdCBvbiB0aGUgdmlzaWJpbGl0eSBsYWRkZXIgKHBhcml0eSB3aXRoIGRlbGV0ZSksXG4gKiByZXN0b3JlLCBhbmQgNDA0IG9uIG1pc3MuIFJldHVybnMgYHsgZXhpc3RpbmcsIHJlc3RvcmVkIH1gIG9uIHN1Y2Nlc3MsXG4gKiBvciBgbnVsbGAgd2hlbiBpdCBoYXMgQUxSRUFEWSBzZW50IGEgcmVzcG9uc2UgKDQwMC80MDQvNDAzKSDigJQgdGhlIGNhbGxlciB0aGVuXG4gKiBkb2VzIHRoZSBlbnRpdHktc3BlY2lmaWMgYXVkaXQgKyByZXNwb25zZS4gRFJZcyB0aGUgbmVhci1pZGVudGljYWxcbiAqIHBpcGVsaW5lIC8gcGx1Z2luIC8gcGlwZWxpbmVfdGVtcGxhdGUgcmVzdG9yZSByb3V0ZXMgKHdoaWNoIGRpZmZlciBvbmx5IGluXG4gKiBzZXJ2aWNlLCBhdWRpdCBhY3Rpb24sIGFuZCByZXNwb25zZSBzaGFwaW5nKS5cbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGxvYWRBbmRSZXN0b3JlPFQgZXh0ZW5kcyBSZXN0b3JhYmxlRW50aXR5PihcbiAgcmVxOiBSZXF1ZXN0LFxuICByZXM6IFJlc3BvbnNlLFxuICBvcmdJZDogc3RyaW5nLFxuICB1c2VySWQ6IHN0cmluZyxcbiAgc2VydmljZTogUmVzdG9yYWJsZVNlcnZpY2U8VD4sXG4gIGxhYmVsOiBzdHJpbmcsXG4gIHB1Ymxpc2hQZXJtaXNzaW9uOiBQZXJtaXNzaW9uLFxuKTogUHJvbWlzZTx7IGV4aXN0aW5nOiBUOyByZXN0b3JlZDogVCB9IHwgbnVsbD4ge1xuICBjb25zdCBpZCA9IGdldFBhcmFtKHJlcS5wYXJhbXMsICdpZCcpO1xuICBpZiAoIWlkKSB7XG4gICAgc2VuZEJhZFJlcXVlc3QocmVzLCBgJHtsYWJlbH0gSUQgaXMgcmVxdWlyZWQuYCwgRXJyb3JDb2RlLk1JU1NJTkdfUkVRVUlSRURfRklFTEQpO1xuICAgIHJldHVybiBudWxsO1xuICB9XG5cbiAgLy8gTG9hZCB0aGUgVE9NQlNUT05FIChvd24tb3JnKS4gQSBsaXZlIHJvdyAvIHVua25vd24gaWQgLyBhbHJlYWR5LXB1cmdlZCDihpIgNDA0LlxuICBjb25zdCBleGlzdGluZyA9IGF3YWl0IHNlcnZpY2UuZmluZERlbGV0ZWRCeUlkKGlkLCBvcmdJZCk7XG4gIGlmICghZXhpc3RpbmcpIHtcbiAgICBzZW5kRW50aXR5Tm90Rm91bmQocmVzLCBsYWJlbCk7XG4gICAgcmV0dXJuIG51bGw7XG4gIH1cblxuICAvLyBTYW1lIGxhZGRlciBhdXRob3JpdHkgYXMgZGVsZXRlOiBgOnB1Ymxpc2hgIGZvciBhIHB1YmxpYyByb3csIGF1dGhvcnNoaXAgZm9yXG4gIC8vIGEgcHJpdmF0ZSBvbmUuIExvYWQtYmVhcmluZyBoZXJlIOKAlCB0aGUgdG9tYnN0b25lIGxvYWQgaXMgb3JnLXNjb3BlZCwgbm90XG4gIC8vIHZpc2liaWxpdHktc2NvcGVkLCBzbyB0aGlzIGlzIHdoYXQgc3RvcHMgYSBjb2xsZWFndWUgcmVzdXJyZWN0aW5nIGEgZHJhZnQuXG4gIGlmICghcmVxdWlyZVZpc2liaWxpdHlXcml0ZUFjY2VzcyhyZXEsIHJlcywgZXhpc3RpbmcsIHVzZXJJZCB8fCAnJywgcHVibGlzaFBlcm1pc3Npb24pKSByZXR1cm4gbnVsbDtcblxuICBjb25zdCByZXN0b3JlZCA9IGF3YWl0IHNlcnZpY2UucmVzdG9yZShpZCwgb3JnSWQsIHVzZXJJZCB8fCAnc3lzdGVtJyk7XG4gIGlmICghcmVzdG9yZWQpIHtcbiAgICBzZW5kRW50aXR5Tm90Rm91bmQocmVzLCBsYWJlbCk7XG4gICAgcmV0dXJuIG51bGw7XG4gIH1cblxuICByZXR1cm4geyBleGlzdGluZywgcmVzdG9yZWQgfTtcbn1cblxuLyoqIFRoZSBzbGljZSBvZiBhIHNlcnZpY2UgYSBwdXJnZSByb3V0ZSB1c2VzIChzYXRpc2ZpZWQgYnkgYW55IENydWRTZXJ2aWNlKS4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgUHVyZ2VhYmxlU2VydmljZTxUIGV4dGVuZHMgUmVzdG9yYWJsZUVudGl0eT4ge1xuICBmaW5kRGVsZXRlZEJ5SWQoaWQ6IHN0cmluZywgb3JnSWQ/
|
|
78
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVzdG9yZS1oZWxwZXJzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2hlbHBlcnMvcmVzdG9yZS1oZWxwZXJzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLCtDQUErQztBQUMvQyxzQ0FBc0M7QUFHdEMsT0FBTyxFQUFFLDRCQUE0QixFQUFFLE1BQU0scUJBQXFCLENBQUM7QUFDbkUsT0FBTyxFQUFFLGtCQUFrQixFQUFFLE1BQU0sbUJBQW1CLENBQUM7QUFDdkQsT0FBTyxFQUFFLFNBQVMsRUFBRSxNQUFNLHlCQUF5QixDQUFDO0FBRXBELE9BQU8sRUFBRSxRQUFRLEVBQUUsTUFBTSxvQkFBb0IsQ0FBQztBQUM5QyxPQUFPLEVBQUUsY0FBYyxFQUFFLE1BQU0sc0JBQXNCLENBQUM7QUFpQnREOzs7Ozs7OztHQVFHO0FBQ0gsTUFBTSxDQUFDLEtBQUssVUFBVSxjQUFjLENBQ2xDLEdBQVksRUFDWixHQUFhLEVBQ2IsS0FBYSxFQUNiLE1BQWMsRUFDZCxPQUE2QixFQUM3QixLQUFhLEVBQ2IsaUJBQTZCO0lBRTdCLE1BQU0sRUFBRSxHQUFHLFFBQVEsQ0FBQyxHQUFHLENBQUMsTUFBTSxFQUFFLElBQUksQ0FBQyxDQUFDO0lBQ3RDLElBQUksQ0FBQyxFQUFFLEVBQUUsQ0FBQztRQUNSLGNBQWMsQ0FBQyxHQUFHLEVBQUUsR0FBRyxLQUFLLGtCQUFrQixFQUFFLFNBQVMsQ0FBQyxzQkFBc0IsQ0FBQyxDQUFDO1FBQ2xGLE9BQU8sSUFBSSxDQUFDO0lBQ2QsQ0FBQztJQUVELGdGQUFnRjtJQUNoRixNQUFNLFFBQVEsR0FBRyxNQUFNLE9BQU8sQ0FBQyxlQUFlLENBQUMsRUFBRSxFQUFFLEtBQUssQ0FBQyxDQUFDO0lBQzFELElBQUksQ0FBQyxRQUFRLEVBQUUsQ0FBQztRQUNkLGtCQUFrQixDQUFDLEdBQUcsRUFBRSxLQUFLLENBQUMsQ0FBQztRQUMvQixPQUFPLElBQUksQ0FBQztJQUNkLENBQUM7SUFFRCwrRUFBK0U7SUFDL0UsMkVBQTJFO0lBQzNFLDZFQUE2RTtJQUM3RSxJQUFJLENBQUMsNEJBQTRCLENBQUMsR0FBRyxFQUFFLEdBQUcsRUFBRSxRQUFRLEVBQUUsTUFBTSxJQUFJLEVBQUUsRUFBRSxpQkFBaUIsQ0FBQztRQUFFLE9BQU8sSUFBSSxDQUFDO0lBRXBHLE1BQU0sUUFBUSxHQUFHLE1BQU0sT0FBTyxDQUFDLE9BQU8sQ0FBQyxFQUFFLEVBQUUsS0FBSyxFQUFFLE1BQU0sSUFBSSxRQUFRLENBQUMsQ0FBQztJQUN0RSxJQUFJLENBQUMsUUFBUSxFQUFFLENBQUM7UUFDZCxrQkFBa0IsQ0FBQyxHQUFHLEVBQUUsS0FBSyxDQUFDLENBQUM7UUFDL0IsT0FBTyxJQUFJLENBQUM7SUFDZCxDQUFDO0lBRUQsT0FBTyxFQUFFLFFBQVEsRUFBRSxRQUFRLEVBQUUsQ0FBQztBQUNoQyxDQUFDO0FBUUQ7Ozs7Ozs7Ozs7O0dBV0c7QUFDSCxNQUFNLENBQUMsS0FBSyxVQUFVLFlBQVksQ0FDaEMsR0FBWSxFQUNaLEdBQWEsRUFDYixLQUFhLEVBQ2IsT0FBNEIsRUFDNUIsS0FBYSxFQUNiLGlCQUE2QixFQUM3QixNQUFjO0lBRWQsTUFBTSxFQUFFLEdBQUcsUUFBUSxDQUFDLEdBQUcsQ0FBQyxNQUFNLEVBQUUsSUFBSSxDQUFDLENBQUM7SUFDdEMsSUFBSSxDQUFDLEVBQUUsRUFBRSxDQUFDO1FBQ1IsY0FBYyxDQUFDLEdBQUcsRUFBRSxHQUFHLEtBQUssa0JBQWtCLEVBQUUsU0FBUyxDQUFDLHNCQUFzQixDQUFDLENBQUM7UUFDbEYsT0FBTyxJQUFJLENBQUM7SUFDZCxDQUFDO0lBRUQsZ0ZBQWdGO0lBQ2hGLHdFQUF3RTtJQUN4RSxNQUFNLFFBQVEsR0FBRyxNQUFNLE9BQU8sQ0FBQyxlQUFlLENBQUMsRUFBRSxFQUFFLEtBQUssQ0FBQyxDQUFDO0lBQzFELElBQUksQ0FBQyxRQUFRLEVBQUUsQ0FBQztRQUNkLGtCQUFrQixDQUFDLEdBQUcsRUFBRSxLQUFLLENBQUMsQ0FBQztRQUMvQixPQUFPLElBQUksQ0FBQztJQUNkLENBQUM7SUFFRCxnRUFBZ0U7SUFDaEUsSUFBSSxDQUFDLDRCQUE0QixDQUFDLEdBQUcsRUFBRSxHQUFHLEVBQUUsUUFBUSxFQUFFLE1BQU0sSUFBSSxFQUFFLEVBQUUsaUJBQWlCLENBQUM7UUFBRSxPQUFPLElBQUksQ0FBQztJQUVwRyw0RUFBNEU7SUFDNUUsNEVBQTRFO0lBQzVFLE1BQU0sUUFBUSxHQUFHLE1BQU0sT0FBTyxDQUFDLFNBQVMsQ0FBQyxFQUFFLEVBQUUsS0FBSyxDQUFDLENBQUM7SUFDcEQsSUFBSSxDQUFDLFFBQVEsRUFBRSxDQUFDO1FBQ2Qsa0JBQWtCLENBQUMsR0FBRyxFQUFFLEtBQUssQ0FBQyxDQUFDO1FBQy9CLE9BQU8sSUFBSSxDQUFDO0lBQ2QsQ0FBQztJQUVELE9BQU8sRUFBRSxRQUFRLEVBQUUsUUFBUSxFQUFFLENBQUM7QUFDaEMsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8vIENvcHlyaWdodCAyMDI2IFBpcGVsaW5lIEJ1aWxkZXIgQ29udHJpYnV0b3JzXG4vLyBTUERYLUxpY2Vuc2UtSWRlbnRpZmllcjogQXBhY2hlLTIuMFxuXG5pbXBvcnQgdHlwZSB7IFJlcXVlc3QsIFJlc3BvbnNlIH0gZnJvbSAnZXhwcmVzcyc7XG5pbXBvcnQgeyByZXF1aXJlVmlzaWJpbGl0eVdyaXRlQWNjZXNzIH0gZnJvbSAnLi9hY2Nlc3MtaGVscGVycy5qcyc7XG5pbXBvcnQgeyBzZW5kRW50aXR5Tm90Rm91bmQgfSBmcm9tICcuL2NydWQtaGVscGVycy5qcyc7XG5pbXBvcnQgeyBFcnJvckNvZGUgfSBmcm9tICcuLi90eXBlcy9lcnJvci1jb2Rlcy5qcyc7XG5pbXBvcnQgdHlwZSB7IFBlcm1pc3Npb24gfSBmcm9tICcuLi90eXBlcy9wZXJtaXNzaW9ucy5qcyc7XG5pbXBvcnQgeyBnZXRQYXJhbSB9IGZyb20gJy4uL3V0aWxzL3BhcmFtcy5qcyc7XG5pbXBvcnQgeyBzZW5kQmFkUmVxdWVzdCB9IGZyb20gJy4uL3V0aWxzL3Jlc3BvbnNlLmpzJztcblxuLyoqIE1pbmltYWwgc2hhcGUgYSByZXN0b3JlIHJvdXRlIG5lZWRzIGZyb20gdGhlIGVudGl0eSBpdCByZXN0b3Jlcy4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgUmVzdG9yYWJsZUVudGl0eSB7XG4gIG9yZ0lkOiBzdHJpbmc7XG4gIC8qKiBSdW5nIHRoZSB0b21ic3RvbmUgc2F0IGF0IOKAlCBkcml2ZXMgdGhlIHJlc3RvcmUvcHVyZ2UgYXV0aG9yaXR5IGNoZWNrLiAqL1xuICB2aXNpYmlsaXR5Pzogc3RyaW5nO1xuICAvKiogQXV0aG9yIOKAlCB0aGUgb25seSBub24tYWRtaW4gd2hvIG1heSByZXN0b3JlL3B1cmdlIGEgYHByaXZhdGVgIHJvdy4gKi9cbiAgY3JlYXRlZEJ5Pzogc3RyaW5nO1xufVxuXG4vKiogVGhlIHNsaWNlIG9mIGEgc2VydmljZSBhIHJlc3RvcmUgcm91dGUgdXNlcyAoc2F0aXNmaWVkIGJ5IGFueSBDcnVkU2VydmljZSkuICovXG5leHBvcnQgaW50ZXJmYWNlIFJlc3RvcmFibGVTZXJ2aWNlPFQgZXh0ZW5kcyBSZXN0b3JhYmxlRW50aXR5PiB7XG4gIGZpbmREZWxldGVkQnlJZChpZDogc3RyaW5nLCBvcmdJZD86IHN0cmluZyk6IFByb21pc2U8VCB8IG51bGw+O1xuICByZXN0b3JlKGlkOiBzdHJpbmcsIG9yZ0lkOiBzdHJpbmcsIHVzZXJJZDogc3RyaW5nKTogUHJvbWlzZTxUIHwgbnVsbD47XG59XG5cbi8qKlxuICogU2hhcmVkIHNrZWxldG9uIGZvciBhIHN0ZXAtdXAtZ2F0ZWQgZW50aXR5IHJlc3RvcmUgcm91dGU6IGV4dHJhY3QgYDppZGAsIGxvYWRcbiAqIHRoZSBvd24tb3JnIHRvbWJzdG9uZSwgZ2F0ZSBpdCBvbiB0aGUgdmlzaWJpbGl0eSBsYWRkZXIgKHBhcml0eSB3aXRoIGRlbGV0ZSksXG4gKiByZXN0b3JlLCBhbmQgNDA0IG9uIG1pc3MuIFJldHVybnMgYHsgZXhpc3RpbmcsIHJlc3RvcmVkIH1gIG9uIHN1Y2Nlc3MsXG4gKiBvciBgbnVsbGAgd2hlbiBpdCBoYXMgQUxSRUFEWSBzZW50IGEgcmVzcG9uc2UgKDQwMC80MDQvNDAzKSDigJQgdGhlIGNhbGxlciB0aGVuXG4gKiBkb2VzIHRoZSBlbnRpdHktc3BlY2lmaWMgYXVkaXQgKyByZXNwb25zZS4gRFJZcyB0aGUgbmVhci1pZGVudGljYWxcbiAqIHBpcGVsaW5lIC8gcGx1Z2luIC8gcGlwZWxpbmVfdGVtcGxhdGUgcmVzdG9yZSByb3V0ZXMgKHdoaWNoIGRpZmZlciBvbmx5IGluXG4gKiBzZXJ2aWNlLCBhdWRpdCBhY3Rpb24sIGFuZCByZXNwb25zZSBzaGFwaW5nKS5cbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGxvYWRBbmRSZXN0b3JlPFQgZXh0ZW5kcyBSZXN0b3JhYmxlRW50aXR5PihcbiAgcmVxOiBSZXF1ZXN0LFxuICByZXM6IFJlc3BvbnNlLFxuICBvcmdJZDogc3RyaW5nLFxuICB1c2VySWQ6IHN0cmluZyxcbiAgc2VydmljZTogUmVzdG9yYWJsZVNlcnZpY2U8VD4sXG4gIGxhYmVsOiBzdHJpbmcsXG4gIHB1Ymxpc2hQZXJtaXNzaW9uOiBQZXJtaXNzaW9uLFxuKTogUHJvbWlzZTx7IGV4aXN0aW5nOiBUOyByZXN0b3JlZDogVCB9IHwgbnVsbD4ge1xuICBjb25zdCBpZCA9IGdldFBhcmFtKHJlcS5wYXJhbXMsICdpZCcpO1xuICBpZiAoIWlkKSB7XG4gICAgc2VuZEJhZFJlcXVlc3QocmVzLCBgJHtsYWJlbH0gSUQgaXMgcmVxdWlyZWQuYCwgRXJyb3JDb2RlLk1JU1NJTkdfUkVRVUlSRURfRklFTEQpO1xuICAgIHJldHVybiBudWxsO1xuICB9XG5cbiAgLy8gTG9hZCB0aGUgVE9NQlNUT05FIChvd24tb3JnKS4gQSBsaXZlIHJvdyAvIHVua25vd24gaWQgLyBhbHJlYWR5LXB1cmdlZCDihpIgNDA0LlxuICBjb25zdCBleGlzdGluZyA9IGF3YWl0IHNlcnZpY2UuZmluZERlbGV0ZWRCeUlkKGlkLCBvcmdJZCk7XG4gIGlmICghZXhpc3RpbmcpIHtcbiAgICBzZW5kRW50aXR5Tm90Rm91bmQocmVzLCBsYWJlbCk7XG4gICAgcmV0dXJuIG51bGw7XG4gIH1cblxuICAvLyBTYW1lIGxhZGRlciBhdXRob3JpdHkgYXMgZGVsZXRlOiBgOnB1Ymxpc2hgIGZvciBhIHB1YmxpYyByb3csIGF1dGhvcnNoaXAgZm9yXG4gIC8vIGEgcHJpdmF0ZSBvbmUuIExvYWQtYmVhcmluZyBoZXJlIOKAlCB0aGUgdG9tYnN0b25lIGxvYWQgaXMgb3JnLXNjb3BlZCwgbm90XG4gIC8vIHZpc2liaWxpdHktc2NvcGVkLCBzbyB0aGlzIGlzIHdoYXQgc3RvcHMgYSBjb2xsZWFndWUgcmVzdXJyZWN0aW5nIGEgZHJhZnQuXG4gIGlmICghcmVxdWlyZVZpc2liaWxpdHlXcml0ZUFjY2VzcyhyZXEsIHJlcywgZXhpc3RpbmcsIHVzZXJJZCB8fCAnJywgcHVibGlzaFBlcm1pc3Npb24pKSByZXR1cm4gbnVsbDtcblxuICBjb25zdCByZXN0b3JlZCA9IGF3YWl0IHNlcnZpY2UucmVzdG9yZShpZCwgb3JnSWQsIHVzZXJJZCB8fCAnc3lzdGVtJyk7XG4gIGlmICghcmVzdG9yZWQpIHtcbiAgICBzZW5kRW50aXR5Tm90Rm91bmQocmVzLCBsYWJlbCk7XG4gICAgcmV0dXJuIG51bGw7XG4gIH1cblxuICByZXR1cm4geyBleGlzdGluZywgcmVzdG9yZWQgfTtcbn1cblxuLyoqIFRoZSBzbGljZSBvZiBhIHNlcnZpY2UgYSBwdXJnZSByb3V0ZSB1c2VzIChzYXRpc2ZpZWQgYnkgYW55IENydWRTZXJ2aWNlKS4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgUHVyZ2VhYmxlU2VydmljZTxUIGV4dGVuZHMgUmVzdG9yYWJsZUVudGl0eT4ge1xuICBmaW5kRGVsZXRlZEJ5SWQoaWQ6IHN0cmluZywgb3JnSWQ/OiBzdHJpbmcpOiBQcm9taXNlPFQgfCBudWxsPjtcbiAgcHVyZ2VCeUlkKGlkOiBzdHJpbmcsIG9yZ0lkPzogc3RyaW5nKTogUHJvbWlzZTxzdHJpbmcgfCBudWxsPjtcbn1cblxuLyoqXG4gKiBTaGFyZWQgc2tlbGV0b24gZm9yIGFuIGVudGl0eSBQVVJHRSByb3V0ZTogZXh0cmFjdCBgOmlkYCwgbG9hZCB0aGUgb3duLW9yZ1xuICogdG9tYnN0b25lLCBnYXRlIGl0IG9uIHRoZSB2aXNpYmlsaXR5IGxhZGRlciAocGFyaXR5IHdpdGhcbiAqIGRlbGV0ZS9yZXN0b3JlKSwgdGhlbiBwZXJtYW5lbnRseSBoYXJkLWRlbGV0ZSBpdCDigJQgYnV0IE9OTFkgd2hlbiBpdCBpcyBhXG4gKiBnZW51aW5lIHRvbWJzdG9uZSAoYSBsaXZlL2FjdGl2ZSByb3cgbG9hZHMgYXMgYG51bGxgIGhlcmUgYW5kIDQwNHMsIHNvIHB1cmdlXG4gKiBjYW4gbmV2ZXIgaGFyZC1kZWxldGUgYSBsaXZlIHJvdykuIFJldHVybnMgYHsgZXhpc3RpbmcsIHB1cmdlZElkIH1gIG9uIHN1Y2Nlc3NcbiAqICh0aGUgY2FsbGVyIGVtaXRzIHRoZSBlbnRpdHktc3BlY2lmaWMgYXVkaXQgb2ZmIGBleGlzdGluZ2ApLCBvciBgbnVsbGAgd2hlbiBpdFxuICogaGFzIEFMUkVBRFkgc2VudCBhIHJlc3BvbnNlICg0MDAvNDA0LzQwMykuIFN0ZXAtdXAgaXMgdGhlIFJPVVRFJ3Mgam9iOiB0aGVcbiAqIHBpcGVsaW5lLCB0ZW1wbGF0ZSBhbmQgcGx1Z2luIHNlcnZpY2VzIG1vdW50IHB1cmdlIGJlaGluZCBgcmVxdWlyZVN0ZXBVcGBcbiAqIChzaGFyaW5nIHJlc3RvcmUncyBjaGFpbiBzbyBvbmUgdG9rZW4gaXNuJ3QgY29uc3VtZWQgdHdpY2UpLiBEUllzIHRoZSBuZWFyLWlkZW50aWNhbFxuICogcGlwZWxpbmUgLyBwaXBlbGluZV90ZW1wbGF0ZSBwdXJnZSByb3V0ZXMuXG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBsb2FkQW5kUHVyZ2U8VCBleHRlbmRzIFJlc3RvcmFibGVFbnRpdHk+KFxuICByZXE6IFJlcXVlc3QsXG4gIHJlczogUmVzcG9uc2UsXG4gIG9yZ0lkOiBzdHJpbmcsXG4gIHNlcnZpY2U6IFB1cmdlYWJsZVNlcnZpY2U8VD4sXG4gIGxhYmVsOiBzdHJpbmcsXG4gIHB1Ymxpc2hQZXJtaXNzaW9uOiBQZXJtaXNzaW9uLFxuICB1c2VySWQ6IHN0cmluZyxcbik6IFByb21pc2U8eyBleGlzdGluZzogVDsgcHVyZ2VkSWQ6IHN0cmluZyB9IHwgbnVsbD4ge1xuICBjb25zdCBpZCA9IGdldFBhcmFtKHJlcS5wYXJhbXMsICdpZCcpO1xuICBpZiAoIWlkKSB7XG4gICAgc2VuZEJhZFJlcXVlc3QocmVzLCBgJHtsYWJlbH0gSUQgaXMgcmVxdWlyZWQuYCwgRXJyb3JDb2RlLk1JU1NJTkdfUkVRVUlSRURfRklFTEQpO1xuICAgIHJldHVybiBudWxsO1xuICB9XG5cbiAgLy8gTG9hZCB0aGUgVE9NQlNUT05FIChvd24tb3JnKS4gQSBsaXZlIHJvdyAvIHVua25vd24gaWQgLyBhbHJlYWR5LXB1cmdlZCDihpIgNDA0LFxuICAvLyBzbyBhIGN1cnJlbnRseS1hY3RpdmUgZW50aXR5IGlzIG5ldmVyIGhhcmQtZGVsZXRlZCB0aHJvdWdoIHRoaXMgcGF0aC5cbiAgY29uc3QgZXhpc3RpbmcgPSBhd2FpdCBzZXJ2aWNlLmZpbmREZWxldGVkQnlJZChpZCwgb3JnSWQpO1xuICBpZiAoIWV4aXN0aW5nKSB7XG4gICAgc2VuZEVudGl0eU5vdEZvdW5kKHJlcywgbGFiZWwpO1xuICAgIHJldHVybiBudWxsO1xuICB9XG5cbiAgLy8gU2FtZSBsYWRkZXIgYXV0aG9yaXR5IGFzIGRlbGV0ZS9yZXN0b3JlIChzZWUgbG9hZEFuZFJlc3RvcmUpLlxuICBpZiAoIXJlcXVpcmVWaXNpYmlsaXR5V3JpdGVBY2Nlc3MocmVxLCByZXMsIGV4aXN0aW5nLCB1c2VySWQgfHwgJycsIHB1Ymxpc2hQZXJtaXNzaW9uKSkgcmV0dXJuIG51bGw7XG5cbiAgLy8gUmV1c2UgdGhlIHJldGVudGlvbiBzd2VlcCdzIHNpbmdsZS1pZCBoYXJkLWRlbGV0ZSAoc2FtZSB0b21ic3RvbmUgbWF0Y2ggK1xuICAvLyBkZXBlbmRlbnQtdGVhcmRvd24gaG9va3MpLiBSZXR1cm5zIG51bGwgb24gYSByYWNlIChhbHJlYWR5IHB1cmdlZCkg4oaSIDQwNC5cbiAgY29uc3QgcHVyZ2VkSWQgPSBhd2FpdCBzZXJ2aWNlLnB1cmdlQnlJZChpZCwgb3JnSWQpO1xuICBpZiAoIXB1cmdlZElkKSB7XG4gICAgc2VuZEVudGl0eU5vdEZvdW5kKHJlcywgbGFiZWwpO1xuICAgIHJldHVybiBudWxsO1xuICB9XG5cbiAgcmV0dXJuIHsgZXhpc3RpbmcsIHB1cmdlZElkIH07XG59XG4iXX0=
|
|
@@ -9,8 +9,15 @@ export declare function writeSseHeaders(res: Response): void;
|
|
|
9
9
|
/**
|
|
10
10
|
* Set SSE response headers and flush.
|
|
11
11
|
* Returns an `aborted()` function to check if the client disconnected.
|
|
12
|
+
*
|
|
13
|
+
* Disconnect is detected on the RESPONSE: `res` emits 'close' when the
|
|
14
|
+
* underlying connection goes away, and `writableFinished` tells an early close
|
|
15
|
+
* (client gone) from the normal close after `res.end()`. The request's own
|
|
16
|
+
* 'close' is not a disconnect signal — on current Node it fires once the request
|
|
17
|
+
* BODY has been consumed, which for a POST stream happens before this listener
|
|
18
|
+
* is attached, so a disconnect was never observed.
|
|
12
19
|
*/
|
|
13
|
-
export declare function initSSEStream(
|
|
20
|
+
export declare function initSSEStream(_req: Request, res: Response, timeoutMs: number): {
|
|
14
21
|
aborted: () => boolean;
|
|
15
22
|
};
|
|
16
23
|
/**
|
|
@@ -16,13 +16,21 @@ export function writeSseHeaders(res) {
|
|
|
16
16
|
/**
|
|
17
17
|
* Set SSE response headers and flush.
|
|
18
18
|
* Returns an `aborted()` function to check if the client disconnected.
|
|
19
|
+
*
|
|
20
|
+
* Disconnect is detected on the RESPONSE: `res` emits 'close' when the
|
|
21
|
+
* underlying connection goes away, and `writableFinished` tells an early close
|
|
22
|
+
* (client gone) from the normal close after `res.end()`. The request's own
|
|
23
|
+
* 'close' is not a disconnect signal — on current Node it fires once the request
|
|
24
|
+
* BODY has been consumed, which for a POST stream happens before this listener
|
|
25
|
+
* is attached, so a disconnect was never observed.
|
|
19
26
|
*/
|
|
20
|
-
export function initSSEStream(
|
|
27
|
+
export function initSSEStream(_req, res, timeoutMs) {
|
|
21
28
|
writeSseHeaders(res);
|
|
22
29
|
res.setTimeout(timeoutMs);
|
|
23
30
|
res.flushHeaders();
|
|
24
31
|
let _aborted = false;
|
|
25
|
-
|
|
32
|
+
res.on('close', () => { if (!res.writableFinished)
|
|
33
|
+
_aborted = true; });
|
|
26
34
|
return { aborted: () => _aborted };
|
|
27
35
|
}
|
|
28
36
|
/**
|
|
@@ -49,4 +57,4 @@ export function handleAIError(res, message, fallbackMessage) {
|
|
|
49
57
|
res.write(`data: ${JSON.stringify({ type: 'error', message })}\n\n`);
|
|
50
58
|
res.end();
|
|
51
59
|
}
|
|
52
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
60
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3NlLWhlbHBlcnMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvaGVscGVycy9zc2UtaGVscGVycy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSwrQ0FBK0M7QUFDL0Msc0NBQXNDO0FBR3RDLE9BQU8sRUFBRSxjQUFjLEVBQUUsaUJBQWlCLEVBQUUsTUFBTSxzQkFBc0IsQ0FBQztBQUV6RTs7Ozs7R0FLRztBQUNILE1BQU0sVUFBVSxlQUFlLENBQUMsR0FBYTtJQUMzQyxHQUFHLENBQUMsU0FBUyxDQUFDLGNBQWMsRUFBRSxtQkFBbUIsQ0FBQyxDQUFDO0lBQ25ELEdBQUcsQ0FBQyxTQUFTLENBQUMsZUFBZSxFQUFFLFVBQVUsQ0FBQyxDQUFDO0lBQzNDLEdBQUcsQ0FBQyxTQUFTLENBQUMsWUFBWSxFQUFFLFlBQVksQ0FBQyxDQUFDO0lBQzFDLEdBQUcsQ0FBQyxTQUFTLENBQUMsbUJBQW1CLEVBQUUsSUFBSSxDQUFDLENBQUM7QUFDM0MsQ0FBQztBQUVEOzs7Ozs7Ozs7O0dBVUc7QUFDSCxNQUFNLFVBQVUsYUFBYSxDQUFDLElBQWEsRUFBRSxHQUFhLEVBQUUsU0FBaUI7SUFDM0UsZUFBZSxDQUFDLEdBQUcsQ0FBQyxDQUFDO0lBQ3JCLEdBQUcsQ0FBQyxVQUFVLENBQUMsU0FBUyxDQUFDLENBQUM7SUFDMUIsR0FBRyxDQUFDLFlBQVksRUFBRSxDQUFDO0lBQ25CLElBQUksUUFBUSxHQUFHLEtBQUssQ0FBQztJQUNyQixHQUFHLENBQUMsRUFBRSxDQUFDLE9BQU8sRUFBRSxHQUFHLEVBQUUsR0FBRyxJQUFJLENBQUMsR0FBRyxDQUFDLGdCQUFnQjtRQUFFLFFBQVEsR0FBRyxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUN2RSxPQUFPLEVBQUUsT0FBTyxFQUFFLEdBQUcsRUFBRSxDQUFDLFFBQVEsRUFBRSxDQUFDO0FBQ3JDLENBQUM7QUFFRDs7O0dBR0c7QUFDSCxNQUFNLFVBQVUsYUFBYSxDQUFDLEdBQWEsRUFBRSxPQUFlLEVBQUUsZUFBdUI7SUFDbkYsSUFBSSxDQUFDLEdBQUcsQ0FBQyxXQUFXLEVBQUUsQ0FBQztRQUNyQixJQUFJLE9BQU8sQ0FBQyxRQUFRLENBQUMsZ0JBQWdCLENBQUMsSUFBSSxPQUFPLENBQUMsUUFBUSxDQUFDLFNBQVMsQ0FBQyxFQUFFLENBQUM7WUFDdEUscUVBQXFFO1lBQ3JFLHdFQUF3RTtZQUN4RSx3RUFBd0U7WUFDeEUsaUVBQWlFO1lBQ2pFLDBFQUEwRTtZQUMxRSx5RUFBeUU7WUFDekUseUVBQXlFO1lBQ3pFLE9BQU8saUJBQWlCLENBQUMsR0FBRyxFQUFFLE9BQU8sQ0FBQyxDQUFDO1FBQ3pDLENBQUM7UUFDRCxJQUFJLE9BQU8sQ0FBQyxRQUFRLENBQUMsNEJBQTRCLENBQUMsRUFBRSxDQUFDO1lBQ25ELE9BQU8sY0FBYyxDQUFDLEdBQUcsRUFBRSxPQUFPLENBQUMsQ0FBQztRQUN0QyxDQUFDO1FBQ0QsT0FBTyxpQkFBaUIsQ0FBQyxHQUFHLEVBQUUsZUFBZSxFQUFFLEVBQUUsT0FBTyxFQUFFLE9BQU8sRUFBRSxDQUFDLENBQUM7SUFDdkUsQ0FBQztJQUNELEdBQUcsQ0FBQyxLQUFLLENBQUMsU0FBUyxJQUFJLENBQUMsU0FBUyxDQUFDLEVBQUUsSUFBSSxFQUFFLE9BQU8sRUFBRSxPQUFPLEVBQUUsQ0FBQyxNQUFNLENBQUMsQ0FBQztJQUNyRSxHQUFHLENBQUMsR0FBRyxFQUFFLENBQUM7QUFDWixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLy8gQ29weXJpZ2h0IDIwMjYgUGlwZWxpbmUgQnVpbGRlciBDb250cmlidXRvcnNcbi8vIFNQRFgtTGljZW5zZS1JZGVudGlmaWVyOiBBcGFjaGUtMi4wXG5cbmltcG9ydCB0eXBlIHsgUmVxdWVzdCwgUmVzcG9uc2UgfSBmcm9tICdleHByZXNzJztcbmltcG9ydCB7IHNlbmRCYWRSZXF1ZXN0LCBzZW5kSW50ZXJuYWxFcnJvciB9IGZyb20gJy4uL3V0aWxzL3Jlc3BvbnNlLmpzJztcblxuLyoqXG4gKiBTZXQgdGhlIGZvdXIgc3RhbmRhcmQgU1NFIHJlc3BvbnNlIGhlYWRlcnMgKGRvZXMgTk9UIGZsdXNoIOKAlCB0aGUgY2FsbGVyIGZsdXNoZXNcbiAqIGFmdGVyIGFueSBhZGRpdGlvbmFsIHBlci1lbmRwb2ludCBzZXR1cCBsaWtlIGByZXMuc2V0VGltZW91dGApLiBUaGUgc2luZ2xlXG4gKiBzb3VyY2Ugb2YgdHJ1dGggZm9yIHRoZSBgdGV4dC9ldmVudC1zdHJlYW1gICsgbm8tY2FjaGUgKyBrZWVwLWFsaXZlICtcbiAqIGBYLUFjY2VsLUJ1ZmZlcmluZzogbm9gIChkaXNhYmxlIG5naW54IGJ1ZmZlcmluZykgYmxvY2suXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiB3cml0ZVNzZUhlYWRlcnMocmVzOiBSZXNwb25zZSk6IHZvaWQge1xuICByZXMuc2V0SGVhZGVyKCdDb250ZW50LVR5cGUnLCAndGV4dC9ldmVudC1zdHJlYW0nKTtcbiAgcmVzLnNldEhlYWRlcignQ2FjaGUtQ29udHJvbCcsICduby1jYWNoZScpO1xuICByZXMuc2V0SGVhZGVyKCdDb25uZWN0aW9uJywgJ2tlZXAtYWxpdmUnKTtcbiAgcmVzLnNldEhlYWRlcignWC1BY2NlbC1CdWZmZXJpbmcnLCAnbm8nKTtcbn1cblxuLyoqXG4gKiBTZXQgU1NFIHJlc3BvbnNlIGhlYWRlcnMgYW5kIGZsdXNoLlxuICogUmV0dXJucyBhbiBgYWJvcnRlZCgpYCBmdW5jdGlvbiB0byBjaGVjayBpZiB0aGUgY2xpZW50IGRpc2Nvbm5lY3RlZC5cbiAqXG4gKiBEaXNjb25uZWN0IGlzIGRldGVjdGVkIG9uIHRoZSBSRVNQT05TRTogYHJlc2AgZW1pdHMgJ2Nsb3NlJyB3aGVuIHRoZVxuICogdW5kZXJseWluZyBjb25uZWN0aW9uIGdvZXMgYXdheSwgYW5kIGB3cml0YWJsZUZpbmlzaGVkYCB0ZWxscyBhbiBlYXJseSBjbG9zZVxuICogKGNsaWVudCBnb25lKSBmcm9tIHRoZSBub3JtYWwgY2xvc2UgYWZ0ZXIgYHJlcy5lbmQoKWAuIFRoZSByZXF1ZXN0J3Mgb3duXG4gKiAnY2xvc2UnIGlzIG5vdCBhIGRpc2Nvbm5lY3Qgc2lnbmFsIOKAlCBvbiBjdXJyZW50IE5vZGUgaXQgZmlyZXMgb25jZSB0aGUgcmVxdWVzdFxuICogQk9EWSBoYXMgYmVlbiBjb25zdW1lZCwgd2hpY2ggZm9yIGEgUE9TVCBzdHJlYW0gaGFwcGVucyBiZWZvcmUgdGhpcyBsaXN0ZW5lclxuICogaXMgYXR0YWNoZWQsIHNvIGEgZGlzY29ubmVjdCB3YXMgbmV2ZXIgb2JzZXJ2ZWQuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBpbml0U1NFU3RyZWFtKF9yZXE6IFJlcXVlc3QsIHJlczogUmVzcG9uc2UsIHRpbWVvdXRNczogbnVtYmVyKTogeyBhYm9ydGVkOiAoKSA9PiBib29sZWFuIH0ge1xuICB3cml0ZVNzZUhlYWRlcnMocmVzKTtcbiAgcmVzLnNldFRpbWVvdXQodGltZW91dE1zKTtcbiAgcmVzLmZsdXNoSGVhZGVycygpO1xuICBsZXQgX2Fib3J0ZWQgPSBmYWxzZTtcbiAgcmVzLm9uKCdjbG9zZScsICgpID0+IHsgaWYgKCFyZXMud3JpdGFibGVGaW5pc2hlZCkgX2Fib3J0ZWQgPSB0cnVlOyB9KTtcbiAgcmV0dXJuIHsgYWJvcnRlZDogKCkgPT4gX2Fib3J0ZWQgfTtcbn1cblxuLyoqXG4gKiBDbGFzc2lmeSBBSSBnZW5lcmF0aW9uIGVycm9ycyBhbmQgc2VuZCB0aGUgYXBwcm9wcmlhdGUgSFRUUCByZXNwb25zZSBvciBTU0UgZXZlbnQuXG4gKiBJZiBoZWFkZXJzIGFyZSBhbHJlYWR5IHNlbnQgKHN0cmVhbWluZyksIHdyaXRlcyBhbiBTU0UgZXJyb3IgZXZlbnQgYW5kIGVuZHMgdGhlIHJlc3BvbnNlLlxuICovXG5leHBvcnQgZnVuY3Rpb24gaGFuZGxlQUlFcnJvcihyZXM6IFJlc3BvbnNlLCBtZXNzYWdlOiBzdHJpbmcsIGZhbGxiYWNrTWVzc2FnZTogc3RyaW5nKTogdm9pZCB7XG4gIGlmICghcmVzLmhlYWRlcnNTZW50KSB7XG4gICAgaWYgKG1lc3NhZ2UuaW5jbHVkZXMoJ25vdCBjb25maWd1cmVkJykgfHwgbWVzc2FnZS5pbmNsdWRlcygnQVBJIGtleScpKSB7XG4gICAgICAvLyBTdXJmYWNlIHRoZSBPUklHSU5BTCBtZXNzYWdlLiBJdCBuYW1lcyB0aGUgTUlTU0lORyBDT05GSUdVUkFUSU9OIOKAlFxuICAgICAgLy8gXCJubyBwcm92aWRlciBBUEkga2V5IGlzIHNldCBhbmQgT1BFTkFJX0NPTVBBVElCTEVfQkFTRV9VUkwgaXMgdW5zZXRcIixcbiAgICAgIC8vIG9yIHdoaWNoIGVudiB2YXIgYSBuYW1lZCBwcm92aWRlciB3YW50cyDigJQgbmV2ZXIgYSBzZWNyZXQgVkFMVUUsIHNvIGl0XG4gICAgICAvLyBpcyBzYWZlIHRvIHNob3cgYW5kIGl0IGlzIHRoZSBvbmx5IHRoaW5nIHRoYXQgbWFrZXMgdGhpcyBlcnJvclxuICAgICAgLy8gYWN0aW9uYWJsZS4gVGhlIGdlbmVyaWMgcmVwbGFjZW1lbnQgYWxzbyBtaXMtZGVzY3JpYmVkIHRoZSBjb21tb24gY2FzZTpcbiAgICAgIC8vIGl0IHNhaWQgXCJmb3IgdGhlIHJlcXVlc3RlZCBwcm92aWRlclwiIHdoZW4gbm8gcHJvdmlkZXIgd2FzIHJlcXVlc3RlZCBhdFxuICAgICAgLy8gYWxsLCBwb2ludGluZyBvcGVyYXRvcnMgYXQgYSBwcm92aWRlciBidWcgaW5zdGVhZCBvZiBhdCBjb25maWd1cmF0aW9uLlxuICAgICAgcmV0dXJuIHNlbmRJbnRlcm5hbEVycm9yKHJlcywgbWVzc2FnZSk7XG4gICAgfVxuICAgIGlmIChtZXNzYWdlLmluY2x1ZGVzKCdub3QgYXZhaWxhYmxlIGZvciBwcm92aWRlcicpKSB7XG4gICAgICByZXR1cm4gc2VuZEJhZFJlcXVlc3QocmVzLCBtZXNzYWdlKTtcbiAgICB9XG4gICAgcmV0dXJuIHNlbmRJbnRlcm5hbEVycm9yKHJlcywgZmFsbGJhY2tNZXNzYWdlLCB7IGRldGFpbHM6IG1lc3NhZ2UgfSk7XG4gIH1cbiAgcmVzLndyaXRlKGBkYXRhOiAke0pTT04uc3RyaW5naWZ5KHsgdHlwZTogJ2Vycm9yJywgbWVzc2FnZSB9KX1cXG5cXG5gKTtcbiAgcmVzLmVuZCgpO1xufVxuIl19
|
package/lib/middleware/auth.d.ts
CHANGED
|
@@ -1,44 +1,41 @@
|
|
|
1
1
|
import type { Request, Response, NextFunction } from 'express';
|
|
2
|
-
import
|
|
3
|
-
import type { JwtPayload } from '../types/common.js';
|
|
2
|
+
import { type AssuranceLevel, type JwtPayload } from '../types/common.js';
|
|
4
3
|
import { type Permission } from '../types/permissions.js';
|
|
5
4
|
/**
|
|
6
|
-
*
|
|
5
|
+
* Verify a token that speaks for a PERSON — access, step-up, and the token an
|
|
6
|
+
* opaque access key is exchanged for — against platform's published signing
|
|
7
|
+
* keys.
|
|
7
8
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
|
|
13
|
-
export declare function _resetJwtSecretCacheForTests(): void;
|
|
14
|
-
/**
|
|
15
|
-
* The JWT verify options every token check in the fleet must share: pinned
|
|
16
|
-
* algorithm (blocks alg-confusion / `alg:none`) plus issuer/audience when the
|
|
17
|
-
* deployment configures them.
|
|
9
|
+
* ES256 and a `kid` are both mandatory: no `kid` means no way to pick the right
|
|
10
|
+
* key across a rotation, and any other algorithm is refused outright, which is
|
|
11
|
+
* what makes "no service accepts an HS256 user token" a property of the code
|
|
12
|
+
* rather than a convention. Key lookup goes through the shared JWKS cache
|
|
13
|
+
* (10-minute refresh, one refetch on an unknown `kid`, brief negative cache).
|
|
18
14
|
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
* system sharing `JWT_SECRET` was accepted.
|
|
15
|
+
* @throws {UnknownKidError} the key set is current and has no such key → 401.
|
|
16
|
+
* @throws {JwksUnavailableError} the key set could not be obtained → 503; an
|
|
17
|
+
* unverifiable token is never treated as valid.
|
|
23
18
|
*/
|
|
24
|
-
export declare function
|
|
19
|
+
export declare function verifyUserJwt<T = JwtPayload>(token: string): Promise<T>;
|
|
25
20
|
/**
|
|
26
|
-
* Verify
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
21
|
+
* Verify ANY bearer token and return its claims.
|
|
22
|
+
*
|
|
23
|
+
* Since #14 there is no shared secret left: BOTH chains are ES256 with a `kid`,
|
|
24
|
+
* so the dispatch is by **who owns the `kid`** rather than by algorithm — and
|
|
25
|
+
* because a `kid` is an RFC 7638 thumbprint, ownership is a fact about the key,
|
|
26
|
+
* not a claim the token makes about itself:
|
|
30
27
|
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
28
|
+
* - a `kid` published in the per-service key bundle → an INTERNAL SERVICE token
|
|
29
|
+
* ({@link signServiceToken}), verified with that service's key; its `sub` must
|
|
30
|
+
* name the same service, so one service can never speak for another.
|
|
31
|
+
* - anything else → a platform-signed USER token, verified against platform's
|
|
32
|
+
* published JWKS. A token claiming `principalType: 'service'` is refused
|
|
33
|
+
* here: an internal identity may not ride the user signing key.
|
|
34
|
+
*
|
|
35
|
+
* Anything that is not ES256 with a `kid` fails in both chains, which is what
|
|
36
|
+
* makes "no HS256 token is accepted anywhere" a property of the code.
|
|
40
37
|
*/
|
|
41
|
-
export declare function
|
|
38
|
+
export declare function verifyBearerToken(token: string): Promise<JwtPayload>;
|
|
42
39
|
export interface RequireAuthOptions {
|
|
43
40
|
/**
|
|
44
41
|
* Allow x-org-id/x-org-name headers to override the JWT's organization fields.
|
|
@@ -50,6 +47,35 @@ export interface RequireAuthOptions {
|
|
|
50
47
|
* (e.g. a sysadmin managing a given org's billing). If unsure, leave it disabled.
|
|
51
48
|
*/
|
|
52
49
|
allowOrgHeaderOverride?: boolean;
|
|
50
|
+
/**
|
|
51
|
+
* Demand that the whole SESSION be at least this assurance level (#8).
|
|
52
|
+
*
|
|
53
|
+
* `minAssurance: 2` means the person authenticated with a second factor — a
|
|
54
|
+
* passkey with user verification, a password plus an authenticator code, or
|
|
55
|
+
* SSO through an IdP the org marked as enforcing MFA. A weaker token gets
|
|
56
|
+
* **401 `MFA_REQUIRED`**, which the client answers by sending the person to
|
|
57
|
+
* enrolment or to a stronger sign-in; refreshing cannot help, because `aal`
|
|
58
|
+
* lives on the session slot and a refresh never raises it.
|
|
59
|
+
*
|
|
60
|
+
* This is the SESSION-level counterpart to {@link requireStepUp}, which stays
|
|
61
|
+
* a fresh, per-action confirmation. Use both on the most dangerous routes.
|
|
62
|
+
*
|
|
63
|
+
* MACHINES NEVER SATISFY IT. A service principal, an org service account and
|
|
64
|
+
* an exchanged access key all get **403 `HUMAN_SESSION_REQUIRED`** — there is
|
|
65
|
+
* no person behind them to have presented a factor, so "assurance" is not a
|
|
66
|
+
* question their credential can answer. That is deliberately not a silent
|
|
67
|
+
* pass-through for internal services: a route that demands MFA is a route a
|
|
68
|
+
* human is doing something dangerous on.
|
|
69
|
+
*/
|
|
70
|
+
minAssurance?: AssuranceLevel;
|
|
71
|
+
/**
|
|
72
|
+
* With {@link minAssurance}, additionally bound how long ago that sign-in
|
|
73
|
+
* happened (seconds, compared against the token's `auth_time`). A session that
|
|
74
|
+
* is MFA-grade but older than this gets **401 `REAUTH_REQUIRED`**. Ignored
|
|
75
|
+
* without `minAssurance` — "how recent" is only meaningful once "how strong"
|
|
76
|
+
* is being asked.
|
|
77
|
+
*/
|
|
78
|
+
maxAge?: number;
|
|
53
79
|
}
|
|
54
80
|
/** JWT auth middleware. Use directly or call with options. */
|
|
55
81
|
export declare function requireAuth(req: Request, res: Response, next: NextFunction): void;
|
|
@@ -106,6 +132,42 @@ export declare function isAccessTokenRevoked(claims: {
|
|
|
106
132
|
jti?: string;
|
|
107
133
|
impersonatorId?: string;
|
|
108
134
|
}): Promise<boolean>;
|
|
135
|
+
/**
|
|
136
|
+
* Whether a set of verified claims speaks for a PERSON's own session — the only
|
|
137
|
+
* kind of credential an assurance level can be asserted about.
|
|
138
|
+
*
|
|
139
|
+
* Excludes all three machine shapes: an internal service principal, an org
|
|
140
|
+
* service account, and any exchanged access key (`token_use: 'api_key'`, i.e. a
|
|
141
|
+
* PAT or a service-account key), each of which authenticates a program holding a
|
|
142
|
+
* secret rather than a person presenting factors.
|
|
143
|
+
*/
|
|
144
|
+
export declare function isHumanPrincipal(claims: Pick<JwtPayload, 'principalType' | 'token_use'> | undefined): boolean;
|
|
145
|
+
/** What an assurance gate demands. */
|
|
146
|
+
export interface AssuranceOptions {
|
|
147
|
+
minAssurance: AssuranceLevel;
|
|
148
|
+
maxAge?: number;
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Standalone assurance gate, for a service whose own `requireAuth` is not
|
|
152
|
+
* api-core's (platform's is its own, because it reads Mongo). Compose it AFTER
|
|
153
|
+
* authentication: `router.get('/x', requireAuth, requireAssurance({ minAssurance: 2 }), h)`.
|
|
154
|
+
*
|
|
155
|
+
* Identical rules and identical refusals to `requireAuth({ minAssurance })` —
|
|
156
|
+
* that option simply calls the same check inline, so there is one implementation
|
|
157
|
+
* of "is this session MFA-grade" in the codebase.
|
|
158
|
+
*/
|
|
159
|
+
export declare function requireAssurance(options: AssuranceOptions): (req: Request, res: Response, next: NextFunction) => void;
|
|
160
|
+
/**
|
|
161
|
+
* Whether a verified token carries a well-formed identity: a known
|
|
162
|
+
* `principalType` and `token_use`, and the claims that kind of principal must
|
|
163
|
+
* have. Fails closed — a token minted before these claims existed, or one with a
|
|
164
|
+
* contradictory combination, is not an identity any gate should reason about.
|
|
165
|
+
*
|
|
166
|
+
* - `service`: `token_use: 'access'` and a `service:<name>` subject (the name
|
|
167
|
+
* feeds the denylist and audit attribution).
|
|
168
|
+
* - `user` / `service_account`: `amr` (known methods), `aal` and `auth_time`.
|
|
169
|
+
*/
|
|
170
|
+
export declare function hasValidIdentityClaims(decoded: Partial<JwtPayload>): boolean;
|
|
109
171
|
/**
|
|
110
172
|
* Whether the request's user holds `permission`. Superadmins implicitly hold
|
|
111
173
|
* every permission. Reads the resolved `permissions` claim (set at token issue,
|
|
@@ -134,6 +196,16 @@ export interface AuthzDenialInfo {
|
|
|
134
196
|
* auditor. Idempotent; the last registration wins.
|
|
135
197
|
*/
|
|
136
198
|
export declare function setAuthzDenialAuditor(fn: ((info: AuthzDenialInfo) => void) | undefined): void;
|
|
199
|
+
/**
|
|
200
|
+
* Emit a denial event, best-effort. Only fires for state-changing (non-GET,
|
|
201
|
+
* non-HEAD/OPTIONS) requests — a rejected GET is low-signal probing noise and
|
|
202
|
+
* would amplify audit volume under a scan. Never throws.
|
|
203
|
+
*
|
|
204
|
+
* The permission gates here call it themselves. Export it for data-driven checks
|
|
205
|
+
* a route makes inline (cross-org access, service-only endpoints), so those 403s
|
|
206
|
+
* reach the same `authz.denied` trail. `required` names what was missing.
|
|
207
|
+
*/
|
|
208
|
+
export declare function recordAuthzDenial(req: Request, required: string): void;
|
|
137
209
|
/**
|
|
138
210
|
* Requires that the user hold AT LEAST ONE of the given permissions (or be a
|
|
139
211
|
* superadmin). Use after requireAuth. Mirrors `requireRole`'s any-of semantics;
|
|
@@ -152,7 +224,7 @@ export declare function requirePermission(...permissions: Permission[]): (req: R
|
|
|
152
224
|
export declare function requireAllPermissions(...permissions: Permission[]): (req: Request, res: Response, next: NextFunction) => void;
|
|
153
225
|
/**
|
|
154
226
|
* Like {@link requirePermission} (any-of) but ALSO admits an internal service
|
|
155
|
-
* principal (
|
|
227
|
+
* principal (`principalType: 'service'`). For READ endpoints that BOTH
|
|
156
228
|
* interactive users — who must hold one of the `:read` capabilities — AND
|
|
157
229
|
* service-to-service callers legitimately hit; service tokens carry no
|
|
158
230
|
* permission claims (they're least-privilege `role:member`), so a plain
|
|
@@ -163,13 +235,41 @@ export declare function requireAllPermissions(...permissions: Permission[]): (re
|
|
|
163
235
|
*/
|
|
164
236
|
export declare function requirePermissionOrService(...permissions: Permission[]): (req: Request, res: Response, next: NextFunction) => void;
|
|
165
237
|
/**
|
|
166
|
-
* Gate an endpoint to internal service-to-service callers only (a
|
|
167
|
-
*
|
|
238
|
+
* Gate an endpoint to internal service-to-service callers only (a
|
|
239
|
+
* `principalType: 'service'` principal minted via {@link signServiceToken} / `getServiceAuthHeader`).
|
|
168
240
|
* Rejects any user token. For NON-user-facing endpoints — entity-event ingest,
|
|
169
241
|
* auto-subscribe, and similar peer-service hooks — that a browser must never
|
|
170
242
|
* reach. Compose after `requireAuth` so `req.user` is populated.
|
|
171
243
|
*/
|
|
172
244
|
export declare function requireServicePrincipal(req: Request, res: Response, next: NextFunction): void;
|
|
245
|
+
/**
|
|
246
|
+
* THE gate for an INTERNAL route (#14) — the one every `/internal/*` path, the
|
|
247
|
+
* quota usage counters, the entity-event ingest and the audit ingest go through.
|
|
248
|
+
*
|
|
249
|
+
* Two rules, both fail-closed:
|
|
250
|
+
*
|
|
251
|
+
* 1. **No user token, ever.** Not a member's, not a superadmin's, not a
|
|
252
|
+
* service-account key's. An internal route is a peer-service API surface, so
|
|
253
|
+
* "a sufficiently privileged human" is not an acceptable caller — that
|
|
254
|
+
* equivalence is exactly how a browser-reachable path becomes a tenant
|
|
255
|
+
* boundary bug. `requireServicePrincipal` already refused users; this also
|
|
256
|
+
* closes the `|| isSystemAdmin(req)` escape hatches that had grown around
|
|
257
|
+
* the individual internal routes.
|
|
258
|
+
* 2. **Only the NAMED callers.** `callers` lists the services that legitimately
|
|
259
|
+
* call this route, and the name is cryptographically bound to the signing
|
|
260
|
+
* key (`services/service-keys.ts`), so this is an identity check rather than
|
|
261
|
+
* a claim check. It is the same allow-list the mesh policy names, at the
|
|
262
|
+
* layer that also holds in docker compose, where there is no mesh at all —
|
|
263
|
+
* the Istio `AuthorizationPolicy` is defence in depth on top, never the
|
|
264
|
+
* enforcement.
|
|
265
|
+
*
|
|
266
|
+
* Compose after `requireAuth` so `req.user` is populated. Refusals are counted
|
|
267
|
+
* (`internal_route_refused_total`) and audited through the shared `authz.denied`
|
|
268
|
+
* sink.
|
|
269
|
+
*/
|
|
270
|
+
export declare function requireInternalService(options: {
|
|
271
|
+
callers: readonly string[];
|
|
272
|
+
}): (req: Request, res: Response, next: NextFunction) => void;
|
|
173
273
|
/**
|
|
174
274
|
* The system tenant's canonical org **id** — a fixed, well-known ObjectId (NOT
|
|
175
275
|
* the string 'system'). This is the single knob every service resolves the
|
|
@@ -214,10 +314,19 @@ export declare function requireSystemAdmin(req: Request, res: Response, next: Ne
|
|
|
214
314
|
* Sysadmins (isSuperAdmin) bypass — they always have every feature.
|
|
215
315
|
*/
|
|
216
316
|
export declare function requireFeature(feature: string): (req: Request, res: Response, next: NextFunction) => void;
|
|
217
|
-
/**
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
317
|
+
/**
|
|
318
|
+
* The calling service's name for a SERVICE principal (`principalType: 'service'`),
|
|
319
|
+
* else `undefined`. A user token never yields a name, whatever its `sub` says.
|
|
320
|
+
*/
|
|
321
|
+
export declare function serviceNameOf(claims: {
|
|
322
|
+
principalType?: string;
|
|
323
|
+
sub?: string;
|
|
324
|
+
} | undefined): string | undefined;
|
|
325
|
+
/** True when the claims are a service principal whose service is on the denylist. */
|
|
326
|
+
export declare function isServiceTokenDenied(claims: {
|
|
327
|
+
principalType?: string;
|
|
328
|
+
sub?: string;
|
|
329
|
+
}): boolean;
|
|
221
330
|
export interface ServiceTokenOptions {
|
|
222
331
|
/** Calling service identifier (e.g. 'billing', 'platform'). Embedded as `sub: service:<name>`. */
|
|
223
332
|
serviceName: string;
|
|
@@ -248,19 +357,41 @@ export interface ServiceTokenOptions {
|
|
|
248
357
|
* Mint a JWT identifying the calling service. Used for inter-service HTTP calls.
|
|
249
358
|
* The token satisfies `requireAuth` and (when orgId is present) `requireOrganization`.
|
|
250
359
|
* Scope it with `opts.role` — least privilege keeps a leaked token low-value.
|
|
360
|
+
*
|
|
361
|
+
* `opts.serviceName` must be THIS process's own identity (`SERVICE_NAME`): a
|
|
362
|
+
* service holds only its own signing key, so minting a token that names another
|
|
363
|
+
* service throws rather than producing one every peer would reject. That is the
|
|
364
|
+
* one behaviour change #14 forces on callers — see `services/service-keys.ts`.
|
|
251
365
|
*/
|
|
252
366
|
export declare function signServiceToken(opts: ServiceTokenOptions): string;
|
|
253
367
|
/** Convenience: returns a `Bearer <token>` header value for fetch/axios calls. */
|
|
254
368
|
export declare function getServiceAuthHeader(opts: ServiceTokenOptions): string;
|
|
255
|
-
/** True when `req.user
|
|
369
|
+
/** True when `req.user` is a service principal (`principalType: 'service'`, minted by `signServiceToken`). */
|
|
256
370
|
export declare function isServicePrincipal(req: Request): boolean;
|
|
371
|
+
/**
|
|
372
|
+
* True when `req.user` is an ORG SERVICE ACCOUNT (`principalType:
|
|
373
|
+
* 'service_account'`) — a non-human principal owned by one org, authenticating
|
|
374
|
+
* with a `pb_sa_…` key exchanged at platform.
|
|
375
|
+
*
|
|
376
|
+
* A service account is NOT an internal service principal: it holds org Roles and
|
|
377
|
+
* is subject to every permission gate a member is. What it can never do is
|
|
378
|
+
* satisfy a HUMAN-presence requirement — step-up (and any later assurance gate)
|
|
379
|
+
* refuses it outright, because there is no person behind it to re-verify.
|
|
380
|
+
*/
|
|
381
|
+
export declare function isServiceAccountPrincipal(req: Request): boolean;
|
|
382
|
+
/** True when `req.user` is the named internal service (e.g. `'billing'`). */
|
|
383
|
+
export declare function isServicePrincipalNamed(req: Request, serviceName: string): boolean;
|
|
257
384
|
/**
|
|
258
385
|
* PRE-auth check: cryptographically verify the request carries a valid, signed
|
|
259
|
-
* SERVICE token (`
|
|
386
|
+
* SERVICE token (`principalType: 'service'`). Unlike {@link isServicePrincipal}
|
|
260
387
|
* (which reads the already-populated `req.user`), this verifies the bearer token
|
|
261
388
|
* itself, so it is safe to call BEFORE `requireAuth` runs — e.g. the global rate
|
|
262
389
|
* limiter's `skip`, which must not trust the spoofable `x-internal-service`
|
|
263
|
-
* header. Mirrors `requireAuth`'s
|
|
264
|
-
*
|
|
390
|
+
* header. Mirrors `requireAuth`'s SERVICE chain exactly (the signer's own key,
|
|
391
|
+
* resolved by `kid`, with the `sub`/signer agreement check and the optional
|
|
392
|
+
* issuer/audience) and returns `false` on any missing/invalid/non-service token
|
|
393
|
+
* — including any platform-signed USER token, whose `kid` is not in the service
|
|
394
|
+
* bundle at all. Stays SYNCHRONOUS, which is why the per-service public keys are
|
|
395
|
+
* distributed as a mounted bundle rather than fetched over HTTP.
|
|
265
396
|
*/
|
|
266
397
|
export declare function verifyServicePrincipal(req: Request): boolean;
|