@astram/guard 0.2.0 → 0.4.0
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/dist/auth/account.controller.d.ts +245 -0
- package/dist/auth/account.controller.d.ts.map +1 -0
- package/dist/auth/account.controller.js +673 -0
- package/dist/auth/auth.controller.d.ts +219 -5
- package/dist/auth/auth.controller.d.ts.map +1 -1
- package/dist/auth/auth.controller.js +790 -12
- package/dist/auth/auth.module.d.ts +101 -3
- package/dist/auth/auth.module.d.ts.map +1 -1
- package/dist/auth/auth.module.js +300 -8
- package/dist/auth/dto.d.ts +16 -6
- package/dist/auth/dto.d.ts.map +1 -1
- package/dist/auth/ports/audit-sink.d.ts +47 -0
- package/dist/auth/ports/audit-sink.d.ts.map +1 -0
- package/dist/auth/ports/audit-sink.js +20 -0
- package/dist/auth/ports/credential-writer.d.ts +23 -0
- package/dist/auth/ports/credential-writer.d.ts.map +1 -0
- package/dist/auth/ports/credential-writer.js +2 -0
- package/dist/auth/ports/email-sender.d.ts +14 -0
- package/dist/auth/ports/email-sender.d.ts.map +1 -0
- package/dist/auth/ports/email-sender.js +2 -0
- package/dist/auth/ports/mfa-device-store.d.ts +45 -0
- package/dist/auth/ports/mfa-device-store.d.ts.map +1 -0
- package/dist/auth/ports/mfa-device-store.js +13 -0
- package/dist/auth/ports/profile-writer.d.ts +12 -0
- package/dist/auth/ports/profile-writer.d.ts.map +1 -0
- package/dist/auth/ports/profile-writer.js +2 -0
- package/dist/auth/ports/recovery-code-store.d.ts +10 -0
- package/dist/auth/ports/recovery-code-store.d.ts.map +1 -0
- package/dist/auth/ports/recovery-code-store.js +2 -0
- package/dist/auth/ports/reset-token-store.d.ts +12 -0
- package/dist/auth/ports/reset-token-store.d.ts.map +1 -0
- package/dist/auth/ports/reset-token-store.js +2 -0
- package/dist/auth/ports/session-facts-projector.d.ts +44 -0
- package/dist/auth/ports/session-facts-projector.d.ts.map +1 -0
- package/dist/auth/ports/session-facts-projector.js +30 -0
- package/dist/auth/ports/session-sweeper.d.ts +12 -0
- package/dist/auth/ports/session-sweeper.d.ts.map +1 -0
- package/dist/auth/ports/session-sweeper.js +2 -0
- package/dist/auth/profile-resolver.d.ts +73 -0
- package/dist/auth/profile-resolver.d.ts.map +1 -0
- package/dist/auth/profile-resolver.js +28 -0
- package/dist/auth/rate-limit/attempt-limiter.d.ts +91 -0
- package/dist/auth/rate-limit/attempt-limiter.d.ts.map +1 -0
- package/dist/auth/rate-limit/attempt-limiter.js +133 -0
- package/dist/auth/tokens.d.ts +174 -0
- package/dist/auth/tokens.d.ts.map +1 -1
- package/dist/auth/tokens.js +190 -1
- package/dist/auth/totp-enrollment.d.ts +42 -0
- package/dist/auth/totp-enrollment.d.ts.map +1 -0
- package/dist/auth/totp-enrollment.js +96 -0
- package/dist/authentication/mariadb-credential-resolver.d.ts +38 -24
- package/dist/authentication/mariadb-credential-resolver.d.ts.map +1 -1
- package/dist/authentication/mariadb-credential-resolver.js +42 -28
- package/dist/authz/cross-tenant-grant-writer.d.ts +259 -0
- package/dist/authz/cross-tenant-grant-writer.d.ts.map +1 -0
- package/dist/authz/cross-tenant-grant-writer.js +397 -0
- package/dist/authz/manifest-operation-authority-adapter.d.ts +65 -0
- package/dist/authz/manifest-operation-authority-adapter.d.ts.map +1 -0
- package/dist/authz/manifest-operation-authority-adapter.js +108 -0
- package/dist/authz/mariadb-cross-tenant-grant-adapter.d.ts +90 -0
- package/dist/authz/mariadb-cross-tenant-grant-adapter.d.ts.map +1 -0
- package/dist/authz/mariadb-cross-tenant-grant-adapter.js +211 -0
- package/dist/authz/mariadb-record-rule-adapter.d.ts +60 -0
- package/dist/authz/mariadb-record-rule-adapter.d.ts.map +1 -0
- package/dist/authz/mariadb-record-rule-adapter.js +154 -0
- package/dist/authz/mariadb-record-share-adapter.d.ts +75 -0
- package/dist/authz/mariadb-record-share-adapter.d.ts.map +1 -0
- package/dist/authz/mariadb-record-share-adapter.js +106 -0
- package/dist/authz/record-share-cascade.d.ts +52 -0
- package/dist/authz/record-share-cascade.d.ts.map +1 -0
- package/dist/authz/record-share-cascade.js +49 -0
- package/dist/authz/record-share-writer.d.ts +175 -0
- package/dist/authz/record-share-writer.d.ts.map +1 -0
- package/dist/authz/record-share-writer.js +225 -0
- package/dist/authz/wire-record-authorization.d.ts +66 -0
- package/dist/authz/wire-record-authorization.d.ts.map +1 -0
- package/dist/authz/wire-record-authorization.js +34 -0
- package/dist/completeness/catalog-reconciler.d.ts +21 -0
- package/dist/completeness/catalog-reconciler.d.ts.map +1 -0
- package/dist/completeness/catalog-reconciler.js +74 -0
- package/dist/completeness/operation-inventory-scanner.d.ts +17 -0
- package/dist/completeness/operation-inventory-scanner.d.ts.map +1 -0
- package/dist/completeness/operation-inventory-scanner.js +79 -0
- package/dist/completeness/ports/catalog-store.d.ts +33 -0
- package/dist/completeness/ports/catalog-store.d.ts.map +1 -0
- package/dist/completeness/ports/catalog-store.js +2 -0
- package/dist/config/app-config.d.ts +18 -16
- package/dist/config/app-config.d.ts.map +1 -1
- package/dist/config/app-config.js +68 -35
- package/dist/enforcement/classification.decorator.d.ts +33 -14
- package/dist/enforcement/classification.decorator.d.ts.map +1 -1
- package/dist/enforcement/classification.decorator.js +34 -14
- package/dist/enforcement/global-enforcement.guard.d.ts +29 -19
- package/dist/enforcement/global-enforcement.guard.d.ts.map +1 -1
- package/dist/enforcement/global-enforcement.guard.js +108 -25
- package/dist/enforcement/reenroll-allowlist.d.ts +49 -0
- package/dist/enforcement/reenroll-allowlist.d.ts.map +1 -0
- package/dist/enforcement/reenroll-allowlist.js +77 -0
- package/dist/index.d.ts +35 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +159 -7
- package/dist/module/shield-tokens.d.ts +15 -0
- package/dist/module/shield-tokens.d.ts.map +1 -0
- package/dist/module/shield-tokens.js +17 -0
- package/dist/module/shield.module.d.ts +154 -10
- package/dist/module/shield.module.d.ts.map +1 -1
- package/dist/module/shield.module.js +181 -24
- package/dist/request-auth/request-principal.d.ts +42 -0
- package/dist/request-auth/request-principal.d.ts.map +1 -0
- package/dist/request-auth/request-principal.js +87 -0
- package/dist/request-auth/session-facts-als.d.ts +65 -0
- package/dist/request-auth/session-facts-als.d.ts.map +1 -0
- package/dist/request-auth/session-facts-als.js +57 -0
- package/dist/request-auth/session-token-binder.d.ts +130 -0
- package/dist/request-auth/session-token-binder.d.ts.map +1 -0
- package/dist/request-auth/session-token-binder.js +277 -0
- package/dist/request-auth/tokens.d.ts +14 -0
- package/dist/request-auth/tokens.d.ts.map +1 -0
- package/dist/request-auth/tokens.js +16 -0
- package/package.json +2 -2
|
@@ -4,22 +4,40 @@
|
|
|
4
4
|
* Every HTTP handler in Shield must carry a classification. The global
|
|
5
5
|
* enforcement guard (see {@link GlobalEnforcementGuard}) reads these keys and,
|
|
6
6
|
* for any route WITHOUT one, denies the request (deny-by-default). This is the
|
|
7
|
-
* structurally-unavoidable, fail-closed posture ADR-0002 mandates
|
|
8
|
-
*
|
|
9
|
-
*
|
|
7
|
+
* structurally-unavoidable, fail-closed posture ADR-0002 mandates.
|
|
8
|
+
*
|
|
9
|
+
* The `@Classified(...)` value now carries the {@link Classification} the guard
|
|
10
|
+
* maps to a `decide(principal, action, resource, context)` call (the request-auth
|
|
11
|
+
* layer): `{ typeKey, action }`. A bare string is still accepted (deprecated
|
|
12
|
+
* back-compat) and treated as `typeKey` with a default `action` of `'access'`.
|
|
10
13
|
*/
|
|
11
14
|
export declare const CLASSIFICATION_METADATA: {
|
|
12
15
|
/** Marks a route as an explicit, code-reviewed public opt-out. */
|
|
13
16
|
readonly PUBLIC: "shield:classification:public";
|
|
14
17
|
/**
|
|
15
|
-
* Marks a route as classified
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
* stub guard checks: a classified route is "declared", an undeclared route is
|
|
19
|
-
* denied.
|
|
18
|
+
* Marks a route as classified. The value is a {@link Classification}
|
|
19
|
+
* (`{ typeKey, action }`) the guard uses to build the `decide()` request; a bare
|
|
20
|
+
* string is accepted for back-compat (→ `{ typeKey: <string>, action: 'access' }`).
|
|
20
21
|
*/
|
|
21
22
|
readonly CLASSIFIED: "shield:classification:classified";
|
|
22
23
|
};
|
|
24
|
+
/**
|
|
25
|
+
* What a `@Classified` route gates on — the object-type key + the action the
|
|
26
|
+
* caller is attempting. The guard maps this to the Core `decide()` inputs:
|
|
27
|
+
* `Action = { key: action }`, `Resource = { level: 'object_type', typeKey,
|
|
28
|
+
* tenantId: <principal's tenant> }`. Stable keys (never route strings), matching
|
|
29
|
+
* the Core metadata contract (ADR-0004/ADR-0012).
|
|
30
|
+
*/
|
|
31
|
+
export interface Classification {
|
|
32
|
+
/** Stable object-type key the action operates on (e.g. `'company'`). */
|
|
33
|
+
readonly typeKey: string;
|
|
34
|
+
/** Stable action key (e.g. `'read'`, `'create'`, `'update'`, `'delete'`). */
|
|
35
|
+
readonly action: string;
|
|
36
|
+
}
|
|
37
|
+
/** Default action when a bare-string `@Classified('typeKey')` is used (back-compat). */
|
|
38
|
+
export declare const DEFAULT_CLASSIFIED_ACTION = "access";
|
|
39
|
+
/** Normalize either form of the decorator value to a {@link Classification}. */
|
|
40
|
+
export declare function normalizeClassification(value: string | Classification | undefined): Classification | null;
|
|
23
41
|
/**
|
|
24
42
|
* `@Public()` — the explicit, deny-by-default opt-OUT (ADR-0002).
|
|
25
43
|
*
|
|
@@ -30,12 +48,13 @@ export declare const CLASSIFICATION_METADATA: {
|
|
|
30
48
|
*/
|
|
31
49
|
export declare const Public: () => MethodDecorator & ClassDecorator;
|
|
32
50
|
/**
|
|
33
|
-
* `@Classified(
|
|
51
|
+
* `@Classified({ typeKey, action })` — declare what a route gates on.
|
|
34
52
|
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
53
|
+
* The global enforcement guard reconstructs the caller's `Principal` from the
|
|
54
|
+
* request's bearer token, builds `Action`/`Resource` from this classification, and
|
|
55
|
+
* calls the Core `decide()` — allowing the request iff the decision is `allow`
|
|
56
|
+
* (deny-by-default otherwise). A bare string (`@Classified('company')`) is accepted
|
|
57
|
+
* for back-compat (→ action `'access'`), but the structured form is preferred.
|
|
39
58
|
*/
|
|
40
|
-
export declare const Classified: (classification: string) => MethodDecorator & ClassDecorator;
|
|
59
|
+
export declare const Classified: (classification: string | Classification) => MethodDecorator & ClassDecorator;
|
|
41
60
|
//# sourceMappingURL=classification.decorator.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"classification.decorator.d.ts","sourceRoot":"","sources":["../../src/enforcement/classification.decorator.ts"],"names":[],"mappings":"AAEA
|
|
1
|
+
{"version":3,"file":"classification.decorator.d.ts","sourceRoot":"","sources":["../../src/enforcement/classification.decorator.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,uBAAuB;IAClC,kEAAkE;;IAElE;;;;OAIG;;CAEK,CAAC;AAEX;;;;;;GAMG;AACH,MAAM,WAAW,cAAc;IAC7B,wEAAwE;IACxE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,6EAA6E;IAC7E,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED,wFAAwF;AACxF,eAAO,MAAM,yBAAyB,WAAW,CAAC;AAElD,gFAAgF;AAChF,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS,GACzC,cAAc,GAAG,IAAI,CAcvB;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,MAAM,QAAO,eAAe,GAAG,cACO,CAAC;AAEpD;;;;;;;;GAQG;AACH,eAAO,MAAM,UAAU,GACrB,gBAAgB,MAAM,GAAG,cAAc,KACtC,eAAe,GAAG,cAC4C,CAAC"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.Classified = exports.Public = exports.CLASSIFICATION_METADATA = void 0;
|
|
3
|
+
exports.Classified = exports.Public = exports.DEFAULT_CLASSIFIED_ACTION = exports.CLASSIFICATION_METADATA = void 0;
|
|
4
|
+
exports.normalizeClassification = normalizeClassification;
|
|
4
5
|
const common_1 = require("@nestjs/common");
|
|
5
6
|
/**
|
|
6
7
|
* Route-classification metadata keys (ADR-0002).
|
|
@@ -8,22 +9,40 @@ const common_1 = require("@nestjs/common");
|
|
|
8
9
|
* Every HTTP handler in Shield must carry a classification. The global
|
|
9
10
|
* enforcement guard (see {@link GlobalEnforcementGuard}) reads these keys and,
|
|
10
11
|
* for any route WITHOUT one, denies the request (deny-by-default). This is the
|
|
11
|
-
* structurally-unavoidable, fail-closed posture ADR-0002 mandates
|
|
12
|
-
*
|
|
13
|
-
*
|
|
12
|
+
* structurally-unavoidable, fail-closed posture ADR-0002 mandates.
|
|
13
|
+
*
|
|
14
|
+
* The `@Classified(...)` value now carries the {@link Classification} the guard
|
|
15
|
+
* maps to a `decide(principal, action, resource, context)` call (the request-auth
|
|
16
|
+
* layer): `{ typeKey, action }`. A bare string is still accepted (deprecated
|
|
17
|
+
* back-compat) and treated as `typeKey` with a default `action` of `'access'`.
|
|
14
18
|
*/
|
|
15
19
|
exports.CLASSIFICATION_METADATA = {
|
|
16
20
|
/** Marks a route as an explicit, code-reviewed public opt-out. */
|
|
17
21
|
PUBLIC: 'shield:classification:public',
|
|
18
22
|
/**
|
|
19
|
-
* Marks a route as classified
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
* stub guard checks: a classified route is "declared", an undeclared route is
|
|
23
|
-
* denied.
|
|
23
|
+
* Marks a route as classified. The value is a {@link Classification}
|
|
24
|
+
* (`{ typeKey, action }`) the guard uses to build the `decide()` request; a bare
|
|
25
|
+
* string is accepted for back-compat (→ `{ typeKey: <string>, action: 'access' }`).
|
|
24
26
|
*/
|
|
25
27
|
CLASSIFIED: 'shield:classification:classified',
|
|
26
28
|
};
|
|
29
|
+
/** Default action when a bare-string `@Classified('typeKey')` is used (back-compat). */
|
|
30
|
+
exports.DEFAULT_CLASSIFIED_ACTION = 'access';
|
|
31
|
+
/** Normalize either form of the decorator value to a {@link Classification}. */
|
|
32
|
+
function normalizeClassification(value) {
|
|
33
|
+
if (value === undefined)
|
|
34
|
+
return null;
|
|
35
|
+
if (typeof value === 'string') {
|
|
36
|
+
return value.length > 0 ? { typeKey: value, action: exports.DEFAULT_CLASSIFIED_ACTION } : null;
|
|
37
|
+
}
|
|
38
|
+
if (typeof value.typeKey === 'string' &&
|
|
39
|
+
value.typeKey.length > 0 &&
|
|
40
|
+
typeof value.action === 'string' &&
|
|
41
|
+
value.action.length > 0) {
|
|
42
|
+
return { typeKey: value.typeKey, action: value.action };
|
|
43
|
+
}
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
27
46
|
/**
|
|
28
47
|
* `@Public()` — the explicit, deny-by-default opt-OUT (ADR-0002).
|
|
29
48
|
*
|
|
@@ -35,12 +54,13 @@ exports.CLASSIFICATION_METADATA = {
|
|
|
35
54
|
const Public = () => (0, common_1.SetMetadata)(exports.CLASSIFICATION_METADATA.PUBLIC, true);
|
|
36
55
|
exports.Public = Public;
|
|
37
56
|
/**
|
|
38
|
-
* `@Classified(
|
|
57
|
+
* `@Classified({ typeKey, action })` — declare what a route gates on.
|
|
39
58
|
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
59
|
+
* The global enforcement guard reconstructs the caller's `Principal` from the
|
|
60
|
+
* request's bearer token, builds `Action`/`Resource` from this classification, and
|
|
61
|
+
* calls the Core `decide()` — allowing the request iff the decision is `allow`
|
|
62
|
+
* (deny-by-default otherwise). A bare string (`@Classified('company')`) is accepted
|
|
63
|
+
* for back-compat (→ action `'access'`), but the structured form is preferred.
|
|
44
64
|
*/
|
|
45
65
|
const Classified = (classification) => (0, common_1.SetMetadata)(exports.CLASSIFICATION_METADATA.CLASSIFIED, classification);
|
|
46
66
|
exports.Classified = Classified;
|
|
@@ -1,31 +1,41 @@
|
|
|
1
1
|
import { CanActivate, ExecutionContext } from '@nestjs/common';
|
|
2
2
|
import { Reflector } from '@nestjs/core';
|
|
3
|
+
import type { CoreDecisionAuthority } from './core-seam.js';
|
|
4
|
+
import type { SessionTokenBinder } from '../request-auth/session-token-binder.js';
|
|
3
5
|
/**
|
|
4
|
-
* Global, fail-closed enforcement guard (ADR-0002)
|
|
6
|
+
* Global, fail-closed enforcement guard (ADR-0002) — now with REAL request
|
|
7
|
+
* authorization for classified routes.
|
|
5
8
|
*
|
|
6
9
|
* This guard is the SINGLE, structurally-unavoidable point every request passes
|
|
7
|
-
* through
|
|
8
|
-
*
|
|
9
|
-
* holds the sole binding) — NOT opt-in per controller — so no endpoint can escape
|
|
10
|
-
* it. Its posture is **deny-by-default**:
|
|
10
|
+
* through (bound as the sole `APP_GUARD` via `ShieldModule.forRoot()`). Posture is
|
|
11
|
+
* **deny-by-default**:
|
|
11
12
|
*
|
|
12
|
-
* -
|
|
13
|
-
* -
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
13
|
+
* - `@Public()` → ALLOWED (explicit, code-reviewed opt-out).
|
|
14
|
+
* - `@Classified({typeKey, action})` → the request-auth path runs:
|
|
15
|
+
* 1. extract the bearer access token from `Authorization`,
|
|
16
|
+
* 2. resolve it to a `Principal` (via the {@link SessionTokenBinder}),
|
|
17
|
+
* 3. build `Action`/`Resource` from the classification,
|
|
18
|
+
* 4. call the Core `decide()` — ALLOW iff `effect === 'allow'`, else 403.
|
|
19
|
+
* No/invalid token → 401 (fail-closed). This replaces the old scaffold stub
|
|
20
|
+
* that let classified routes through unconditionally.
|
|
21
|
+
* - No classification → DENIED (403). A forgotten declaration locks the door.
|
|
19
22
|
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
23
|
+
* OPT-IN / GRACEFUL DEGRADE: the request-auth dependencies (the token binder) exist
|
|
24
|
+
* only when `auth: true`. When they are ABSENT (enforcement-only, no-`.env` boot),
|
|
25
|
+
* the guard cannot authenticate a request — so a classified route with the
|
|
26
|
+
* request-auth path unavailable is DENIED (403), never allowed-through. This keeps
|
|
27
|
+
* the enforcement-only app booting AND fail-closed: an app that declares classified
|
|
28
|
+
* routes but hasn't enabled auth gets a clear deny, not a silent open door. (The
|
|
29
|
+
* decision authority defaults to the deny-by-default stub, so even a wired-but-
|
|
30
|
+
* unconfigured authority denies.)
|
|
25
31
|
*/
|
|
26
32
|
export declare class GlobalEnforcementGuard implements CanActivate {
|
|
27
33
|
private readonly reflector;
|
|
28
|
-
|
|
29
|
-
|
|
34
|
+
private readonly decisionAuthority?;
|
|
35
|
+
private readonly tokenBinder?;
|
|
36
|
+
constructor(reflector: Reflector, decisionAuthority?: CoreDecisionAuthority | undefined, tokenBinder?: SessionTokenBinder | undefined);
|
|
37
|
+
canActivate(context: ExecutionContext): Promise<boolean>;
|
|
38
|
+
/** The real authorization path for a classified route (fail-closed throughout). */
|
|
39
|
+
private authorizeClassified;
|
|
30
40
|
}
|
|
31
41
|
//# sourceMappingURL=global-enforcement.guard.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"global-enforcement.guard.d.ts","sourceRoot":"","sources":["../../src/enforcement/global-enforcement.guard.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"global-enforcement.guard.d.ts","sourceRoot":"","sources":["../../src/enforcement/global-enforcement.guard.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,WAAW,EACX,gBAAgB,EAMjB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAOzC,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AAE5D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yCAAyC,CAAC;AAQlF;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,qBACa,sBAAuB,YAAW,WAAW;IAEtD,OAAO,CAAC,QAAQ,CAAC,SAAS;IAG1B,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAC;IAGnC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC;gBANZ,SAAS,EAAE,SAAS,EAGpB,iBAAiB,CAAC,EAAE,qBAAqB,YAAA,EAGzC,WAAW,CAAC,EAAE,kBAAkB,YAAA;IAG7C,WAAW,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,OAAO,CAAC;IAuC9D,mFAAmF;YACrE,mBAAmB;CA0FlC"}
|
|
@@ -8,40 +8,56 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
8
8
|
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
9
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
10
|
};
|
|
11
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
12
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
13
|
+
};
|
|
11
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
15
|
exports.GlobalEnforcementGuard = void 0;
|
|
13
16
|
const common_1 = require("@nestjs/common");
|
|
14
17
|
const core_1 = require("@nestjs/core");
|
|
15
18
|
const classification_decorator_js_1 = require("./classification.decorator.js");
|
|
19
|
+
const shield_tokens_js_1 = require("../module/shield-tokens.js");
|
|
20
|
+
const tokens_js_1 = require("../request-auth/tokens.js");
|
|
21
|
+
const request_principal_js_1 = require("../request-auth/request-principal.js");
|
|
22
|
+
const session_facts_als_js_1 = require("../request-auth/session-facts-als.js");
|
|
23
|
+
const reenroll_allowlist_js_1 = require("./reenroll-allowlist.js");
|
|
16
24
|
/**
|
|
17
|
-
* Global, fail-closed enforcement guard (ADR-0002)
|
|
25
|
+
* Global, fail-closed enforcement guard (ADR-0002) — now with REAL request
|
|
26
|
+
* authorization for classified routes.
|
|
18
27
|
*
|
|
19
28
|
* This guard is the SINGLE, structurally-unavoidable point every request passes
|
|
20
|
-
* through
|
|
21
|
-
*
|
|
22
|
-
* holds the sole binding) — NOT opt-in per controller — so no endpoint can escape
|
|
23
|
-
* it. Its posture is **deny-by-default**:
|
|
29
|
+
* through (bound as the sole `APP_GUARD` via `ShieldModule.forRoot()`). Posture is
|
|
30
|
+
* **deny-by-default**:
|
|
24
31
|
*
|
|
25
|
-
* -
|
|
26
|
-
* -
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
+
* - `@Public()` → ALLOWED (explicit, code-reviewed opt-out).
|
|
33
|
+
* - `@Classified({typeKey, action})` → the request-auth path runs:
|
|
34
|
+
* 1. extract the bearer access token from `Authorization`,
|
|
35
|
+
* 2. resolve it to a `Principal` (via the {@link SessionTokenBinder}),
|
|
36
|
+
* 3. build `Action`/`Resource` from the classification,
|
|
37
|
+
* 4. call the Core `decide()` — ALLOW iff `effect === 'allow'`, else 403.
|
|
38
|
+
* No/invalid token → 401 (fail-closed). This replaces the old scaffold stub
|
|
39
|
+
* that let classified routes through unconditionally.
|
|
40
|
+
* - No classification → DENIED (403). A forgotten declaration locks the door.
|
|
32
41
|
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
42
|
+
* OPT-IN / GRACEFUL DEGRADE: the request-auth dependencies (the token binder) exist
|
|
43
|
+
* only when `auth: true`. When they are ABSENT (enforcement-only, no-`.env` boot),
|
|
44
|
+
* the guard cannot authenticate a request — so a classified route with the
|
|
45
|
+
* request-auth path unavailable is DENIED (403), never allowed-through. This keeps
|
|
46
|
+
* the enforcement-only app booting AND fail-closed: an app that declares classified
|
|
47
|
+
* routes but hasn't enabled auth gets a clear deny, not a silent open door. (The
|
|
48
|
+
* decision authority defaults to the deny-by-default stub, so even a wired-but-
|
|
49
|
+
* unconfigured authority denies.)
|
|
38
50
|
*/
|
|
39
51
|
let GlobalEnforcementGuard = class GlobalEnforcementGuard {
|
|
40
52
|
reflector;
|
|
41
|
-
|
|
53
|
+
decisionAuthority;
|
|
54
|
+
tokenBinder;
|
|
55
|
+
constructor(reflector, decisionAuthority, tokenBinder) {
|
|
42
56
|
this.reflector = reflector;
|
|
57
|
+
this.decisionAuthority = decisionAuthority;
|
|
58
|
+
this.tokenBinder = tokenBinder;
|
|
43
59
|
}
|
|
44
|
-
canActivate(context) {
|
|
60
|
+
async canActivate(context) {
|
|
45
61
|
// Only HTTP requests are classified here. Non-HTTP contexts (if ever added)
|
|
46
62
|
// must be handled explicitly rather than silently allowed.
|
|
47
63
|
if (context.getType() !== 'http') {
|
|
@@ -57,20 +73,87 @@ let GlobalEnforcementGuard = class GlobalEnforcementGuard {
|
|
|
57
73
|
if (isPublic === true) {
|
|
58
74
|
return true;
|
|
59
75
|
}
|
|
60
|
-
// 2. Classified route →
|
|
61
|
-
|
|
62
|
-
const classification =
|
|
63
|
-
if (
|
|
64
|
-
return
|
|
76
|
+
// 2. Classified route → run the request-auth + decide() path.
|
|
77
|
+
const raw = this.reflector.getAllAndOverride(classification_decorator_js_1.CLASSIFICATION_METADATA.CLASSIFIED, [handler, controller]);
|
|
78
|
+
const classification = (0, classification_decorator_js_1.normalizeClassification)(raw);
|
|
79
|
+
if (classification !== null) {
|
|
80
|
+
return this.authorizeClassified(context, classification);
|
|
65
81
|
}
|
|
66
82
|
// 3. No classification at all → DENY (ADR-0002 deny-by-default / fail-closed).
|
|
67
83
|
throw new common_1.ForbiddenException('Shield enforcement: this route carries no access classification. ' +
|
|
68
84
|
'It is denied by default (ADR-0002). Mark it @Public() to opt out, or ' +
|
|
69
85
|
'@Classified(...) to declare its permission.');
|
|
70
86
|
}
|
|
87
|
+
/** The real authorization path for a classified route (fail-closed throughout). */
|
|
88
|
+
async authorizeClassified(context, classification) {
|
|
89
|
+
// Request-auth is only possible when the token binder is wired (auth: true).
|
|
90
|
+
// Absent → we cannot authenticate the caller → DENY (never allow-through).
|
|
91
|
+
if (this.tokenBinder === undefined || this.decisionAuthority === undefined) {
|
|
92
|
+
throw new common_1.ForbiddenException('Shield enforcement: this route is @Classified but request authentication is not ' +
|
|
93
|
+
"enabled (ShieldModule.forRoot({ auth: true })). It is denied by default.");
|
|
94
|
+
}
|
|
95
|
+
const req = context.switchToHttp().getRequest();
|
|
96
|
+
const authHeader = req?.headers?.['authorization'];
|
|
97
|
+
const resolved = await (0, request_principal_js_1.resolveRequestAuth)(authHeader, this.tokenBinder);
|
|
98
|
+
if (resolved === null) {
|
|
99
|
+
// No/invalid/expired token → not authenticated. 401 (distinct from a deny).
|
|
100
|
+
throw new common_1.UnauthorizedException('Shield enforcement: missing or invalid access token for a classified route.');
|
|
101
|
+
}
|
|
102
|
+
const { principal, claims } = resolved;
|
|
103
|
+
// ENFORCED-RE-ENROLL GATE (spec §4.2): a session established via a recovery-code
|
|
104
|
+
// login is restricted to the re-secure allowlist until a fresh device is confirmed.
|
|
105
|
+
// Checked AFTER authentication, BEFORE decide() — a marked session hitting any route
|
|
106
|
+
// NOT on REENROLL_ALLOWED is 403 `reenrollment_required` (a distinct code the
|
|
107
|
+
// frontend recognizes → forces the re-enroll screen). Fail-closed: this is an
|
|
108
|
+
// allowlist, so an unknown/unclassified route with a marked session also 403s. This
|
|
109
|
+
// runs only on @Classified routes (the @Public re-secure routes self-authenticate
|
|
110
|
+
// and check nothing here). The classified route being gated here is, by definition,
|
|
111
|
+
// NOT on the allowlist (the allowlist is @Public), so a marked session is always
|
|
112
|
+
// denied — the isReenrollAllowed check below is defense-in-depth for a future
|
|
113
|
+
// classified allowlisted route.
|
|
114
|
+
if (claims.mustReenroll === true) {
|
|
115
|
+
const method = req?.method;
|
|
116
|
+
const url = req?.originalUrl ?? req?.url;
|
|
117
|
+
if (!(0, reenroll_allowlist_js_1.isReenrollAllowed)(method, url)) {
|
|
118
|
+
throw new common_1.ForbiddenException({ error: 'reenrollment_required' });
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
// Attach the resolved principal so downstream handlers can act as this user.
|
|
122
|
+
req.principal = principal;
|
|
123
|
+
// ── ADR-0011 SESSION-FACTS HOT PATH ──────────────────────────────────────────
|
|
124
|
+
// Enter the per-request session-facts ALS scope with THIS request's cached grants
|
|
125
|
+
// (from the binding — same bearer token → same principal → same facts) around the
|
|
126
|
+
// decide() call. The app-owned `SessionFactsReader` (an ALS-backed reader wired into
|
|
127
|
+
// the decision authority) reads the SAME `SHIELD_SESSION_FACTS_ALS` store inside
|
|
128
|
+
// decide(), so gate 5 gets these facts and can serve the coarse decision from cache
|
|
129
|
+
// (no DB reload) when the cached version is still fresh.
|
|
130
|
+
//
|
|
131
|
+
// CROSS-PRINCIPAL ISOLATION: `als.run(store, cb)` binds `store` to this async
|
|
132
|
+
// context and everything decide() awaits, and ONLY that context — a concurrent
|
|
133
|
+
// request runs its own `run()` with its own store, so getStore() never returns
|
|
134
|
+
// another request's facts. The store is built HERE from THIS request's claims.
|
|
135
|
+
//
|
|
136
|
+
// FAIL-CLOSED: no projector wired / absent / malformed binding → `claims.cachedFacts`
|
|
137
|
+
// is undefined → the store carries `cachedFacts: null` → gate 5 DB-reloads (today's
|
|
138
|
+
// behavior). The cache is only ever consulted; it never fabricates a permit.
|
|
139
|
+
const factsStore = {
|
|
140
|
+
cachedFacts: claims.cachedFacts ?? null,
|
|
141
|
+
permissionsVersion: claims.cachedFacts?.permissionsVersion ?? 0,
|
|
142
|
+
};
|
|
143
|
+
const decision = await session_facts_als_js_1.SHIELD_SESSION_FACTS_ALS.run(factsStore, () => this.decisionAuthority.decide(principal, { key: classification.action }, { level: 'object_type', typeKey: classification.typeKey, tenantId: principal.tenantId }, { isAuthenticated: true }));
|
|
144
|
+
if (decision.effect === 'allow') {
|
|
145
|
+
return true;
|
|
146
|
+
}
|
|
147
|
+
// Deny (or any non-allow) → 403. Do NOT leak the reason detail to the caller.
|
|
148
|
+
throw new common_1.ForbiddenException(`Shield enforcement: access denied for ${classification.action} on ${classification.typeKey}.`);
|
|
149
|
+
}
|
|
71
150
|
};
|
|
72
151
|
exports.GlobalEnforcementGuard = GlobalEnforcementGuard;
|
|
73
152
|
exports.GlobalEnforcementGuard = GlobalEnforcementGuard = __decorate([
|
|
74
153
|
(0, common_1.Injectable)(),
|
|
75
|
-
|
|
154
|
+
__param(1, (0, common_1.Optional)()),
|
|
155
|
+
__param(1, (0, common_1.Inject)(shield_tokens_js_1.SHIELD_DECISION_AUTHORITY)),
|
|
156
|
+
__param(2, (0, common_1.Optional)()),
|
|
157
|
+
__param(2, (0, common_1.Inject)(tokens_js_1.SHIELD_SESSION_TOKEN_BINDER)),
|
|
158
|
+
__metadata("design:paramtypes", [core_1.Reflector, Object, Function])
|
|
76
159
|
], GlobalEnforcementGuard);
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* REENROLL_ALLOWED — the enforced-re-enroll allowlist (spec §4.2).
|
|
3
|
+
*
|
|
4
|
+
* A session established via a recovery-code login carries `mustReenroll: true` in its
|
|
5
|
+
* bound claims (see {@link import('../request-auth/session-token-binder.js').BoundPrincipalClaims}).
|
|
6
|
+
* The {@link import('./global-enforcement.guard.js').GlobalEnforcementGuard} restricts
|
|
7
|
+
* such a session to ONLY the routes below — the minimal set needed to RE-SECURE the
|
|
8
|
+
* account (add a fresh authenticator device + refresh recovery codes) and to observe
|
|
9
|
+
* the restricted state (`/auth/me`, list devices) or leave it (`/auth/logout`). EVERY
|
|
10
|
+
* other authenticated route returns 403 `{ error: 'reenrollment_required' }`.
|
|
11
|
+
*
|
|
12
|
+
* FAIL-CLOSED: this is an allowlist, not a denylist — a route NOT listed here (incl. an
|
|
13
|
+
* unclassified/unknown route) is DENIED for a `mustReenroll` session. Adding a route
|
|
14
|
+
* that a re-enrolling user must reach means adding it HERE, deliberately.
|
|
15
|
+
*
|
|
16
|
+
* NOTE — allowlisted ≠ gate-clearing (spec §4.3): being on this list only lets the
|
|
17
|
+
* request THROUGH the gate; it does NOT clear `mustReenroll`. ONLY a successful
|
|
18
|
+
* `POST /account/mfa/devices/confirm` clears the flag (the account controller calls
|
|
19
|
+
* `binder.clearReenroll`). Regenerating codes is allowed but does not re-secure.
|
|
20
|
+
*/
|
|
21
|
+
/** One allowlisted route: an HTTP method + the path SUFFIX it must end with. */
|
|
22
|
+
export interface ReenrollAllowedRoute {
|
|
23
|
+
readonly method: string;
|
|
24
|
+
/** The route path (no query string, no trailing slash), matched by suffix. */
|
|
25
|
+
readonly path: string;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* The allowlist (spec §4.2). Paths are the framework-owned route paths; matched
|
|
29
|
+
* robustly by method + normalized-path suffix (see {@link isReenrollAllowed}) so a
|
|
30
|
+
* future global prefix, a trailing slash, or a query string never defeats the match.
|
|
31
|
+
*/
|
|
32
|
+
export declare const REENROLL_ALLOWED: readonly ReenrollAllowedRoute[];
|
|
33
|
+
/**
|
|
34
|
+
* Normalize a raw request path for matching: drop the query string and any fragment,
|
|
35
|
+
* collapse a trailing slash (except the root). Never throws.
|
|
36
|
+
*/
|
|
37
|
+
export declare function normalizeRequestPath(rawUrl: unknown): string;
|
|
38
|
+
/**
|
|
39
|
+
* True iff the (method, path) is on the enforced-re-enroll allowlist. Matching is
|
|
40
|
+
* case-insensitive on the method and SUFFIX-based on the normalized path — so
|
|
41
|
+
* `/api/account/mfa/devices/confirm` (a prefixed mount) still matches
|
|
42
|
+
* `/account/mfa/devices/confirm`. The path boundary is inherent: every `route.path`
|
|
43
|
+
* BEGINS with `/`, so `endsWith(route.path)` cannot spuriously match a longer segment
|
|
44
|
+
* name (`/evilaccount/...` does NOT end with `/account/...` — there is no `/` before
|
|
45
|
+
* `account`). `/account/mfa/devices/confirm-evil` also fails (its suffix is `confirm-evil`,
|
|
46
|
+
* not `/confirm`).
|
|
47
|
+
*/
|
|
48
|
+
export declare function isReenrollAllowed(method: unknown, rawUrl: unknown): boolean;
|
|
49
|
+
//# sourceMappingURL=reenroll-allowlist.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reenroll-allowlist.d.ts","sourceRoot":"","sources":["../../src/enforcement/reenroll-allowlist.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,gFAAgF;AAChF,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,8EAA8E;IAC9E,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,EAAE,SAAS,oBAAoB,EAO1D,CAAC;AAEH;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,CAO5D;AAED;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,GAAG,OAAO,CAU3E"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* REENROLL_ALLOWED — the enforced-re-enroll allowlist (spec §4.2).
|
|
4
|
+
*
|
|
5
|
+
* A session established via a recovery-code login carries `mustReenroll: true` in its
|
|
6
|
+
* bound claims (see {@link import('../request-auth/session-token-binder.js').BoundPrincipalClaims}).
|
|
7
|
+
* The {@link import('./global-enforcement.guard.js').GlobalEnforcementGuard} restricts
|
|
8
|
+
* such a session to ONLY the routes below — the minimal set needed to RE-SECURE the
|
|
9
|
+
* account (add a fresh authenticator device + refresh recovery codes) and to observe
|
|
10
|
+
* the restricted state (`/auth/me`, list devices) or leave it (`/auth/logout`). EVERY
|
|
11
|
+
* other authenticated route returns 403 `{ error: 'reenrollment_required' }`.
|
|
12
|
+
*
|
|
13
|
+
* FAIL-CLOSED: this is an allowlist, not a denylist — a route NOT listed here (incl. an
|
|
14
|
+
* unclassified/unknown route) is DENIED for a `mustReenroll` session. Adding a route
|
|
15
|
+
* that a re-enrolling user must reach means adding it HERE, deliberately.
|
|
16
|
+
*
|
|
17
|
+
* NOTE — allowlisted ≠ gate-clearing (spec §4.3): being on this list only lets the
|
|
18
|
+
* request THROUGH the gate; it does NOT clear `mustReenroll`. ONLY a successful
|
|
19
|
+
* `POST /account/mfa/devices/confirm` clears the flag (the account controller calls
|
|
20
|
+
* `binder.clearReenroll`). Regenerating codes is allowed but does not re-secure.
|
|
21
|
+
*/
|
|
22
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
23
|
+
exports.REENROLL_ALLOWED = void 0;
|
|
24
|
+
exports.normalizeRequestPath = normalizeRequestPath;
|
|
25
|
+
exports.isReenrollAllowed = isReenrollAllowed;
|
|
26
|
+
/**
|
|
27
|
+
* The allowlist (spec §4.2). Paths are the framework-owned route paths; matched
|
|
28
|
+
* robustly by method + normalized-path suffix (see {@link isReenrollAllowed}) so a
|
|
29
|
+
* future global prefix, a trailing slash, or a query string never defeats the match.
|
|
30
|
+
*/
|
|
31
|
+
exports.REENROLL_ALLOWED = Object.freeze([
|
|
32
|
+
{ method: 'POST', path: '/account/mfa/devices/begin' },
|
|
33
|
+
{ method: 'POST', path: '/account/mfa/devices/confirm' },
|
|
34
|
+
{ method: 'POST', path: '/account/mfa/recovery-codes/regenerate' },
|
|
35
|
+
{ method: 'GET', path: '/account/mfa/devices' },
|
|
36
|
+
{ method: 'GET', path: '/auth/me' },
|
|
37
|
+
{ method: 'POST', path: '/auth/logout' },
|
|
38
|
+
]);
|
|
39
|
+
/**
|
|
40
|
+
* Normalize a raw request path for matching: drop the query string and any fragment,
|
|
41
|
+
* collapse a trailing slash (except the root). Never throws.
|
|
42
|
+
*/
|
|
43
|
+
function normalizeRequestPath(rawUrl) {
|
|
44
|
+
if (typeof rawUrl !== 'string' || rawUrl.length === 0)
|
|
45
|
+
return '';
|
|
46
|
+
// Strip query (?...) and fragment (#...).
|
|
47
|
+
let path = rawUrl.split('?', 1)[0].split('#', 1)[0];
|
|
48
|
+
// Collapse a single trailing slash (but keep the root "/").
|
|
49
|
+
if (path.length > 1 && path.endsWith('/'))
|
|
50
|
+
path = path.slice(0, -1);
|
|
51
|
+
return path;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* True iff the (method, path) is on the enforced-re-enroll allowlist. Matching is
|
|
55
|
+
* case-insensitive on the method and SUFFIX-based on the normalized path — so
|
|
56
|
+
* `/api/account/mfa/devices/confirm` (a prefixed mount) still matches
|
|
57
|
+
* `/account/mfa/devices/confirm`. The path boundary is inherent: every `route.path`
|
|
58
|
+
* BEGINS with `/`, so `endsWith(route.path)` cannot spuriously match a longer segment
|
|
59
|
+
* name (`/evilaccount/...` does NOT end with `/account/...` — there is no `/` before
|
|
60
|
+
* `account`). `/account/mfa/devices/confirm-evil` also fails (its suffix is `confirm-evil`,
|
|
61
|
+
* not `/confirm`).
|
|
62
|
+
*/
|
|
63
|
+
function isReenrollAllowed(method, rawUrl) {
|
|
64
|
+
if (typeof method !== 'string')
|
|
65
|
+
return false;
|
|
66
|
+
const m = method.toUpperCase();
|
|
67
|
+
const path = normalizeRequestPath(rawUrl);
|
|
68
|
+
if (path === '')
|
|
69
|
+
return false;
|
|
70
|
+
for (const route of exports.REENROLL_ALLOWED) {
|
|
71
|
+
if (route.method !== m)
|
|
72
|
+
continue;
|
|
73
|
+
if (path === route.path || path.endsWith(route.path))
|
|
74
|
+
return true;
|
|
75
|
+
}
|
|
76
|
+
return false;
|
|
77
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -14,21 +14,53 @@
|
|
|
14
14
|
* Depends on `@astram/core` (workspace); PEER-depends on `@nestjs/common`,
|
|
15
15
|
* `@nestjs/core`, `reflect-metadata`, `rxjs` so the host owns one copy of each.
|
|
16
16
|
*/
|
|
17
|
-
export { ShieldModule, SHIELD_DECISION_AUTHORITY, type ShieldModuleOptions, type ShieldModuleAsyncOptions, } from './module/shield.module.js';
|
|
18
|
-
export { Public, Classified, CLASSIFICATION_METADATA, } from './enforcement/classification.decorator.js';
|
|
17
|
+
export { ShieldModule, SHIELD_DECISION_AUTHORITY, type ShieldModuleOptions, type ShieldModuleAsyncOptions, type RecordAuthorizationInputs, } from './module/shield.module.js';
|
|
18
|
+
export { Public, Classified, CLASSIFICATION_METADATA, DEFAULT_CLASSIFIED_ACTION, normalizeClassification, } from './enforcement/classification.decorator.js';
|
|
19
|
+
export type { Classification } from './enforcement/classification.decorator.js';
|
|
20
|
+
export { OperationInventoryScanner } from './completeness/operation-inventory-scanner.js';
|
|
21
|
+
export { CatalogReconciler, CatalogGatabilityError } from './completeness/catalog-reconciler.js';
|
|
22
|
+
export type { CatalogStore, CatalogPermissionRow, ReconcileReport, } from './completeness/ports/catalog-store.js';
|
|
23
|
+
export { SHIELD_SESSION_TOKEN_BINDER } from './request-auth/tokens.js';
|
|
24
|
+
export { SessionTokenBinder } from './request-auth/session-token-binder.js';
|
|
25
|
+
export type { BoundPrincipalClaims } from './request-auth/session-token-binder.js';
|
|
26
|
+
export { resolveRequestPrincipal, principalFromClaims, extractBearerToken, } from './request-auth/request-principal.js';
|
|
27
|
+
export { SHIELD_SESSION_FACTS_ALS, NO_SESSION_FACTS, type SessionFactsStore, } from './request-auth/session-facts-als.js';
|
|
19
28
|
export { GlobalEnforcementGuard } from './enforcement/global-enforcement.guard.js';
|
|
20
29
|
export { stubDecisionAuthority, type Effect, type CoreDecisionAuthority, type CoreDecision, type Principal, } from './enforcement/core-seam.js';
|
|
21
30
|
export { Argon2idPasswordHasher, argon2idHasherProvider, SHIELD_PASSWORD_HASHER, type Argon2idParams, } from './authentication/argon2id-password-hasher.js';
|
|
22
31
|
export { MariaDbSessionRecordStore, SHIELD_SESSION_RECORD_STORE, RedisHotSessionStore, SHIELD_HOT_SESSION_STORE, CryptoTokenGenerator, Sha256RefreshTokenHasher, SystemClock, cryptoTokenGeneratorProvider, refreshHasherProvider, systemClockProvider, SHIELD_TOKEN_GENERATOR, SHIELD_REFRESH_HASHER, SHIELD_CLOCK, } from './session/index.js';
|
|
32
|
+
export { MariaDbRecordPermissionRuleAdapter, SHIELD_RECORD_RULE_PORT, type RuleQueryable, } from './authz/mariadb-record-rule-adapter.js';
|
|
33
|
+
export { MariaDbRecordShareAdapter, SHIELD_RECORD_SHARE_PORT, type ShareQueryable, } from './authz/mariadb-record-share-adapter.js';
|
|
34
|
+
export { RecordShareWriter, SHIELD_RECORD_SHARE_WRITER, ShareNotPermittedError, ShareStoreUnavailableError, type ShareInput, type ShareGranteeKind, type ShareTypeInfo, type ShareTypeResolver, type ShareAuditEvent, type ShareAuditSink, } from './authz/record-share-writer.js';
|
|
35
|
+
export { makeShareDeleteCascade, type RecordDeletedHook, type ShareQueryFn, } from './authz/record-share-cascade.js';
|
|
36
|
+
export { MariaDbCrossTenantGrantAdapter, SHIELD_CROSS_TENANT_GRANT_PORT, type GrantQueryable, } from './authz/mariadb-cross-tenant-grant-adapter.js';
|
|
37
|
+
export { CrossTenantGrantWriter, SHIELD_CROSS_TENANT_GRANT_WRITER, GrantNotPermittedError, InvalidGrantError, GrantStoreUnavailableError, type GrantAuthorizeFn, type GrantAuditEvent, type GrantAuditSink, type GrantGranteeKind, type GrantScope, type OfferGrantInput, type AcceptGrantInput, type RevokeGrantInput, } from './authz/cross-tenant-grant-writer.js';
|
|
38
|
+
export { ManifestOperationAuthorityAdapter, SHIELD_OPERATION_AUTHORITY_PORT, type OperationManifestEntry, } from './authz/manifest-operation-authority-adapter.js';
|
|
39
|
+
export { buildRecordAuthorization } from './authz/wire-record-authorization.js';
|
|
23
40
|
export { BootSequence, assertPoolBudget } from './boot/boot-sequence.js';
|
|
24
41
|
export { APP_CONFIG } from './config/config.module.js';
|
|
25
42
|
export { loadAppConfig, loadPoolBudget, evaluatePoolBudget, DEFAULT_POOL_BUDGET, SCHEMA_SVC_POOL_HARD_CAP, type AppConfig, type PoolBudgetConfig, type PoolBudgetEvaluation, type RuntimeAccountPools, loadDbConfig, loadRedisConfig, loadSessionLifetimes, loadRefreshHmacKey, assertRefreshHmacKey, DEFAULT_DB_CONFIG, DEFAULT_REDIS_URL, type DbConfig, type RedisConfig, type SessionLifetimeOverrides, } from './config/app-config.js';
|
|
26
|
-
export { SHIELD_DB_POOL, SHIELD_REDIS, SHIELD_CREDENTIAL_RESOLVER, SHIELD_AUTHENTICATOR, SHIELD_SESSION_AUTHORITY, } from './auth/tokens.js';
|
|
43
|
+
export { SHIELD_DB_POOL, SHIELD_REDIS, SHIELD_CREDENTIAL_RESOLVER, SHIELD_PROFILE_RESOLVER, SHIELD_AUTHENTICATOR, SHIELD_SESSION_AUTHORITY, SHIELD_CREDENTIAL_WRITER, SHIELD_RESET_TOKEN_STORE, SHIELD_EMAIL_SENDER, SHIELD_RECOVERY_CODE_STORE, SHIELD_PROFILE_WRITER, SHIELD_SESSION_SWEEPER, SHIELD_AUDIT_SINK, SHIELD_MFA_DEVICE_STORE, SHIELD_TOTP_VERIFIER_OVERRIDE, } from './auth/tokens.js';
|
|
27
44
|
export { MariaDbCredentialResolver } from './authentication/mariadb-credential-resolver.js';
|
|
28
45
|
export type { SessionView, SessionTokensView, SessionSuccess, LoginRequest, MfaChallengeResponse, LoginResponse, MfaRequest, MfaResponse, RefreshRequest, RefreshResponse, LogoutRequest, LogoutAck, } from './auth/dto.js';
|
|
29
46
|
export { PersistenceModule } from './auth/persistence.module.js';
|
|
30
47
|
export { AuthModule, type AuthModuleOptions } from './auth/auth.module.js';
|
|
48
|
+
export { makeDefaultTotpVerifier, resolveTotpVerifier, hmacSha1 } from './auth/auth.module.js';
|
|
31
49
|
export type { CredentialResolver, ResolvedCredential, RequiredFactor, } from '@astram/core';
|
|
50
|
+
export type { FactorVerifier, FactorVerdict, Credential } from '@astram/core';
|
|
51
|
+
export type { ProfileResolver, CurrentUserProfile, ProfileRole, ProfileTenant, } from './auth/profile-resolver.js';
|
|
52
|
+
export type { CredentialWriter } from './auth/ports/credential-writer.js';
|
|
53
|
+
export type { ResetTokenStore } from './auth/ports/reset-token-store.js';
|
|
54
|
+
export type { EmailSender, EmailMessage } from './auth/ports/email-sender.js';
|
|
55
|
+
export type { RecoveryCodeStore } from './auth/ports/recovery-code-store.js';
|
|
56
|
+
export type { ProfileWriter } from './auth/ports/profile-writer.js';
|
|
57
|
+
export type { SessionSweeper } from './auth/ports/session-sweeper.js';
|
|
58
|
+
export type { MfaDeviceStore, MfaDevice } from './auth/ports/mfa-device-store.js';
|
|
59
|
+
export type { AuditSink, AuthAuditEvent } from './auth/ports/audit-sink.js';
|
|
60
|
+
export type { SessionFactsProjector } from './auth/ports/session-facts-projector.js';
|
|
61
|
+
export { SHIELD_SESSION_FACTS_PROJECTOR } from './auth/tokens.js';
|
|
32
62
|
export { AuthController } from './auth/auth.controller.js';
|
|
33
63
|
export { PendingMfaStore } from './auth/pending-mfa-store.js';
|
|
64
|
+
export { generateTotpSecret, buildOtpauthUri, generateRecoveryCodes, } from './auth/totp-enrollment.js';
|
|
65
|
+
export { AccountController } from './auth/account.controller.js';
|
|
34
66
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAGH,OAAO,EACL,YAAY,EACZ,yBAAyB,EACzB,KAAK,mBAAmB,EACxB,KAAK,wBAAwB,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|