@astram/guard 0.12.1 → 0.14.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/connections.d.ts +19 -1
- package/dist/auth/connections.d.ts.map +1 -1
- package/dist/auth/connections.js +44 -1
- package/dist/config/app-config.d.ts +66 -0
- package/dist/config/app-config.d.ts.map +1 -1
- package/dist/config/app-config.js +112 -1
- package/dist/config/config.module.d.ts.map +1 -1
- package/dist/config/config.module.js +5 -1
- package/dist/config/loopback-host.d.ts +9 -0
- package/dist/config/loopback-host.d.ts.map +1 -0
- package/dist/config/loopback-host.js +19 -0
- package/dist/encryption/local-kek-crypto.d.ts +6 -0
- package/dist/encryption/local-kek-crypto.d.ts.map +1 -1
- package/dist/encryption/local-kek-crypto.js +8 -0
- package/dist/identity/identity-actions.d.ts +18 -7
- package/dist/identity/identity-actions.d.ts.map +1 -1
- package/dist/identity/identity-actions.js +16 -17
- package/dist/identity/module-registry.bridge.d.ts.map +1 -1
- package/dist/identity/module-registry.bridge.js +5 -3
- package/dist/index.d.ts +11 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +56 -21
- package/dist/module/shield-tokens.d.ts +11 -0
- package/dist/module/shield-tokens.d.ts.map +1 -1
- package/dist/module/shield-tokens.js +12 -1
- package/dist/module/shield.module.d.ts +10 -0
- package/dist/module/shield.module.d.ts.map +1 -1
- package/dist/module/shield.module.js +35 -5
- package/dist/secrets/boot-secrets.d.ts +33 -0
- package/dist/secrets/boot-secrets.d.ts.map +1 -0
- package/dist/secrets/boot-secrets.js +180 -0
- package/dist/storage/file-intake.interceptor.d.ts +54 -0
- package/dist/storage/file-intake.interceptor.d.ts.map +1 -0
- package/dist/storage/file-intake.interceptor.js +223 -0
- package/dist/storage/node-sha256-digest.d.ts +6 -0
- package/dist/storage/node-sha256-digest.d.ts.map +1 -0
- package/dist/storage/node-sha256-digest.js +15 -0
- package/dist/storage/object-store-boot-check.d.ts +16 -0
- package/dist/storage/object-store-boot-check.d.ts.map +1 -0
- package/dist/storage/object-store-boot-check.js +52 -0
- package/dist/storage/optional-peer.d.ts +10 -0
- package/dist/storage/optional-peer.d.ts.map +1 -0
- package/dist/storage/optional-peer.js +26 -0
- package/dist/storage/s3-object-store.d.ts +35 -0
- package/dist/storage/s3-object-store.d.ts.map +1 -0
- package/dist/storage/s3-object-store.js +238 -0
- package/dist/storage/storage.providers.d.ts +10 -0
- package/dist/storage/storage.providers.d.ts.map +1 -0
- package/dist/storage/storage.providers.js +104 -0
- package/package.json +11 -3
|
@@ -24,7 +24,8 @@
|
|
|
24
24
|
import { type OnModuleDestroy } from '@nestjs/common';
|
|
25
25
|
import { type Pool } from 'mariadb';
|
|
26
26
|
import Redis from 'ioredis';
|
|
27
|
-
import { type AppConfig } from '../config/app-config.js';
|
|
27
|
+
import { type AppConfig, type DbTlsConfig } from '../config/app-config.js';
|
|
28
|
+
import { isLoopbackHost } from '../config/loopback-host.js';
|
|
28
29
|
/**
|
|
29
30
|
* Create the ONE live `mariadb` {@link Pool} for the `shield_core` account from
|
|
30
31
|
* {@link AppConfig}. The `connectionLimit` comes from the ADR-0018 budget
|
|
@@ -37,6 +38,23 @@ import { type AppConfig } from '../config/app-config.js';
|
|
|
37
38
|
* documented defaults, so the descriptor is always well-formed.
|
|
38
39
|
*/
|
|
39
40
|
export declare function createDbPool(config: AppConfig): Pool;
|
|
41
|
+
export { isLoopbackHost };
|
|
42
|
+
/**
|
|
43
|
+
* The boot warning for a production pool to a remote MariaDB without TLS (Phase 16), or
|
|
44
|
+
* `undefined` when none applies. Warns only: production accounts are provisioned `REQUIRE SSL`,
|
|
45
|
+
* so the server refuses such a login anyway; the warning names the cause before that error.
|
|
46
|
+
* Names the host and the env var only, never the user or password.
|
|
47
|
+
*/
|
|
48
|
+
export declare function dbTlsBootWarning(config: Pick<AppConfig, 'db'>, env?: NodeJS.ProcessEnv): string | undefined;
|
|
49
|
+
/**
|
|
50
|
+
* The `mariadb` driver's `ssl` option for {@link DbTlsConfig} (Phase 16): `undefined` when off;
|
|
51
|
+
* otherwise TLS that always verifies the server, against the CA file when one is configured.
|
|
52
|
+
* Every framework pool uses this, so `APP_DB_TLS=on` reaches all of them.
|
|
53
|
+
*/
|
|
54
|
+
export declare function mariadbSslOption(tls: DbTlsConfig | undefined): {
|
|
55
|
+
readonly ca?: string;
|
|
56
|
+
readonly rejectUnauthorized: true;
|
|
57
|
+
} | undefined;
|
|
40
58
|
/**
|
|
41
59
|
* Create the ONE live `ioredis` client (the hot session tier, ADR-0013) from
|
|
42
60
|
* {@link AppConfig}. `config.redis.url` is the canonical `ioredis`-native form
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"connections.d.ts","sourceRoot":"","sources":["../../src/auth/connections.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;
|
|
1
|
+
{"version":3,"file":"connections.d.ts","sourceRoot":"","sources":["../../src/auth/connections.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAGH,OAAO,EAAsB,KAAK,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAC1E,OAAgB,EAAE,KAAK,IAAI,EAAE,MAAM,SAAS,CAAC;AAC7C,OAAO,KAAK,MAAM,SAAS,CAAC;AAC5B,OAAO,EAEL,KAAK,SAAS,EACd,KAAK,WAAW,EACjB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAE5D;;;;;;;;;;GAUG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAiBpD;AAMD,OAAO,EAAE,cAAc,EAAE,CAAC;AAE1B;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,EAC7B,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,MAAM,GAAG,SAAS,CASpB;AAOD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,WAAW,GAAG,SAAS,GAC3B;IAAE,QAAQ,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,kBAAkB,EAAE,IAAI,CAAA;CAAE,GAAG,SAAS,CAIzE;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,SAAS,GAAG,KAAK,CAgB1D;AAED;;;;;;GAMG;AACH,qBACa,mBAAoB,YAAW,eAAe;IAEvD,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,KAAK;gBADL,IAAI,EAAE,IAAI,EACV,KAAK,EAAE,KAAK;IAGzB,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC;CAavC"}
|
package/dist/auth/connections.js
CHANGED
|
@@ -35,13 +35,18 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
35
35
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
36
36
|
};
|
|
37
37
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
38
|
-
exports.ConnectionLifecycle = void 0;
|
|
38
|
+
exports.ConnectionLifecycle = exports.isLoopbackHost = void 0;
|
|
39
39
|
exports.createDbPool = createDbPool;
|
|
40
|
+
exports.dbTlsBootWarning = dbTlsBootWarning;
|
|
41
|
+
exports.mariadbSslOption = mariadbSslOption;
|
|
40
42
|
exports.createRedisClient = createRedisClient;
|
|
43
|
+
const node_fs_1 = require("node:fs");
|
|
41
44
|
const common_1 = require("@nestjs/common");
|
|
42
45
|
const mariadb_1 = __importDefault(require("mariadb"));
|
|
43
46
|
const ioredis_1 = __importDefault(require("ioredis"));
|
|
44
47
|
const app_config_js_1 = require("../config/app-config.js");
|
|
48
|
+
const loopback_host_js_1 = require("../config/loopback-host.js");
|
|
49
|
+
Object.defineProperty(exports, "isLoopbackHost", { enumerable: true, get: function () { return loopback_host_js_1.isLoopbackHost; } });
|
|
45
50
|
/**
|
|
46
51
|
* Create the ONE live `mariadb` {@link Pool} for the `shield_core` account from
|
|
47
52
|
* {@link AppConfig}. The `connectionLimit` comes from the ADR-0018 budget
|
|
@@ -55,6 +60,9 @@ const app_config_js_1 = require("../config/app-config.js");
|
|
|
55
60
|
*/
|
|
56
61
|
function createDbPool(config) {
|
|
57
62
|
const { db, pool } = config;
|
|
63
|
+
const tlsWarning = dbTlsBootWarning(config);
|
|
64
|
+
if (tlsWarning !== undefined)
|
|
65
|
+
dbBootLogger.warn(tlsWarning);
|
|
58
66
|
return mariadb_1.default.createPool({
|
|
59
67
|
host: db.host,
|
|
60
68
|
port: db.port,
|
|
@@ -66,8 +74,43 @@ function createDbPool(config) {
|
|
|
66
74
|
connectTimeout: 10_000,
|
|
67
75
|
// All platform timestamps are UTC (parity with the session adapters).
|
|
68
76
|
timezone: 'Z',
|
|
77
|
+
...withSsl(mariadbSslOption(db.tls)),
|
|
69
78
|
});
|
|
70
79
|
}
|
|
80
|
+
const dbBootLogger = new common_1.Logger('ShieldAuth');
|
|
81
|
+
/**
|
|
82
|
+
* The boot warning for a production pool to a remote MariaDB without TLS (Phase 16), or
|
|
83
|
+
* `undefined` when none applies. Warns only: production accounts are provisioned `REQUIRE SSL`,
|
|
84
|
+
* so the server refuses such a login anyway; the warning names the cause before that error.
|
|
85
|
+
* Names the host and the env var only, never the user or password.
|
|
86
|
+
*/
|
|
87
|
+
function dbTlsBootWarning(config, env = process.env) {
|
|
88
|
+
if (env.NODE_ENV !== 'production')
|
|
89
|
+
return undefined;
|
|
90
|
+
if ((config.db.tls?.mode ?? 'off') === 'on')
|
|
91
|
+
return undefined;
|
|
92
|
+
if ((0, loopback_host_js_1.isLoopbackHost)(config.db.host))
|
|
93
|
+
return undefined;
|
|
94
|
+
return (`APP_DB_TLS is off but APP_DB_HOST '${config.db.host}' is not loopback (NODE_ENV=production): ` +
|
|
95
|
+
'database traffic, credentials included, is sent in plaintext. Set APP_DB_TLS=on ' +
|
|
96
|
+
'(and APP_DB_TLS_CA if the server certificate is not publicly trusted).');
|
|
97
|
+
}
|
|
98
|
+
/** `{ ssl }` when TLS is on, `{}` when off (spread into a driver config). */
|
|
99
|
+
function withSsl(ssl) {
|
|
100
|
+
return ssl === undefined ? {} : { ssl };
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* The `mariadb` driver's `ssl` option for {@link DbTlsConfig} (Phase 16): `undefined` when off;
|
|
104
|
+
* otherwise TLS that always verifies the server, against the CA file when one is configured.
|
|
105
|
+
* Every framework pool uses this, so `APP_DB_TLS=on` reaches all of them.
|
|
106
|
+
*/
|
|
107
|
+
function mariadbSslOption(tls) {
|
|
108
|
+
if (tls === undefined || tls.mode === 'off')
|
|
109
|
+
return undefined;
|
|
110
|
+
if (tls.caPath === undefined)
|
|
111
|
+
return { rejectUnauthorized: true };
|
|
112
|
+
return { ca: (0, node_fs_1.readFileSync)(tls.caPath, 'utf8'), rejectUnauthorized: true };
|
|
113
|
+
}
|
|
71
114
|
/**
|
|
72
115
|
* Create the ONE live `ioredis` client (the hot session tier, ADR-0013) from
|
|
73
116
|
* {@link AppConfig}. `config.redis.url` is the canonical `ioredis`-native form
|
|
@@ -96,6 +96,21 @@ export interface DbConfig {
|
|
|
96
96
|
/** ⛔ Secret — resolved from env/secrets-manager; never logged. */
|
|
97
97
|
readonly password: string;
|
|
98
98
|
readonly database: string;
|
|
99
|
+
/**
|
|
100
|
+
* TLS to MariaDB (Phase 16, `APP_DB_TLS` / `APP_DB_TLS_CA`). Absent ⇒ off. Production accounts
|
|
101
|
+
* are provisioned `REQUIRE SSL` (`infra/mariadb/grants/matrix.json`), so production sets `on`.
|
|
102
|
+
*/
|
|
103
|
+
readonly tls?: DbTlsConfig;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* TLS to MariaDB. `on` always verifies the server: against `caPath` (a PEM file) when set, else
|
|
107
|
+
* the connector's default verification (MariaDB 11.4's auto-generated certificate is verified
|
|
108
|
+
* from the password exchange). There is deliberately no unverified mode.
|
|
109
|
+
*/
|
|
110
|
+
export interface DbTlsConfig {
|
|
111
|
+
readonly mode: 'off' | 'on';
|
|
112
|
+
/** PEM file of the CA that signed the server certificate (read when a pool is created). */
|
|
113
|
+
readonly caPath?: string;
|
|
99
114
|
}
|
|
100
115
|
/**
|
|
101
116
|
* The Redis connection descriptor. A single `url` (e.g. `redis://host:6379`) is
|
|
@@ -206,6 +221,14 @@ export declare const DEFAULT_REDIS_URL = "redis://localhost:6379";
|
|
|
206
221
|
* which is a fail-closed security invariant even for unit tests).
|
|
207
222
|
*/
|
|
208
223
|
export declare function loadDbConfig(env?: NodeJS.ProcessEnv): DbConfig;
|
|
224
|
+
/**
|
|
225
|
+
* `APP_DB_TLS` = `off` (default) | `on`; `APP_DB_TLS_CA` = PEM path (only with `on`). Anything
|
|
226
|
+
* else throws at boot: a typo must not silently fall back to plaintext.
|
|
227
|
+
*/
|
|
228
|
+
export declare function loadDbTlsConfig(env?: NodeJS.ProcessEnv, names?: {
|
|
229
|
+
readonly mode: string;
|
|
230
|
+
readonly ca: string;
|
|
231
|
+
}): DbTlsConfig;
|
|
209
232
|
/**
|
|
210
233
|
* Build the {@link RedisConfig} from env. Prefers `REDIS_URL`; falls back
|
|
211
234
|
* to composing `redis://host:port` from `REDIS_HOST`/`REDIS_PORT`, else the
|
|
@@ -287,6 +310,49 @@ export declare function loadAppConfig(env?: NodeJS.ProcessEnv): AppConfig;
|
|
|
287
310
|
* The core resolver (`resolveSecretRefs`) does the work; this is the guard-side boot seam.
|
|
288
311
|
*/
|
|
289
312
|
export declare function loadAppConfigAsync(provider?: SecretsProvider, env?: NodeJS.ProcessEnv): Promise<AppConfig>;
|
|
313
|
+
/** The S3-compatible object store descriptor read from `OBJECT_STORAGE_*`. */
|
|
314
|
+
export interface ObjectStorageConfig {
|
|
315
|
+
/** `https:` URL (or `http:` in development/test, or to a loopback host). Never carries userinfo. */
|
|
316
|
+
readonly endpoint: string;
|
|
317
|
+
readonly bucket: string;
|
|
318
|
+
/** `OBJECT_STORAGE_REGION`, default `us-east-1`. */
|
|
319
|
+
readonly region: string;
|
|
320
|
+
readonly accessKeyId: string;
|
|
321
|
+
readonly secretAccessKey: string;
|
|
322
|
+
/** Server-side encryption mode. Only SSE-S3 (`AES256`) is accepted now. */
|
|
323
|
+
readonly sse: 'AES256';
|
|
324
|
+
/** `OBJECT_STORAGE_MAX_BYTES` (default 10 MiB), at most `FILE_MAX_BYTES_CEILING` (50 MiB). */
|
|
325
|
+
readonly maxBytes: number;
|
|
326
|
+
}
|
|
327
|
+
/** Options for {@link loadObjectStorageConfig}. */
|
|
328
|
+
export interface LoadObjectStorageConfigOptions {
|
|
329
|
+
/**
|
|
330
|
+
* Provenance check for the two credential variables outside development/test (SPEC §5 F: a
|
|
331
|
+
* literal credential there is a boot error). Returns `true` when the variable's value was
|
|
332
|
+
* resolved from a `secret-ref:` at boot (`wasResolvedFromSecretRef`). Omitted outside
|
|
333
|
+
* development/test: fail closed, every credential is treated as a literal and refused (the
|
|
334
|
+
* value alone cannot tell a literal from a resolved reference).
|
|
335
|
+
*/
|
|
336
|
+
readonly resolvedFromSecretRef?: (envKey: string, env: Readonly<Record<string, string | undefined>>) => boolean;
|
|
337
|
+
}
|
|
338
|
+
/** Default per-file cap when `OBJECT_STORAGE_MAX_BYTES` is unset (10 MiB, SPEC §10). */
|
|
339
|
+
export declare const DEFAULT_OBJECT_STORAGE_MAX_BYTES: number;
|
|
340
|
+
/**
|
|
341
|
+
* Read the object-storage config (FILE_STORAGE SPEC §5 F). Returns `undefined` when
|
|
342
|
+
* `OBJECT_STORAGE_BUCKET` is unset or blank (storage off). Otherwise every bad value is a boot
|
|
343
|
+
* error that names the variable and never its value (Shield #1, #10):
|
|
344
|
+
* - endpoint or either credential missing/blank;
|
|
345
|
+
* - endpoint not a URL, carrying userinfo, or not `https:` outside development/test unless the
|
|
346
|
+
* host is loopback (the raw `NODE_ENV`; unset is not dev);
|
|
347
|
+
* - a credential still holding an unresolved `secret-ref:` value;
|
|
348
|
+
* - outside development/test, a credential not resolved from a `secret-ref:` per
|
|
349
|
+
* `options.resolvedFromSecretRef` (`ShieldModule.forRoot({ storage: 'env' })` passes
|
|
350
|
+
* `wasResolvedFromSecretRef`); without the option every credential there is refused;
|
|
351
|
+
* - `OBJECT_STORAGE_SSE` other than `AES256`;
|
|
352
|
+
* - `OBJECT_STORAGE_MAX_BYTES` not a plain positive decimal without leading zeros, or above the
|
|
353
|
+
* 50 MiB ceiling.
|
|
354
|
+
*/
|
|
355
|
+
export declare function loadObjectStorageConfig(env?: NodeJS.ProcessEnv, options?: LoadObjectStorageConfigOptions): ObjectStorageConfig | undefined;
|
|
290
356
|
/** Result of evaluating a {@link PoolBudgetConfig} against the ADR-0018 budget. */
|
|
291
357
|
export interface PoolBudgetEvaluation {
|
|
292
358
|
/** Σ(per-account runtime pool max) for a single worker. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app-config.d.ts","sourceRoot":"","sources":["../../src/config/app-config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"app-config.d.ts","sourceRoot":"","sources":["../../src/config/app-config.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.DEFAULT_REDIS_URL = exports.DEFAULT_DB_CONFIG = exports.SCHEMA_SVC_POOL_HARD_CAP = exports.DEFAULT_POOL_BUDGET = exports.DEFAULT_REDIS_COMMAND_TIMEOUT_MS = exports.DEFAULT_LOGIN_IP_LIMIT = void 0;
|
|
3
|
+
exports.DEFAULT_OBJECT_STORAGE_MAX_BYTES = exports.DEFAULT_REDIS_URL = exports.DEFAULT_DB_CONFIG = exports.SCHEMA_SVC_POOL_HARD_CAP = exports.DEFAULT_POOL_BUDGET = exports.DEFAULT_REDIS_COMMAND_TIMEOUT_MS = exports.DEFAULT_LOGIN_IP_LIMIT = void 0;
|
|
4
4
|
exports.isDevOrTestNodeEnv = isDevOrTestNodeEnv;
|
|
5
5
|
exports.loadPoolBudget = loadPoolBudget;
|
|
6
6
|
exports.loadDbConfig = loadDbConfig;
|
|
7
|
+
exports.loadDbTlsConfig = loadDbTlsConfig;
|
|
7
8
|
exports.loadRedisConfig = loadRedisConfig;
|
|
8
9
|
exports.loadSessionLifetimes = loadSessionLifetimes;
|
|
9
10
|
exports.loadLoginIpLimit = loadLoginIpLimit;
|
|
@@ -12,8 +13,10 @@ exports.loadRefreshHmacKey = loadRefreshHmacKey;
|
|
|
12
13
|
exports.assertRefreshHmacKey = assertRefreshHmacKey;
|
|
13
14
|
exports.loadAppConfig = loadAppConfig;
|
|
14
15
|
exports.loadAppConfigAsync = loadAppConfigAsync;
|
|
16
|
+
exports.loadObjectStorageConfig = loadObjectStorageConfig;
|
|
15
17
|
exports.evaluatePoolBudget = evaluatePoolBudget;
|
|
16
18
|
const core_1 = require("@astram/core");
|
|
19
|
+
const loopback_host_js_1 = require("./loopback-host.js");
|
|
17
20
|
/** Plan D3 default: 50 failed logins per client IP per 15 minutes. */
|
|
18
21
|
exports.DEFAULT_LOGIN_IP_LIMIT = {
|
|
19
22
|
maxAttempts: 50,
|
|
@@ -144,8 +147,27 @@ function loadDbConfig(env = process.env) {
|
|
|
144
147
|
user: parseString(env.APP_DB_USER, exports.DEFAULT_DB_CONFIG.user),
|
|
145
148
|
password: parseString(env.APP_DB_PASSWORD, ''),
|
|
146
149
|
database: parseString(env.APP_DB_NAME, exports.DEFAULT_DB_CONFIG.database),
|
|
150
|
+
tls: loadDbTlsConfig(env),
|
|
147
151
|
};
|
|
148
152
|
}
|
|
153
|
+
/**
|
|
154
|
+
* `APP_DB_TLS` = `off` (default) | `on`; `APP_DB_TLS_CA` = PEM path (only with `on`). Anything
|
|
155
|
+
* else throws at boot: a typo must not silently fall back to plaintext.
|
|
156
|
+
*/
|
|
157
|
+
function loadDbTlsConfig(env = process.env, names = {
|
|
158
|
+
mode: 'APP_DB_TLS',
|
|
159
|
+
ca: 'APP_DB_TLS_CA',
|
|
160
|
+
}) {
|
|
161
|
+
const raw = parseString(env[names.mode], 'off').toLowerCase();
|
|
162
|
+
if (raw !== 'off' && raw !== 'on') {
|
|
163
|
+
throw new Error(`${names.mode} must be 'on' or 'off', got '${env[names.mode]}'.`);
|
|
164
|
+
}
|
|
165
|
+
const caPath = parseString(env[names.ca], '');
|
|
166
|
+
if (caPath !== '' && raw === 'off') {
|
|
167
|
+
throw new Error(`${names.ca} is set but ${names.mode} is off: set ${names.mode}=on.`);
|
|
168
|
+
}
|
|
169
|
+
return raw === 'on' && caPath !== '' ? { mode: 'on', caPath } : { mode: raw };
|
|
170
|
+
}
|
|
149
171
|
/**
|
|
150
172
|
* Build the {@link RedisConfig} from env. Prefers `REDIS_URL`; falls back
|
|
151
173
|
* to composing `redis://host:port` from `REDIS_HOST`/`REDIS_PORT`, else the
|
|
@@ -290,6 +312,95 @@ async function loadAppConfigAsync(provider, env = process.env) {
|
|
|
290
312
|
const resolved = await (0, core_1.resolveSecretRefs)(env, provider);
|
|
291
313
|
return loadAppConfig(resolved);
|
|
292
314
|
}
|
|
315
|
+
/** Default per-file cap when `OBJECT_STORAGE_MAX_BYTES` is unset (10 MiB, SPEC §10). */
|
|
316
|
+
exports.DEFAULT_OBJECT_STORAGE_MAX_BYTES = 10 * 1024 * 1024;
|
|
317
|
+
/**
|
|
318
|
+
* Read the object-storage config (FILE_STORAGE SPEC §5 F). Returns `undefined` when
|
|
319
|
+
* `OBJECT_STORAGE_BUCKET` is unset or blank (storage off). Otherwise every bad value is a boot
|
|
320
|
+
* error that names the variable and never its value (Shield #1, #10):
|
|
321
|
+
* - endpoint or either credential missing/blank;
|
|
322
|
+
* - endpoint not a URL, carrying userinfo, or not `https:` outside development/test unless the
|
|
323
|
+
* host is loopback (the raw `NODE_ENV`; unset is not dev);
|
|
324
|
+
* - a credential still holding an unresolved `secret-ref:` value;
|
|
325
|
+
* - outside development/test, a credential not resolved from a `secret-ref:` per
|
|
326
|
+
* `options.resolvedFromSecretRef` (`ShieldModule.forRoot({ storage: 'env' })` passes
|
|
327
|
+
* `wasResolvedFromSecretRef`); without the option every credential there is refused;
|
|
328
|
+
* - `OBJECT_STORAGE_SSE` other than `AES256`;
|
|
329
|
+
* - `OBJECT_STORAGE_MAX_BYTES` not a plain positive decimal without leading zeros, or above the
|
|
330
|
+
* 50 MiB ceiling.
|
|
331
|
+
*/
|
|
332
|
+
function loadObjectStorageConfig(env = process.env, options = {}) {
|
|
333
|
+
const bucket = env.OBJECT_STORAGE_BUCKET?.trim();
|
|
334
|
+
if (!bucket)
|
|
335
|
+
return undefined;
|
|
336
|
+
const devOrTest = isDevOrTestNodeEnv(env.NODE_ENV);
|
|
337
|
+
const endpoint = requireStorageVar(env, 'OBJECT_STORAGE_ENDPOINT');
|
|
338
|
+
let url;
|
|
339
|
+
try {
|
|
340
|
+
url = new URL(endpoint);
|
|
341
|
+
}
|
|
342
|
+
catch {
|
|
343
|
+
throw new Error('OBJECT_STORAGE_ENDPOINT is not a valid URL');
|
|
344
|
+
}
|
|
345
|
+
if (url.protocol !== 'https:' && url.protocol !== 'http:') {
|
|
346
|
+
throw new Error('OBJECT_STORAGE_ENDPOINT must be an https: (or local http:) URL');
|
|
347
|
+
}
|
|
348
|
+
if (url.username !== '' || url.password !== '') {
|
|
349
|
+
throw new Error('OBJECT_STORAGE_ENDPOINT must not carry credentials (use OBJECT_STORAGE_ACCESS_KEY_ID / ' +
|
|
350
|
+
'OBJECT_STORAGE_SECRET_ACCESS_KEY)');
|
|
351
|
+
}
|
|
352
|
+
if (url.protocol === 'http:' && !devOrTest && !(0, loopback_host_js_1.isLoopbackHost)(url.hostname)) {
|
|
353
|
+
throw new Error('OBJECT_STORAGE_ENDPOINT must use https: outside NODE_ENV development/test ' +
|
|
354
|
+
'(plain http: is allowed only to a loopback host)');
|
|
355
|
+
}
|
|
356
|
+
const credential = (name) => {
|
|
357
|
+
const value = requireStorageVar(env, name);
|
|
358
|
+
// `startsWith` via the core predicate on a widened copy: the predicate is a type guard.
|
|
359
|
+
if ((0, core_1.isSecretRef)(value)) {
|
|
360
|
+
throw new Error(`${name} still holds an unresolved secret-ref: value; call resolveBootSecrets() in main.ts ` +
|
|
361
|
+
'before loading AppModule');
|
|
362
|
+
}
|
|
363
|
+
// Fail closed: without a provenance check every credential counts as a literal here.
|
|
364
|
+
if (!devOrTest && !(options.resolvedFromSecretRef?.(name, env) === true)) {
|
|
365
|
+
throw new Error(`${name} must be a secret-ref: value outside NODE_ENV development/test ` +
|
|
366
|
+
'(literal credentials are refused; see ADR-0032)');
|
|
367
|
+
}
|
|
368
|
+
return value;
|
|
369
|
+
};
|
|
370
|
+
const accessKeyId = credential('OBJECT_STORAGE_ACCESS_KEY_ID');
|
|
371
|
+
const secretAccessKey = credential('OBJECT_STORAGE_SECRET_ACCESS_KEY');
|
|
372
|
+
const sse = env.OBJECT_STORAGE_SSE?.trim() || 'AES256';
|
|
373
|
+
if (sse !== 'AES256') {
|
|
374
|
+
throw new Error("OBJECT_STORAGE_SSE must be 'AES256' (SSE-S3); other modes are not supported");
|
|
375
|
+
}
|
|
376
|
+
return {
|
|
377
|
+
endpoint,
|
|
378
|
+
bucket,
|
|
379
|
+
region: env.OBJECT_STORAGE_REGION?.trim() || 'us-east-1',
|
|
380
|
+
accessKeyId,
|
|
381
|
+
secretAccessKey,
|
|
382
|
+
sse,
|
|
383
|
+
maxBytes: parseStorageMaxBytes(env.OBJECT_STORAGE_MAX_BYTES),
|
|
384
|
+
};
|
|
385
|
+
}
|
|
386
|
+
function requireStorageVar(env, name) {
|
|
387
|
+
const value = env[name]?.trim();
|
|
388
|
+
if (!value)
|
|
389
|
+
throw new Error(`${name} must be set when OBJECT_STORAGE_BUCKET is set`);
|
|
390
|
+
return value;
|
|
391
|
+
}
|
|
392
|
+
function parseStorageMaxBytes(raw) {
|
|
393
|
+
const value = raw?.trim();
|
|
394
|
+
if (!value)
|
|
395
|
+
return exports.DEFAULT_OBJECT_STORAGE_MAX_BYTES;
|
|
396
|
+
// Plain decimal digits, no sign, exponent, separators or leading zeros.
|
|
397
|
+
const n = /^[1-9]\d*$/.test(value) ? Number(value) : NaN;
|
|
398
|
+
if (!Number.isSafeInteger(n) || n <= 0 || n > core_1.FILE_MAX_BYTES_CEILING) {
|
|
399
|
+
throw new Error(`OBJECT_STORAGE_MAX_BYTES must be a positive decimal integer (no leading zeros) of at most ` +
|
|
400
|
+
`${core_1.FILE_MAX_BYTES_CEILING} bytes`);
|
|
401
|
+
}
|
|
402
|
+
return n;
|
|
403
|
+
}
|
|
293
404
|
/**
|
|
294
405
|
* Evaluate a pool budget. Pure: no throwing, no I/O — the caller decides how to
|
|
295
406
|
* react (the boot assertion throws; tests inspect the fields).
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.module.d.ts","sourceRoot":"","sources":["../../src/config/config.module.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"config.module.d.ts","sourceRoot":"","sources":["../../src/config/config.module.ts"],"names":[],"mappings":"AAIA,mDAAmD;AACnD,eAAO,MAAM,UAAU,eAAuB,CAAC;AAE/C;;;;;;GAMG;AACH,qBAaa,YAAY;CAAG"}
|
|
@@ -9,6 +9,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
9
9
|
exports.ConfigModule = exports.APP_CONFIG = void 0;
|
|
10
10
|
const common_1 = require("@nestjs/common");
|
|
11
11
|
const app_config_js_1 = require("./app-config.js");
|
|
12
|
+
const boot_secrets_js_1 = require("../secrets/boot-secrets.js");
|
|
12
13
|
/** DI token for the resolved {@link AppConfig}. */
|
|
13
14
|
exports.APP_CONFIG = Symbol('APP_CONFIG');
|
|
14
15
|
/**
|
|
@@ -27,7 +28,10 @@ exports.ConfigModule = ConfigModule = __decorate([
|
|
|
27
28
|
providers: [
|
|
28
29
|
{
|
|
29
30
|
provide: exports.APP_CONFIG,
|
|
30
|
-
useFactory: () =>
|
|
31
|
+
useFactory: () => {
|
|
32
|
+
(0, boot_secrets_js_1.assertNoUnresolvedSecretRefs)(process.env);
|
|
33
|
+
return (0, app_config_js_1.loadAppConfig)();
|
|
34
|
+
},
|
|
31
35
|
},
|
|
32
36
|
],
|
|
33
37
|
exports: [exports.APP_CONFIG],
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Loopback host predicate, shared by the DB TLS boot warning (`auth/connections.ts`) and the
|
|
3
|
+
* object-storage endpoint check (`loadObjectStorageConfig`). Kept dependency-free here so
|
|
4
|
+
* `app-config.ts` can use it without importing the driver-loading connections module (which
|
|
5
|
+
* itself imports `app-config.ts`).
|
|
6
|
+
*/
|
|
7
|
+
/** `localhost`, `127.0.0.0/8` or `::1` (any case, with or without IPv6 brackets). */
|
|
8
|
+
export declare function isLoopbackHost(host: string): boolean;
|
|
9
|
+
//# sourceMappingURL=loopback-host.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loopback-host.d.ts","sourceRoot":"","sources":["../../src/config/loopback-host.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,qFAAqF;AACrF,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAOpD"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Loopback host predicate, shared by the DB TLS boot warning (`auth/connections.ts`) and the
|
|
4
|
+
* object-storage endpoint check (`loadObjectStorageConfig`). Kept dependency-free here so
|
|
5
|
+
* `app-config.ts` can use it without importing the driver-loading connections module (which
|
|
6
|
+
* itself imports `app-config.ts`).
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.isLoopbackHost = isLoopbackHost;
|
|
10
|
+
/** `localhost`, `127.0.0.0/8` or `::1` (any case, with or without IPv6 brackets). */
|
|
11
|
+
function isLoopbackHost(host) {
|
|
12
|
+
const h = host
|
|
13
|
+
.trim()
|
|
14
|
+
.toLowerCase()
|
|
15
|
+
.replace(/^\[(.*)\]$/, '$1');
|
|
16
|
+
if (h === 'localhost' || h === '::1' || h === '0:0:0:0:0:0:0:1')
|
|
17
|
+
return true;
|
|
18
|
+
return /^127\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(h);
|
|
19
|
+
}
|
|
@@ -9,6 +9,12 @@ export declare class LocalKekCrypto implements CryptoPort {
|
|
|
9
9
|
generateDek(): Promise<Dek>;
|
|
10
10
|
wrapDek(dek: Dek, tenantId: string): Promise<WrappedDek>;
|
|
11
11
|
unwrapDek(wrapped: WrappedDek, tenantId: string): Promise<Dek>;
|
|
12
|
+
/**
|
|
13
|
+
* Synchronous {@link unwrapDek} (same AEAD, same AAD binding) for boot paths that must fail
|
|
14
|
+
* closed before any async work runs (`createSecretsProviderFromEnv`). Throws on a wrong KEK,
|
|
15
|
+
* a wrong AAD or a malformed blob; never includes key material in the error.
|
|
16
|
+
*/
|
|
17
|
+
unwrapDekSync(wrapped: WrappedDek, tenantId: string): Dek;
|
|
12
18
|
encrypt(plaintext: Uint8Array, dek: Dek, tenantId: string): Promise<Ciphertext>;
|
|
13
19
|
decrypt(ciphertext: Ciphertext, dek: Dek, tenantId: string): Promise<Uint8Array>;
|
|
14
20
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"local-kek-crypto.d.ts","sourceRoot":"","sources":["../../src/encryption/local-kek-crypto.ts"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EAAE,UAAU,EAAE,GAAG,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAO5E,qBAAa,cAAe,YAAW,UAAU;;IAG/C;;;OAGG;gBACS,GAAG,EAAE,UAAU;IAOrB,WAAW,IAAI,OAAO,CAAC,GAAG,CAAC;IAI3B,OAAO,CAAC,GAAG,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAIxD,SAAS,CAAC,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC;
|
|
1
|
+
{"version":3,"file":"local-kek-crypto.d.ts","sourceRoot":"","sources":["../../src/encryption/local-kek-crypto.ts"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EAAE,UAAU,EAAE,GAAG,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAO5E,qBAAa,cAAe,YAAW,UAAU;;IAG/C;;;OAGG;gBACS,GAAG,EAAE,UAAU;IAOrB,WAAW,IAAI,OAAO,CAAC,GAAG,CAAC;IAI3B,OAAO,CAAC,GAAG,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAIxD,SAAS,CAAC,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC;IAIpE;;;;OAIG;IACH,aAAa,CAAC,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,GAAG,GAAG;IAInD,OAAO,CAAC,SAAS,EAAE,UAAU,EAAE,GAAG,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAI/E,OAAO,CAAC,UAAU,EAAE,UAAU,EAAE,GAAG,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;CA2BvF"}
|
|
@@ -44,6 +44,14 @@ class LocalKekCrypto {
|
|
|
44
44
|
return this.#seal(this.#kek, Buffer.from(dek), tenantId);
|
|
45
45
|
}
|
|
46
46
|
async unwrapDek(wrapped, tenantId) {
|
|
47
|
+
return this.unwrapDekSync(wrapped, tenantId);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Synchronous {@link unwrapDek} (same AEAD, same AAD binding) for boot paths that must fail
|
|
51
|
+
* closed before any async work runs (`createSecretsProviderFromEnv`). Throws on a wrong KEK,
|
|
52
|
+
* a wrong AAD or a malformed blob; never includes key material in the error.
|
|
53
|
+
*/
|
|
54
|
+
unwrapDekSync(wrapped, tenantId) {
|
|
47
55
|
return new Uint8Array(this.#open(this.#kek, Buffer.from(wrapped), tenantId));
|
|
48
56
|
}
|
|
49
57
|
async encrypt(plaintext, dek, tenantId) {
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* type uses the CRUD verbs.)
|
|
2
|
+
* Module key + action-vocabulary constants for the framework's identity registry. Permission
|
|
3
|
+
* keys are `identity:<typeKey>:<action>`. A type's vocabulary is declared on its engine
|
|
4
|
+
* ObjectType (`actions` / `crudActions`, ADR-0038) and read through {@link actionsOf}; the
|
|
5
|
+
* constants below stay exported as the single literal each identity object declares.
|
|
7
6
|
*/
|
|
7
|
+
import type { ObjectType } from '@astram/engine';
|
|
8
8
|
export declare const MODULE_KEY = "identity";
|
|
9
9
|
export declare const IDENTITY_ACTIONS: readonly ["read", "create", "update", "delete"];
|
|
10
10
|
export type IdentityAction = (typeof IDENTITY_ACTIONS)[number];
|
|
@@ -33,6 +33,17 @@ export type RecordShareAction = (typeof RECORD_SHARE_ACTIONS)[number];
|
|
|
33
33
|
*/
|
|
34
34
|
export declare const SERVICE_ACCOUNT_ACTIONS: readonly ["read", "create", "update", "delete", "manage_keys"];
|
|
35
35
|
export type ServiceAccountAction = (typeof SERVICE_ACCOUNT_ACTIONS)[number];
|
|
36
|
-
/**
|
|
37
|
-
|
|
36
|
+
/**
|
|
37
|
+
* The action vocabulary declared on a registration (ADR-0038): the CRUD verbs then the declared
|
|
38
|
+
* extras, or exactly the declared list when `crudActions === false`. Delegates to engine
|
|
39
|
+
* `effectiveActions`, so an invalid declaration throws the same `RegistrationError`.
|
|
40
|
+
*/
|
|
41
|
+
export declare function actionsOf(reg: Pick<ObjectType, 'actions' | 'crudActions'>): readonly string[];
|
|
42
|
+
/**
|
|
43
|
+
* @deprecated Use {@link actionsOf} with the type's registration. The vocabulary now lives on
|
|
44
|
+
* the object type (ADR-0038), so a typeKey alone cannot name it: this returns the CRUD verbs
|
|
45
|
+
* ({@link IDENTITY_ACTIONS}) for every type, including `cross_tenant_grant`, `record_share` and
|
|
46
|
+
* `service_account`. The framework no longer calls it.
|
|
47
|
+
*/
|
|
48
|
+
export declare function actionsFor(_typeKey: string): readonly string[];
|
|
38
49
|
//# sourceMappingURL=identity-actions.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"identity-actions.d.ts","sourceRoot":"","sources":["../../src/identity/identity-actions.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"identity-actions.d.ts","sourceRoot":"","sources":["../../src/identity/identity-actions.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAGjD,eAAO,MAAM,UAAU,aAAa,CAAC;AACrC,eAAO,MAAM,gBAAgB,iDAAkD,CAAC;AAChF,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,gBAAgB,CAAC,CAAC,MAAM,CAAC,CAAC;AAE/D;;;;;;GAMG;AACH,eAAO,MAAM,0BAA0B,wCAAyC,CAAC;AACjF,MAAM,MAAM,sBAAsB,GAAG,CAAC,OAAO,0BAA0B,CAAC,CAAC,MAAM,CAAC,CAAC;AAEjF;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,4BAA6B,CAAC;AAC/D,MAAM,MAAM,iBAAiB,GAAG,CAAC,OAAO,oBAAoB,CAAC,CAAC,MAAM,CAAC,CAAC;AAEtE;;;;;;GAMG;AACH,eAAO,MAAM,uBAAuB,gEAM1B,CAAC;AACX,MAAM,MAAM,oBAAoB,GAAG,CAAC,OAAO,uBAAuB,CAAC,CAAC,MAAM,CAAC,CAAC;AAE5E;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,IAAI,CAAC,UAAU,EAAE,SAAS,GAAG,aAAa,CAAC,GAAG,SAAS,MAAM,EAAE,CAE7F;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,CAE9D"}
|
|
@@ -1,14 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.SERVICE_ACCOUNT_ACTIONS = exports.RECORD_SHARE_ACTIONS = exports.CROSS_TENANT_GRANT_ACTIONS = exports.IDENTITY_ACTIONS = exports.MODULE_KEY = void 0;
|
|
4
|
+
exports.actionsOf = actionsOf;
|
|
4
5
|
exports.actionsFor = actionsFor;
|
|
5
|
-
|
|
6
|
-
* The fixed action vocabulary + module key for the framework's @astram/identity objects.
|
|
7
|
-
* Engine ObjectTypes carry no action list, so actions are synthesized here — the ONE source
|
|
8
|
-
* the module-registry bridge derives from. Permission keys are `identity:<typeKey>:<action>`.
|
|
9
|
-
* (Ported from astram3. `cross_tenant_grant` gets its own override — see below — every other
|
|
10
|
-
* type uses the CRUD verbs.)
|
|
11
|
-
*/
|
|
6
|
+
const engine_1 = require("@astram/engine");
|
|
12
7
|
exports.MODULE_KEY = 'identity';
|
|
13
8
|
exports.IDENTITY_ACTIONS = ['read', 'create', 'update', 'delete'];
|
|
14
9
|
/**
|
|
@@ -40,15 +35,19 @@ exports.SERVICE_ACCOUNT_ACTIONS = [
|
|
|
40
35
|
'manage_keys',
|
|
41
36
|
];
|
|
42
37
|
/**
|
|
43
|
-
*
|
|
44
|
-
*
|
|
38
|
+
* The action vocabulary declared on a registration (ADR-0038): the CRUD verbs then the declared
|
|
39
|
+
* extras, or exactly the declared list when `crudActions === false`. Delegates to engine
|
|
40
|
+
* `effectiveActions`, so an invalid declaration throws the same `RegistrationError`.
|
|
41
|
+
*/
|
|
42
|
+
function actionsOf(reg) {
|
|
43
|
+
return (0, engine_1.effectiveActions)(reg);
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* @deprecated Use {@link actionsOf} with the type's registration. The vocabulary now lives on
|
|
47
|
+
* the object type (ADR-0038), so a typeKey alone cannot name it: this returns the CRUD verbs
|
|
48
|
+
* ({@link IDENTITY_ACTIONS}) for every type, including `cross_tenant_grant`, `record_share` and
|
|
49
|
+
* `service_account`. The framework no longer calls it.
|
|
45
50
|
*/
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
record_share: exports.RECORD_SHARE_ACTIONS,
|
|
49
|
-
service_account: exports.SERVICE_ACCOUNT_ACTIONS,
|
|
50
|
-
};
|
|
51
|
-
/** The action vocabulary for `typeKey` — the per-type override if one exists, else the default. */
|
|
52
|
-
function actionsFor(typeKey) {
|
|
53
|
-
return ACTIONS_BY_TYPE[typeKey] ?? exports.IDENTITY_ACTIONS;
|
|
51
|
+
function actionsFor(_typeKey) {
|
|
52
|
+
return exports.IDENTITY_ACTIONS;
|
|
54
53
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"module-registry.bridge.d.ts","sourceRoot":"","sources":["../../src/identity/module-registry.bridge.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"module-registry.bridge.d.ts","sourceRoot":"","sources":["../../src/identity/module-registry.bridge.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EACV,cAAc,EAIf,MAAM,cAAc,CAAC;AACtB,OAAO,KAAK,EAAc,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAQlE,wBAAgB,2BAA2B,CAAC,MAAM,EAAE,SAAS,eAAe,EAAE,GAAG,cAAc,CA4B9F"}
|
|
@@ -4,8 +4,10 @@ exports.buildIdentityModuleRegistry = buildIdentityModuleRegistry;
|
|
|
4
4
|
/**
|
|
5
5
|
* Bridge: engine ObjectTypes → a populated core ModuleRegistry. Ported from astram3 (framework
|
|
6
6
|
* now owns identity). Engine ObjectTypes describe physical tables; core's gates + the
|
|
7
|
-
* permission-facts projection need object types + ACTIONS + tiers + planes.
|
|
8
|
-
*
|
|
7
|
+
* permission-facts projection need object types + ACTIONS + tiers + planes. Each type's actions
|
|
8
|
+
* are its declared vocabulary (`actionsOf` = engine `effectiveActions`, ADR-0038): CRUD plus
|
|
9
|
+
* extras, or exactly the declared list for `crudActions: false`; an invalid declaration throws
|
|
10
|
+
* `RegistrationError` here. Every action is `gated` with the type's `access.plane` — nothing
|
|
9
11
|
* open unless reclassified. `register()` throws on duplicate typeKey/permissionKey.
|
|
10
12
|
*/
|
|
11
13
|
const core_1 = require("@astram/core");
|
|
@@ -30,7 +32,7 @@ function buildIdentityModuleRegistry(inputs) {
|
|
|
30
32
|
...(Object.keys(access).length > 0 ? { access } : {}),
|
|
31
33
|
};
|
|
32
34
|
});
|
|
33
|
-
const actions = regs.flatMap((r) => (0, identity_actions_js_1.
|
|
35
|
+
const actions = regs.flatMap((r) => (0, identity_actions_js_1.actionsOf)(r).map((actionKey) => ({
|
|
34
36
|
actionKey,
|
|
35
37
|
typeKey: r.typeKey,
|
|
36
38
|
tier: 'gated',
|
package/dist/index.d.ts
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
*/
|
|
17
17
|
export { guardMigrationsDir } from './migrations-dir.js';
|
|
18
18
|
export { buildIdentityModuleRegistry } from './identity/module-registry.bridge.js';
|
|
19
|
-
export { MODULE_KEY, IDENTITY_ACTIONS, actionsFor } from './identity/identity-actions.js';
|
|
19
|
+
export { MODULE_KEY, IDENTITY_ACTIONS, CROSS_TENANT_GRANT_ACTIONS, RECORD_SHARE_ACTIONS, SERVICE_ACCOUNT_ACTIONS, actionsFor, actionsOf, } from './identity/identity-actions.js';
|
|
20
20
|
export { ShieldModule, SHIELD_DECISION_AUTHORITY, SHIELD_RECORD_AUTHORIZATION, type ShieldModuleOptions, type ShieldModuleAsyncOptions, type RecordAuthorizationInputs, } from './module/shield.module.js';
|
|
21
21
|
export { Public, Classified, CLASSIFICATION_METADATA, DEFAULT_CLASSIFIED_ACTION, normalizeClassification, } from './enforcement/classification.decorator.js';
|
|
22
22
|
export type { Classification } from './enforcement/classification.decorator.js';
|
|
@@ -35,6 +35,14 @@ export { stubDecisionAuthority, type Effect, type CoreDecisionAuthority, type Co
|
|
|
35
35
|
export { Argon2idPasswordHasher, argon2idHasherProvider, SHIELD_PASSWORD_HASHER, type Argon2idParams, } from './authentication/argon2id-password-hasher.js';
|
|
36
36
|
export { LocalKekCrypto } from './encryption/local-kek-crypto.js';
|
|
37
37
|
export { EncryptedFileSecretsProvider } from './secrets/encrypted-file-secrets.js';
|
|
38
|
+
export { resolveBootSecrets, createSecretsProviderFromEnv, assertNoUnresolvedSecretRefs, wasResolvedFromSecretRef, type ResolveBootSecretsOptions, } from './secrets/boot-secrets.js';
|
|
39
|
+
export { S3ObjectStore, type S3ObjectStoreConfig, type S3ObjectStoreOptions, } from './storage/s3-object-store.js';
|
|
40
|
+
export { NodeSha256Digest } from './storage/node-sha256-digest.js';
|
|
41
|
+
export { ObjectStoreBootCheck } from './storage/object-store-boot-check.js';
|
|
42
|
+
export { FileIntake, FILE_INTAKE_REQUEST_KEY, FILE_INTAKE_DEFAULT_MAX_BYTES, type FileIntakeOptions, type FileIntakeResult, } from './storage/file-intake.interceptor.js';
|
|
43
|
+
export type { ShieldStorageOption } from './storage/storage.providers.js';
|
|
44
|
+
export { SHIELD_OBJECT_STORE, SHIELD_FILE_INGEST } from './module/shield-tokens.js';
|
|
45
|
+
export { loadObjectStorageConfig, DEFAULT_OBJECT_STORAGE_MAX_BYTES, type ObjectStorageConfig, type LoadObjectStorageConfigOptions, } from './config/app-config.js';
|
|
38
46
|
export { MariaDbSessionRecordStore, SHIELD_SESSION_RECORD_STORE, RedisHotSessionStore, SHIELD_HOT_SESSION_STORE, RedisRecentRotationStore, type RedisRecentRotationStoreOptions, CryptoTokenGenerator, Sha256RefreshTokenHasher, SystemClock, cryptoTokenGeneratorProvider, refreshHasherProvider, systemClockProvider, SHIELD_TOKEN_GENERATOR, SHIELD_REFRESH_HASHER, SHIELD_CLOCK, HotRevokingSessionRecordStore, type SessionHotRevoker, type LineageSessionLookup, SHIELD_SESSION_LIFETIMES, ROTATE_GRACE_MS, accessBindingTtlMs, } from './session/index.js';
|
|
39
47
|
export { MariaDbRecordPermissionRuleAdapter, SHIELD_RECORD_RULE_PORT, type RuleQueryable, } from './authz/mariadb-record-rule-adapter.js';
|
|
40
48
|
export { MariaDbPolicyStore, SHIELD_POLICY_STORE, type RuleQueryable as PolicyStoreQueryable, } from './authz/mariadb-policy-store.js';
|
|
@@ -55,7 +63,8 @@ export { MariaDbSessionRestrictionStore, type SessionRestrictionStore, type Sess
|
|
|
55
63
|
export { buildListScopeFn, type ListScopeFn } from './authz/wire-record-authorization.js';
|
|
56
64
|
export { BootSequence, assertPoolBudget } from './boot/boot-sequence.js';
|
|
57
65
|
export { APP_CONFIG } from './config/config.module.js';
|
|
58
|
-
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, loadLoginIpLimit, loadRefreshReplayWindowMs, DEFAULT_LOGIN_IP_LIMIT, isDevOrTestNodeEnv, type LoginIpLimitConfig, } from './config/app-config.js';
|
|
66
|
+
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, loadLoginIpLimit, loadRefreshReplayWindowMs, DEFAULT_LOGIN_IP_LIMIT, isDevOrTestNodeEnv, type LoginIpLimitConfig, loadDbTlsConfig, type DbTlsConfig, } from './config/app-config.js';
|
|
67
|
+
export { mariadbSslOption } from './auth/connections.js';
|
|
59
68
|
export { SHIELD_REDIS, SHIELD_CREDENTIAL_RESOLVER, SHIELD_PROFILE_RESOLVER, SHIELD_AUTHENTICATOR, SHIELD_SESSION_AUTHORITY, SHIELD_CREDENTIAL_WRITER, SHIELD_RESET_TOKEN_STORE, SHIELD_EMAIL_CHANGE_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';
|
|
60
69
|
export { MariaDbCredentialResolver } from './authentication/mariadb-credential-resolver.js';
|
|
61
70
|
export type { SessionView, SessionTokensView, SessionSuccess, LoginRequest, MfaChallengeResponse, LoginResponse, MfaRequest, MfaResponse, RefreshRequest, RefreshResponse, LogoutRequest, LogoutAck, } from './auth/dto.js';
|