@astram/guard 0.1.1 → 0.3.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/account.controller.js.map +1 -0
- package/dist/auth/auth.controller.d.ts +316 -0
- package/dist/auth/auth.controller.d.ts.map +1 -0
- package/dist/auth/auth.controller.js +1091 -0
- package/dist/auth/auth.controller.js.map +1 -0
- package/dist/auth/auth.module.d.ts +202 -0
- package/dist/auth/auth.module.d.ts.map +1 -0
- package/dist/auth/auth.module.js +610 -0
- package/dist/auth/auth.module.js.map +1 -0
- package/dist/auth/connections.d.ts +61 -0
- package/dist/auth/connections.d.ts.map +1 -0
- package/dist/auth/connections.js +116 -0
- package/dist/auth/connections.js.map +1 -0
- package/dist/auth/dto.d.ts +154 -0
- package/dist/auth/dto.d.ts.map +1 -0
- package/dist/auth/dto.js +29 -0
- package/dist/auth/dto.js.map +1 -0
- package/dist/auth/pending-mfa-store.d.ts +72 -0
- package/dist/auth/pending-mfa-store.d.ts.map +1 -0
- package/dist/auth/pending-mfa-store.js +106 -0
- package/dist/auth/pending-mfa-store.js.map +1 -0
- package/dist/auth/persistence.module.d.ts +42 -0
- package/dist/auth/persistence.module.d.ts.map +1 -0
- package/dist/auth/persistence.module.js +94 -0
- package/dist/auth/persistence.module.js.map +1 -0
- 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/audit-sink.js.map +1 -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/credential-writer.js.map +1 -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/email-sender.js.map +1 -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/mfa-device-store.js.map +1 -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/profile-writer.js.map +1 -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/recovery-code-store.js.map +1 -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/reset-token-store.js.map +1 -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-facts-projector.js.map +1 -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/ports/session-sweeper.js.map +1 -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/profile-resolver.js.map +1 -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/rate-limit/attempt-limiter.js.map +1 -0
- package/dist/auth/tokens.d.ts +236 -0
- package/dist/auth/tokens.d.ts.map +1 -0
- package/dist/auth/tokens.js +254 -0
- package/dist/auth/tokens.js.map +1 -0
- 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/auth/totp-enrollment.js.map +1 -0
- package/dist/authentication/argon2id-password-hasher.js.map +1 -0
- package/dist/authentication/mariadb-credential-resolver.d.ts +110 -0
- package/dist/authentication/mariadb-credential-resolver.d.ts.map +1 -0
- package/dist/authentication/mariadb-credential-resolver.js +170 -0
- package/dist/authentication/mariadb-credential-resolver.js.map +1 -0
- 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/cross-tenant-grant-writer.js.map +1 -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/manifest-operation-authority-adapter.js.map +1 -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-cross-tenant-grant-adapter.js.map +1 -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-rule-adapter.js.map +1 -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/mariadb-record-share-adapter.js.map +1 -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-cascade.js.map +1 -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/record-share-writer.js.map +1 -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/authz/wire-record-authorization.js.map +1 -0
- package/dist/boot/boot-sequence.js.map +1 -0
- package/dist/boot/boot.module.js.map +1 -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/catalog-reconciler.js.map +1 -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/operation-inventory-scanner.js.map +1 -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/completeness/ports/catalog-store.js.map +1 -0
- package/dist/config/app-config.d.ts +134 -1
- package/dist/config/app-config.d.ts.map +1 -1
- package/dist/config/app-config.js +144 -2
- package/dist/config/app-config.js.map +1 -0
- package/dist/config/config.module.js.map +1 -0
- 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/classification.decorator.js.map +1 -0
- package/dist/enforcement/core-seam.js.map +1 -0
- package/dist/enforcement/enforcement.module.js.map +1 -0
- 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/global-enforcement.guard.js.map +1 -0
- 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/enforcement/reenroll-allowlist.js.map +1 -0
- package/dist/index.d.ts +43 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +198 -1
- package/dist/index.js.map +1 -0
- 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-tokens.js.map +1 -0
- package/dist/module/shield.module.d.ts +203 -8
- package/dist/module/shield.module.d.ts.map +1 -1
- package/dist/module/shield.module.js +232 -14
- package/dist/module/shield.module.js.map +1 -0
- 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/request-principal.js.map +1 -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-facts-als.js.map +1 -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/session-token-binder.js.map +1 -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/dist/request-auth/tokens.js.map +1 -0
- package/dist/session/crypto-adapters.js.map +1 -0
- package/dist/session/index.js.map +1 -0
- package/dist/session/mariadb-session-record-store.js.map +1 -0
- package/dist/session/redis-hot-session-store.js.map +1 -0
- package/package.json +2 -2
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.SCHEMA_SVC_POOL_HARD_CAP = exports.DEFAULT_POOL_BUDGET = void 0;
|
|
3
|
+
exports.DEFAULT_REDIS_URL = exports.DEFAULT_DB_CONFIG = exports.SCHEMA_SVC_POOL_HARD_CAP = exports.DEFAULT_POOL_BUDGET = void 0;
|
|
4
4
|
exports.loadPoolBudget = loadPoolBudget;
|
|
5
|
+
exports.loadDbConfig = loadDbConfig;
|
|
6
|
+
exports.loadRedisConfig = loadRedisConfig;
|
|
7
|
+
exports.loadSessionLifetimes = loadSessionLifetimes;
|
|
8
|
+
exports.loadRefreshHmacKey = loadRefreshHmacKey;
|
|
9
|
+
exports.assertRefreshHmacKey = assertRefreshHmacKey;
|
|
5
10
|
exports.loadAppConfig = loadAppConfig;
|
|
6
11
|
exports.evaluatePoolBudget = evaluatePoolBudget;
|
|
7
12
|
/** Documented MVP single-node defaults (see the class doc + my.cnf). */
|
|
@@ -61,15 +66,152 @@ function loadPoolBudget(env = process.env) {
|
|
|
61
66
|
},
|
|
62
67
|
};
|
|
63
68
|
}
|
|
69
|
+
// ---------------------------------------------------------------------------
|
|
70
|
+
// Phase 0b Auth-surface config loaders (additive) — DB / Redis / secret /
|
|
71
|
+
// lifetimes. All pure env-readers with documented defaults; the ONLY hard-
|
|
72
|
+
// required value is the refresh HMAC key (fail-closed).
|
|
73
|
+
// ---------------------------------------------------------------------------
|
|
74
|
+
/** Documented defaults for the runtime MariaDB descriptor (local docker substrate). */
|
|
75
|
+
exports.DEFAULT_DB_CONFIG = {
|
|
76
|
+
host: 'localhost',
|
|
77
|
+
port: 3306,
|
|
78
|
+
user: 'shield_core',
|
|
79
|
+
database: 'shield_identity',
|
|
80
|
+
};
|
|
81
|
+
/** Documented default Redis url (local docker substrate). */
|
|
82
|
+
exports.DEFAULT_REDIS_URL = 'redis://localhost:6379';
|
|
83
|
+
/** Read a string from env with a fallback (trims; blank ⇒ fallback). */
|
|
84
|
+
function parseString(raw, fallback) {
|
|
85
|
+
if (raw === undefined || raw.trim() === '')
|
|
86
|
+
return fallback;
|
|
87
|
+
return raw.trim();
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Read an OPTIONAL positive-int env override, or `undefined` when absent/blank/
|
|
91
|
+
* invalid — so an omitted lifetime falls through to the core default rather than
|
|
92
|
+
* being coerced to a bogus number. (Distinct from {@link parsePositiveInt}, which
|
|
93
|
+
* always returns a concrete fallback for the pool budget.)
|
|
94
|
+
*/
|
|
95
|
+
function parseOptionalPositiveInt(raw) {
|
|
96
|
+
if (raw === undefined || raw.trim() === '')
|
|
97
|
+
return undefined;
|
|
98
|
+
const n = Number.parseInt(raw, 10);
|
|
99
|
+
return Number.isInteger(n) && n > 0 ? n : undefined;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Build the runtime {@link DbConfig} from env with documented defaults.
|
|
103
|
+
* Field ↔ env name:
|
|
104
|
+
* host ← SHIELD_DB_HOST port ← SHIELD_DB_PORT
|
|
105
|
+
* user ← SHIELD_DB_USER password ← SHIELD_DB_PASSWORD (secret)
|
|
106
|
+
* database ← SHIELD_DB_NAME
|
|
107
|
+
* The password defaults to '' when absent — a live connection then fails at
|
|
108
|
+
* connect time (loud), which is correct for a runtime that needs real creds; the
|
|
109
|
+
* secret is deliberately NOT hard-required at config load (unlike the HMAC key,
|
|
110
|
+
* which is a fail-closed security invariant even for unit tests).
|
|
111
|
+
*/
|
|
112
|
+
function loadDbConfig(env = process.env) {
|
|
113
|
+
return {
|
|
114
|
+
host: parseString(env.SHIELD_DB_HOST, exports.DEFAULT_DB_CONFIG.host),
|
|
115
|
+
port: parsePort(env.SHIELD_DB_PORT, exports.DEFAULT_DB_CONFIG.port),
|
|
116
|
+
user: parseString(env.SHIELD_DB_USER, exports.DEFAULT_DB_CONFIG.user),
|
|
117
|
+
password: parseString(env.SHIELD_DB_PASSWORD, ''),
|
|
118
|
+
database: parseString(env.SHIELD_DB_NAME, exports.DEFAULT_DB_CONFIG.database),
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Build the {@link RedisConfig} from env. Prefers `SHIELD_REDIS_URL`; falls back
|
|
123
|
+
* to composing `redis://host:port` from `SHIELD_REDIS_HOST`/`SHIELD_REDIS_PORT`,
|
|
124
|
+
* else the documented default.
|
|
125
|
+
*/
|
|
126
|
+
function loadRedisConfig(env = process.env) {
|
|
127
|
+
const explicit = env.SHIELD_REDIS_URL;
|
|
128
|
+
if (explicit !== undefined && explicit.trim() !== '') {
|
|
129
|
+
return { url: explicit.trim() };
|
|
130
|
+
}
|
|
131
|
+
const host = env.SHIELD_REDIS_HOST;
|
|
132
|
+
const port = env.SHIELD_REDIS_PORT;
|
|
133
|
+
if ((host !== undefined && host.trim() !== '') || (port !== undefined && port.trim() !== '')) {
|
|
134
|
+
const h = parseString(host, 'localhost');
|
|
135
|
+
const p = parsePort(port, 6379);
|
|
136
|
+
return { url: `redis://${h}:${p}` };
|
|
137
|
+
}
|
|
138
|
+
return { url: exports.DEFAULT_REDIS_URL };
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Build the optional {@link SessionLifetimeOverrides} from env. Every field is
|
|
142
|
+
* optional (omitted ⇒ core ADR-0015 default). The bounds validation is the
|
|
143
|
+
* core's `resolveLifetimes` job (throws LOUD at composition) — this loader only
|
|
144
|
+
* reads the numbers.
|
|
145
|
+
* Field ↔ env name:
|
|
146
|
+
* accessTtlMs ← SHIELD_ACCESS_TTL_MS
|
|
147
|
+
* refreshIdleMs ← SHIELD_REFRESH_IDLE_MS
|
|
148
|
+
* refreshAbsoluteCapMs ← SHIELD_REFRESH_ABSOLUTE_CAP_MS
|
|
149
|
+
*/
|
|
150
|
+
function loadSessionLifetimes(env = process.env) {
|
|
151
|
+
const overrides = {};
|
|
152
|
+
const access = parseOptionalPositiveInt(env.SHIELD_ACCESS_TTL_MS);
|
|
153
|
+
const idle = parseOptionalPositiveInt(env.SHIELD_REFRESH_IDLE_MS);
|
|
154
|
+
const cap = parseOptionalPositiveInt(env.SHIELD_REFRESH_ABSOLUTE_CAP_MS);
|
|
155
|
+
if (access !== undefined)
|
|
156
|
+
overrides.accessTtlMs = access;
|
|
157
|
+
if (idle !== undefined)
|
|
158
|
+
overrides.refreshIdleMs = idle;
|
|
159
|
+
if (cap !== undefined)
|
|
160
|
+
overrides.refreshAbsoluteCapMs = cap;
|
|
161
|
+
return overrides;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Read the refresh-token HMAC key from env (raw; `''` when absent/blank). Total —
|
|
165
|
+
* does NOT throw, so {@link loadAppConfig} stays boot-safe for a non-auth scaffold.
|
|
166
|
+
* The FAIL-CLOSED enforcement lives in {@link assertRefreshHmacKey}. Never logs
|
|
167
|
+
* the key's value.
|
|
168
|
+
*/
|
|
169
|
+
function loadRefreshHmacKey(env = process.env) {
|
|
170
|
+
return parseString(env.SHIELD_REFRESH_HMAC_KEY, '');
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* ⛔ FAIL-CLOSED GATE for the refresh-token HMAC key. Called by the auth
|
|
174
|
+
* composition (AuthModule / persistence factory) at the moment auth is wired:
|
|
175
|
+
* throws LOUD when the key is blank so the app can NEVER compose a working-looking
|
|
176
|
+
* authenticator/session-authority on an empty HMAC key (a leaked DB would then be
|
|
177
|
+
* offline-brute-forceable, and every refresh-token hash would be unkeyed). Mirrors
|
|
178
|
+
* `Sha256RefreshTokenHasher`'s constructor guard but surfaces a clear composition
|
|
179
|
+
* error. Returns the validated non-empty key. NEVER logs the key's value.
|
|
180
|
+
*
|
|
181
|
+
* WHY here and not in `loadAppConfig`: an enforcement-only app (example-backend, a
|
|
182
|
+
* freshly-scaffolded app with no auth wired) must still boot with no `.env`
|
|
183
|
+
* (`create-astram-app/template/_env.example` promises exactly this). Gating at the
|
|
184
|
+
* auth boundary keeps that promise while making auth fail closed the instant it is
|
|
185
|
+
* actually composed.
|
|
186
|
+
*/
|
|
187
|
+
function assertRefreshHmacKey(key) {
|
|
188
|
+
if (typeof key !== 'string' || key.trim() === '') {
|
|
189
|
+
throw new Error('Shield auth composition: SHIELD_REFRESH_HMAC_KEY is required and must be ' +
|
|
190
|
+
'non-empty (fail-closed — refusing to compose auth with a blank refresh-token ' +
|
|
191
|
+
'HMAC key). Set it from your secrets manager; never commit the value.');
|
|
192
|
+
}
|
|
193
|
+
return key;
|
|
194
|
+
}
|
|
64
195
|
/**
|
|
65
196
|
* Build the {@link AppConfig} from `process.env` (or an injected env bag, for
|
|
66
|
-
* testability). Pure
|
|
197
|
+
* testability). Pure, total, and side-effect-free — trivially unit-testable and
|
|
198
|
+
* boot-safe with NO `.env`.
|
|
199
|
+
*
|
|
200
|
+
* Every Phase 0b field has a documented default (DB/Redis descriptors, lifetimes)
|
|
201
|
+
* and `refreshHmacKey` is read-but-not-required HERE, so existing callers
|
|
202
|
+
* (example-backend / template `main.ts`, which read only `.port`) are unaffected.
|
|
203
|
+
* The refresh-key fail-closed check is deferred to {@link assertRefreshHmacKey} at
|
|
204
|
+
* auth-composition time (see its doc).
|
|
67
205
|
*/
|
|
68
206
|
function loadAppConfig(env = process.env) {
|
|
69
207
|
return {
|
|
70
208
|
port: parsePort(env.PORT, 3000),
|
|
71
209
|
environment: parseEnvironment(env.NODE_ENV),
|
|
72
210
|
pool: loadPoolBudget(env),
|
|
211
|
+
db: loadDbConfig(env),
|
|
212
|
+
redis: loadRedisConfig(env),
|
|
213
|
+
refreshHmacKey: loadRefreshHmacKey(env),
|
|
214
|
+
lifetimes: loadSessionLifetimes(env),
|
|
73
215
|
};
|
|
74
216
|
}
|
|
75
217
|
/**
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app-config.js","sourceRoot":"","sources":["../../src/config/app-config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.module.js","sourceRoot":"","sources":["../../src/config/config.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAgD;AAChD,mDAAgE;AAEhE,mDAAmD;AACtC,QAAA,UAAU,GAAG,MAAM,CAAC,YAAY,CAAC,CAAC;AAE/C;;;;;;GAMG;AAWI,IAAM,YAAY,GAAlB,MAAM,YAAY;CAAG,CAAA;AAAf,oCAAY;uBAAZ,YAAY;IAVxB,IAAA,eAAM,GAAE;IACR,IAAA,eAAM,EAAC;QACN,SAAS,EAAE;YACT;gBACE,OAAO,EAAE,kBAAU;gBACnB,UAAU,EAAE,GAAc,EAAE,CAAC,IAAA,6BAAa,GAAE;aAC7C;SACF;QACD,OAAO,EAAE,CAAC,kBAAU,CAAC;KACtB,CAAC;GACW,YAAY,CAAG"}
|
|
@@ -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;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"classification.decorator.js","sourceRoot":"","sources":["../../src/enforcement/classification.decorator.ts"],"names":[],"mappings":";;;AA4CA,0DAgBC;AA5DD,2CAA6C;AAE7C;;;;;;;;;;;;GAYG;AACU,QAAA,uBAAuB,GAAG;IACrC,kEAAkE;IAClE,MAAM,EAAE,8BAA8B;IACtC;;;;OAIG;IACH,UAAU,EAAE,kCAAkC;CACtC,CAAC;AAgBX,wFAAwF;AAC3E,QAAA,yBAAyB,GAAG,QAAQ,CAAC;AAElD,gFAAgF;AAChF,SAAgB,uBAAuB,CACrC,KAA0C;IAE1C,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,IAAI,CAAC;IACrC,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,OAAO,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,iCAAyB,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;IACzF,CAAC;IACD,IACE,OAAO,KAAK,CAAC,OAAO,KAAK,QAAQ;QACjC,KAAK,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC;QACxB,OAAO,KAAK,CAAC,MAAM,KAAK,QAAQ;QAChC,KAAK,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,EACvB,CAAC;QACD,OAAO,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC;IAC1D,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;;;GAOG;AACI,MAAM,MAAM,GAAG,GAAqC,EAAE,CAC3D,IAAA,oBAAW,EAAC,+BAAuB,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;AADvC,QAAA,MAAM,UACiC;AAEpD;;;;;;;;GAQG;AACI,MAAM,UAAU,GAAG,CACxB,cAAuC,EACL,EAAE,CACpC,IAAA,oBAAW,EAAC,+BAAuB,CAAC,UAAU,EAAE,cAAc,CAAC,CAAC;AAHrD,QAAA,UAAU,cAG2C"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"core-seam.js","sourceRoot":"","sources":["../../src/enforcement/core-seam.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;GAiBG;;;AAqBH;;;;;;;;;;;GAWG;AACU,QAAA,qBAAqB,GAA0B;IAC1D,MAAM;QACJ,OAAO,OAAO,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,iBAAiB,EAAE,CAAC,CAAC;IACxE,CAAC;CACF,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"enforcement.module.js","sourceRoot":"","sources":["../../src/enforcement/enforcement.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAwC;AACxC,uCAAyC;AACzC,+EAAuE;AAEvE;;;;;;;;;;;;;;GAcG;AASI,IAAM,iBAAiB,GAAvB,MAAM,iBAAiB;CAAG,CAAA;AAApB,8CAAiB;4BAAjB,iBAAiB;IAR7B,IAAA,eAAM,EAAC;QACN,SAAS,EAAE;YACT;gBACE,OAAO,EAAE,gBAAS;gBAClB,QAAQ,EAAE,oDAAsB;aACjC;SACF;KACF,CAAC;GACW,iBAAiB,CAAG"}
|
|
@@ -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 @@
|
|
|
1
|
+
{"version":3,"file":"global-enforcement.guard.js","sourceRoot":"","sources":["../../src/enforcement/global-enforcement.guard.ts"],"names":[],"mappings":"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"}
|