@astram/guard 0.13.0 → 0.15.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.
Files changed (134) hide show
  1. package/dist/auth/auth.module.d.ts.map +1 -1
  2. package/dist/auth/auth.module.js +57 -30
  3. package/dist/auth/connections.d.ts +2 -2
  4. package/dist/auth/connections.d.ts.map +1 -1
  5. package/dist/auth/connections.js +4 -13
  6. package/dist/authentication/mariadb-api-key-store.d.ts +10 -1
  7. package/dist/authentication/mariadb-api-key-store.d.ts.map +1 -1
  8. package/dist/authentication/mariadb-api-key-store.js +20 -10
  9. package/dist/authentication/mariadb-credential-resolver.d.ts +8 -1
  10. package/dist/authentication/mariadb-credential-resolver.d.ts.map +1 -1
  11. package/dist/authentication/mariadb-credential-resolver.js +15 -5
  12. package/dist/authentication/mariadb-credential-writer.d.ts +6 -1
  13. package/dist/authentication/mariadb-credential-writer.d.ts.map +1 -1
  14. package/dist/authentication/mariadb-credential-writer.js +9 -3
  15. package/dist/authentication/mariadb-email-address-changer.d.ts +10 -1
  16. package/dist/authentication/mariadb-email-address-changer.d.ts.map +1 -1
  17. package/dist/authentication/mariadb-email-address-changer.js +22 -9
  18. package/dist/authentication/mariadb-email-change-token-store.d.ts +8 -1
  19. package/dist/authentication/mariadb-email-change-token-store.d.ts.map +1 -1
  20. package/dist/authentication/mariadb-email-change-token-store.js +18 -9
  21. package/dist/authentication/mariadb-mfa-device-store.d.ts +6 -1
  22. package/dist/authentication/mariadb-mfa-device-store.d.ts.map +1 -1
  23. package/dist/authentication/mariadb-mfa-device-store.js +22 -13
  24. package/dist/authentication/mariadb-profile-resolver.d.ts +6 -1
  25. package/dist/authentication/mariadb-profile-resolver.d.ts.map +1 -1
  26. package/dist/authentication/mariadb-profile-resolver.js +12 -6
  27. package/dist/authentication/mariadb-profile-writer.d.ts +6 -1
  28. package/dist/authentication/mariadb-profile-writer.d.ts.map +1 -1
  29. package/dist/authentication/mariadb-profile-writer.js +11 -4
  30. package/dist/authentication/mariadb-recovery-code-store.d.ts +6 -1
  31. package/dist/authentication/mariadb-recovery-code-store.d.ts.map +1 -1
  32. package/dist/authentication/mariadb-recovery-code-store.js +11 -5
  33. package/dist/authentication/mariadb-reset-token-store.d.ts +6 -1
  34. package/dist/authentication/mariadb-reset-token-store.d.ts.map +1 -1
  35. package/dist/authentication/mariadb-reset-token-store.js +11 -5
  36. package/dist/authentication/mariadb-session-sweeper.d.ts +6 -1
  37. package/dist/authentication/mariadb-session-sweeper.d.ts.map +1 -1
  38. package/dist/authentication/mariadb-session-sweeper.js +11 -5
  39. package/dist/authz/cross-tenant-grant-writer.d.ts.map +1 -1
  40. package/dist/authz/cross-tenant-grant-writer.js +4 -3
  41. package/dist/authz/list-scope-sql.d.ts +10 -2
  42. package/dist/authz/list-scope-sql.d.ts.map +1 -1
  43. package/dist/authz/list-scope-sql.js +4 -4
  44. package/dist/authz/mariadb-cross-tenant-grant-adapter.d.ts.map +1 -1
  45. package/dist/authz/mariadb-cross-tenant-grant-adapter.js +4 -3
  46. package/dist/authz/mariadb-list-scope-adapter.d.ts.map +1 -1
  47. package/dist/authz/mariadb-list-scope-adapter.js +4 -3
  48. package/dist/authz/mariadb-policy-store.d.ts.map +1 -1
  49. package/dist/authz/mariadb-policy-store.js +4 -3
  50. package/dist/authz/mariadb-record-rule-adapter.d.ts.map +1 -1
  51. package/dist/authz/mariadb-record-rule-adapter.js +4 -3
  52. package/dist/authz/mariadb-record-share-adapter.d.ts.map +1 -1
  53. package/dist/authz/mariadb-record-share-adapter.js +4 -3
  54. package/dist/authz/record-share-cascade.d.ts.map +1 -1
  55. package/dist/authz/record-share-cascade.js +4 -3
  56. package/dist/authz/record-share-writer.d.ts.map +1 -1
  57. package/dist/authz/record-share-writer.js +4 -3
  58. package/dist/authz/wire-record-authorization.d.ts +8 -0
  59. package/dist/authz/wire-record-authorization.d.ts.map +1 -1
  60. package/dist/authz/wire-record-authorization.js +14 -6
  61. package/dist/boot/boot-sequence.d.ts +4 -0
  62. package/dist/boot/boot-sequence.d.ts.map +1 -1
  63. package/dist/boot/boot-sequence.js +13 -1
  64. package/dist/boot/migration-ledger-check.d.ts +94 -0
  65. package/dist/boot/migration-ledger-check.d.ts.map +1 -0
  66. package/dist/boot/migration-ledger-check.js +280 -0
  67. package/dist/config/app-config.d.ts +72 -3
  68. package/dist/config/app-config.d.ts.map +1 -1
  69. package/dist/config/app-config.js +129 -7
  70. package/dist/config/config.module.d.ts +7 -0
  71. package/dist/config/config.module.d.ts.map +1 -1
  72. package/dist/config/config.module.js +13 -2
  73. package/dist/config/framework-schema-names.d.ts +29 -0
  74. package/dist/config/framework-schema-names.d.ts.map +1 -0
  75. package/dist/config/framework-schema-names.js +41 -0
  76. package/dist/config/loopback-host.d.ts +9 -0
  77. package/dist/config/loopback-host.d.ts.map +1 -0
  78. package/dist/config/loopback-host.js +19 -0
  79. package/dist/encryption/local-kek-crypto.d.ts +6 -0
  80. package/dist/encryption/local-kek-crypto.d.ts.map +1 -1
  81. package/dist/encryption/local-kek-crypto.js +8 -0
  82. package/dist/identity/identity-actions.d.ts +18 -7
  83. package/dist/identity/identity-actions.d.ts.map +1 -1
  84. package/dist/identity/identity-actions.js +16 -17
  85. package/dist/identity/module-registry.bridge.d.ts.map +1 -1
  86. package/dist/identity/module-registry.bridge.js +5 -3
  87. package/dist/identity/permission-facts.adapter.d.ts +7 -1
  88. package/dist/identity/permission-facts.adapter.d.ts.map +1 -1
  89. package/dist/identity/permission-facts.adapter.js +15 -6
  90. package/dist/index.d.ts +14 -3
  91. package/dist/index.d.ts.map +1 -1
  92. package/dist/index.js +68 -22
  93. package/dist/module/shield-tokens.d.ts +11 -0
  94. package/dist/module/shield-tokens.d.ts.map +1 -1
  95. package/dist/module/shield-tokens.js +12 -1
  96. package/dist/module/shield.module.d.ts +10 -0
  97. package/dist/module/shield.module.d.ts.map +1 -1
  98. package/dist/module/shield.module.js +78 -12
  99. package/dist/secrets/boot-secrets.d.ts +33 -0
  100. package/dist/secrets/boot-secrets.d.ts.map +1 -0
  101. package/dist/secrets/boot-secrets.js +180 -0
  102. package/dist/session/mariadb-session-record-store.d.ts +8 -1
  103. package/dist/session/mariadb-session-record-store.d.ts.map +1 -1
  104. package/dist/session/mariadb-session-record-store.js +23 -13
  105. package/dist/session/session-restriction-store.d.ts +8 -1
  106. package/dist/session/session-restriction-store.d.ts.map +1 -1
  107. package/dist/session/session-restriction-store.js +15 -5
  108. package/dist/storage/file-intake.interceptor.d.ts +54 -0
  109. package/dist/storage/file-intake.interceptor.d.ts.map +1 -0
  110. package/dist/storage/file-intake.interceptor.js +223 -0
  111. package/dist/storage/node-sha256-digest.d.ts +6 -0
  112. package/dist/storage/node-sha256-digest.d.ts.map +1 -0
  113. package/dist/storage/node-sha256-digest.js +15 -0
  114. package/dist/storage/object-store-boot-check.d.ts +16 -0
  115. package/dist/storage/object-store-boot-check.d.ts.map +1 -0
  116. package/dist/storage/object-store-boot-check.js +52 -0
  117. package/dist/storage/optional-peer.d.ts +10 -0
  118. package/dist/storage/optional-peer.d.ts.map +1 -0
  119. package/dist/storage/optional-peer.js +26 -0
  120. package/dist/storage/s3-object-store.d.ts +35 -0
  121. package/dist/storage/s3-object-store.d.ts.map +1 -0
  122. package/dist/storage/s3-object-store.js +238 -0
  123. package/dist/storage/storage.providers.d.ts +10 -0
  124. package/dist/storage/storage.providers.d.ts.map +1 -0
  125. package/dist/storage/storage.providers.js +104 -0
  126. package/migrations/1710000000006-CreateAuthzSchema.cjs +15 -4
  127. package/migrations/1710000000007-ExtendPermissionAndRolePermission.cjs +11 -3
  128. package/migrations/1710000000008-CreateAccessBundleTables.cjs +14 -4
  129. package/migrations/1710000000009-CreateRecordPermissionRule.cjs +15 -4
  130. package/migrations/1710000000014-CreateRecordShare.cjs +15 -4
  131. package/migrations/1710000000015-CreateScopeClosure.cjs +14 -4
  132. package/migrations/1710000000016-AlterCompanyParentFkRestrict.cjs +20 -9
  133. package/migrations/1710000000017-CreateServiceAccountKey.cjs +14 -4
  134. package/package.json +11 -3
@@ -1,6 +1,7 @@
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
+ exports.frameworkSchemasOf = frameworkSchemasOf;
4
5
  exports.isDevOrTestNodeEnv = isDevOrTestNodeEnv;
5
6
  exports.loadPoolBudget = loadPoolBudget;
6
7
  exports.loadDbConfig = loadDbConfig;
@@ -13,8 +14,18 @@ exports.loadRefreshHmacKey = loadRefreshHmacKey;
13
14
  exports.assertRefreshHmacKey = assertRefreshHmacKey;
14
15
  exports.loadAppConfig = loadAppConfig;
15
16
  exports.loadAppConfigAsync = loadAppConfigAsync;
17
+ exports.loadObjectStorageConfig = loadObjectStorageConfig;
16
18
  exports.evaluatePoolBudget = evaluatePoolBudget;
17
19
  const core_1 = require("@astram/core");
20
+ const loopback_host_js_1 = require("./loopback-host.js");
21
+ /**
22
+ * The resolved framework database names for a config: `config.schemas`, else resolved from
23
+ * `process.env` (a hand-built config without `schemas`). Call at boot only (Shield #12).
24
+ * @throws FrameworkSchemaConfigError on an invalid `SHIELD_DB_NAMESPACE` (fail closed).
25
+ */
26
+ function frameworkSchemasOf(config) {
27
+ return config.schemas ?? (0, core_1.resolveFrameworkSchemas)(process.env);
28
+ }
18
29
  /** Plan D3 default: 50 failed logins per client IP per 15 minutes. */
19
30
  exports.DEFAULT_LOGIN_IP_LIMIT = {
20
31
  maxAttempts: 50,
@@ -100,12 +111,18 @@ function loadPoolBudget(env = process.env) {
100
111
  // lifetimes. All pure env-readers with documented defaults; the ONLY hard-
101
112
  // required value is the refresh HMAC key (fail-closed).
102
113
  // ---------------------------------------------------------------------------
103
- /** Documented defaults for the runtime MariaDB descriptor (local docker substrate). */
114
+ /** The default-namespace framework names (`SHIELD_DB_NAMESPACE` unset). */
115
+ const DEFAULT_FRAMEWORK_SCHEMAS = (0, core_1.resolveFrameworkSchemas)({});
116
+ /**
117
+ * Documented defaults for the runtime MariaDB descriptor (local docker substrate), for the
118
+ * default namespace. Under `SHIELD_DB_NAMESPACE` the user and database follow the namespace
119
+ * (`<ns>_core`, `<ns>_identity`; see {@link loadDbConfig}).
120
+ */
104
121
  exports.DEFAULT_DB_CONFIG = {
105
122
  host: 'localhost',
106
123
  port: 3306,
107
- user: 'shield_core',
108
- database: 'shield_identity',
124
+ user: (0, core_1.frameworkAccountName)(DEFAULT_FRAMEWORK_SCHEMAS.namespace, 'core'),
125
+ database: DEFAULT_FRAMEWORK_SCHEMAS.identity,
109
126
  };
110
127
  /** Documented default Redis url (local docker substrate). */
111
128
  exports.DEFAULT_REDIS_URL = 'redis://localhost:6379';
@@ -137,14 +154,29 @@ function parseOptionalPositiveInt(raw) {
137
154
  * connect time (loud), which is correct for a runtime that needs real creds; the
138
155
  * secret is deliberately NOT hard-required at config load (unlike the HMAC key,
139
156
  * which is a fail-closed security invariant even for unit tests).
157
+ *
158
+ * Namespace (configurable-framework-db, spec 5.C): `APP_DB_USER` unset ⇒ `<ns>_core`;
159
+ * `APP_DB_NAME` unset ⇒ `<ns>_identity`. A set `APP_DB_NAME` must be one of the four resolved
160
+ * framework names, else this throws naming the variable (fail closed: an unqualified query must
161
+ * not reach another environment's database). An invalid `SHIELD_DB_NAMESPACE` throws too.
162
+ *
163
+ * `schemas` (optional): names the caller already resolved (e.g. `IdentityFeatureModule.forRoot
164
+ * ({ schemas })`). When given they replace the env namespace for the defaults and the
165
+ * `APP_DB_NAME` check, so the runtime database cannot drift from them (G-007 QA P2-5).
140
166
  */
141
- function loadDbConfig(env = process.env) {
167
+ function loadDbConfig(env = process.env, schemas = (0, core_1.resolveFrameworkSchemas)(env)) {
168
+ const database = parseString(env.APP_DB_NAME, schemas.identity);
169
+ const allowed = [schemas.app, schemas.identity, schemas.authz, schemas.audit];
170
+ if (!allowed.includes(database)) {
171
+ throw new core_1.FrameworkSchemaConfigError(`APP_DB_NAME must be one of the framework databases for namespace ` +
172
+ `"${schemas.namespace}" (${allowed.join(', ')}); unset it for ${schemas.identity}.`);
173
+ }
142
174
  return {
143
175
  host: parseString(env.APP_DB_HOST, exports.DEFAULT_DB_CONFIG.host),
144
176
  port: parsePort(env.APP_DB_PORT, exports.DEFAULT_DB_CONFIG.port),
145
- user: parseString(env.APP_DB_USER, exports.DEFAULT_DB_CONFIG.user),
177
+ user: parseString(env.APP_DB_USER, (0, core_1.frameworkAccountName)(schemas.namespace, 'core')),
146
178
  password: parseString(env.APP_DB_PASSWORD, ''),
147
- database: parseString(env.APP_DB_NAME, exports.DEFAULT_DB_CONFIG.database),
179
+ database,
148
180
  tls: loadDbTlsConfig(env),
149
181
  };
150
182
  }
@@ -279,6 +311,7 @@ function assertRefreshHmacKey(key) {
279
311
  function loadAppConfig(env = process.env) {
280
312
  const refreshReplayWindowMs = loadRefreshReplayWindowMs(env);
281
313
  return {
314
+ schemas: (0, core_1.resolveFrameworkSchemas)(env),
282
315
  port: parsePort(env.PORT, 3000),
283
316
  environment: parseEnvironment(env.NODE_ENV),
284
317
  pool: loadPoolBudget(env),
@@ -310,6 +343,95 @@ async function loadAppConfigAsync(provider, env = process.env) {
310
343
  const resolved = await (0, core_1.resolveSecretRefs)(env, provider);
311
344
  return loadAppConfig(resolved);
312
345
  }
346
+ /** Default per-file cap when `OBJECT_STORAGE_MAX_BYTES` is unset (10 MiB, SPEC §10). */
347
+ exports.DEFAULT_OBJECT_STORAGE_MAX_BYTES = 10 * 1024 * 1024;
348
+ /**
349
+ * Read the object-storage config (FILE_STORAGE SPEC §5 F). Returns `undefined` when
350
+ * `OBJECT_STORAGE_BUCKET` is unset or blank (storage off). Otherwise every bad value is a boot
351
+ * error that names the variable and never its value (Shield #1, #10):
352
+ * - endpoint or either credential missing/blank;
353
+ * - endpoint not a URL, carrying userinfo, or not `https:` outside development/test unless the
354
+ * host is loopback (the raw `NODE_ENV`; unset is not dev);
355
+ * - a credential still holding an unresolved `secret-ref:` value;
356
+ * - outside development/test, a credential not resolved from a `secret-ref:` per
357
+ * `options.resolvedFromSecretRef` (`ShieldModule.forRoot({ storage: 'env' })` passes
358
+ * `wasResolvedFromSecretRef`); without the option every credential there is refused;
359
+ * - `OBJECT_STORAGE_SSE` other than `AES256`;
360
+ * - `OBJECT_STORAGE_MAX_BYTES` not a plain positive decimal without leading zeros, or above the
361
+ * 50 MiB ceiling.
362
+ */
363
+ function loadObjectStorageConfig(env = process.env, options = {}) {
364
+ const bucket = env.OBJECT_STORAGE_BUCKET?.trim();
365
+ if (!bucket)
366
+ return undefined;
367
+ const devOrTest = isDevOrTestNodeEnv(env.NODE_ENV);
368
+ const endpoint = requireStorageVar(env, 'OBJECT_STORAGE_ENDPOINT');
369
+ let url;
370
+ try {
371
+ url = new URL(endpoint);
372
+ }
373
+ catch {
374
+ throw new Error('OBJECT_STORAGE_ENDPOINT is not a valid URL');
375
+ }
376
+ if (url.protocol !== 'https:' && url.protocol !== 'http:') {
377
+ throw new Error('OBJECT_STORAGE_ENDPOINT must be an https: (or local http:) URL');
378
+ }
379
+ if (url.username !== '' || url.password !== '') {
380
+ throw new Error('OBJECT_STORAGE_ENDPOINT must not carry credentials (use OBJECT_STORAGE_ACCESS_KEY_ID / ' +
381
+ 'OBJECT_STORAGE_SECRET_ACCESS_KEY)');
382
+ }
383
+ if (url.protocol === 'http:' && !devOrTest && !(0, loopback_host_js_1.isLoopbackHost)(url.hostname)) {
384
+ throw new Error('OBJECT_STORAGE_ENDPOINT must use https: outside NODE_ENV development/test ' +
385
+ '(plain http: is allowed only to a loopback host)');
386
+ }
387
+ const credential = (name) => {
388
+ const value = requireStorageVar(env, name);
389
+ // `startsWith` via the core predicate on a widened copy: the predicate is a type guard.
390
+ if ((0, core_1.isSecretRef)(value)) {
391
+ throw new Error(`${name} still holds an unresolved secret-ref: value; call resolveBootSecrets() in main.ts ` +
392
+ 'before loading AppModule');
393
+ }
394
+ // Fail closed: without a provenance check every credential counts as a literal here.
395
+ if (!devOrTest && !(options.resolvedFromSecretRef?.(name, env) === true)) {
396
+ throw new Error(`${name} must be a secret-ref: value outside NODE_ENV development/test ` +
397
+ '(literal credentials are refused; see ADR-0032)');
398
+ }
399
+ return value;
400
+ };
401
+ const accessKeyId = credential('OBJECT_STORAGE_ACCESS_KEY_ID');
402
+ const secretAccessKey = credential('OBJECT_STORAGE_SECRET_ACCESS_KEY');
403
+ const sse = env.OBJECT_STORAGE_SSE?.trim() || 'AES256';
404
+ if (sse !== 'AES256') {
405
+ throw new Error("OBJECT_STORAGE_SSE must be 'AES256' (SSE-S3); other modes are not supported");
406
+ }
407
+ return {
408
+ endpoint,
409
+ bucket,
410
+ region: env.OBJECT_STORAGE_REGION?.trim() || 'us-east-1',
411
+ accessKeyId,
412
+ secretAccessKey,
413
+ sse,
414
+ maxBytes: parseStorageMaxBytes(env.OBJECT_STORAGE_MAX_BYTES),
415
+ };
416
+ }
417
+ function requireStorageVar(env, name) {
418
+ const value = env[name]?.trim();
419
+ if (!value)
420
+ throw new Error(`${name} must be set when OBJECT_STORAGE_BUCKET is set`);
421
+ return value;
422
+ }
423
+ function parseStorageMaxBytes(raw) {
424
+ const value = raw?.trim();
425
+ if (!value)
426
+ return exports.DEFAULT_OBJECT_STORAGE_MAX_BYTES;
427
+ // Plain decimal digits, no sign, exponent, separators or leading zeros.
428
+ const n = /^[1-9]\d*$/.test(value) ? Number(value) : NaN;
429
+ if (!Number.isSafeInteger(n) || n <= 0 || n > core_1.FILE_MAX_BYTES_CEILING) {
430
+ throw new Error(`OBJECT_STORAGE_MAX_BYTES must be a positive decimal integer (no leading zeros) of at most ` +
431
+ `${core_1.FILE_MAX_BYTES_CEILING} bytes`);
432
+ }
433
+ return n;
434
+ }
313
435
  /**
314
436
  * Evaluate a pool budget. Pure: no throwing, no I/O — the caller decides how to
315
437
  * react (the boot assertion throws; tests inspect the fields).
@@ -1,5 +1,12 @@
1
1
  /** DI token for the resolved {@link AppConfig}. */
2
2
  export declare const APP_CONFIG: unique symbol;
3
+ /**
4
+ * DI token for the resolved framework database names (core `FrameworkSchemas`:
5
+ * `{ namespace, app, identity, authz, audit, isDefault }`, frozen; configurable-framework-db,
6
+ * ADR-0037). Provided and exported app-wide by `ShieldModule` from `APP_CONFIG`, resolved once at
7
+ * boot; identity and app modules inject it instead of hard-coding `shield_*` names.
8
+ */
9
+ export declare const FRAMEWORK_SCHEMAS: unique symbol;
3
10
  /**
4
11
  * Minimal config module (Scaffold).
5
12
  *
@@ -1 +1 @@
1
- {"version":3,"file":"config.module.d.ts","sourceRoot":"","sources":["../../src/config/config.module.ts"],"names":[],"mappings":"AAGA,mDAAmD;AACnD,eAAO,MAAM,UAAU,eAAuB,CAAC;AAE/C;;;;;;GAMG;AACH,qBAUa,YAAY;CAAG"}
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;;;;;GAKG;AACH,eAAO,MAAM,iBAAiB,eAA8B,CAAC;AAE7D;;;;;;GAMG;AACH,qBAaa,YAAY;CAAG"}
@@ -6,11 +6,19 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
6
6
  return c > 3 && r && Object.defineProperty(target, key, r), r;
7
7
  };
8
8
  Object.defineProperty(exports, "__esModule", { value: true });
9
- exports.ConfigModule = exports.APP_CONFIG = void 0;
9
+ exports.ConfigModule = exports.FRAMEWORK_SCHEMAS = 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');
15
+ /**
16
+ * DI token for the resolved framework database names (core `FrameworkSchemas`:
17
+ * `{ namespace, app, identity, authz, audit, isDefault }`, frozen; configurable-framework-db,
18
+ * ADR-0037). Provided and exported app-wide by `ShieldModule` from `APP_CONFIG`, resolved once at
19
+ * boot; identity and app modules inject it instead of hard-coding `shield_*` names.
20
+ */
21
+ exports.FRAMEWORK_SCHEMAS = Symbol('FRAMEWORK_SCHEMAS');
14
22
  /**
15
23
  * Minimal config module (Scaffold).
16
24
  *
@@ -27,7 +35,10 @@ exports.ConfigModule = ConfigModule = __decorate([
27
35
  providers: [
28
36
  {
29
37
  provide: exports.APP_CONFIG,
30
- useFactory: () => (0, app_config_js_1.loadAppConfig)(),
38
+ useFactory: () => {
39
+ (0, boot_secrets_js_1.assertNoUnresolvedSecretRefs)(process.env);
40
+ return (0, app_config_js_1.loadAppConfig)();
41
+ },
31
42
  },
32
43
  ],
33
44
  exports: [exports.APP_CONFIG],
@@ -0,0 +1,29 @@
1
+ /**
2
+ * The framework database names a guard store or adapter targets (configurable-framework-db,
3
+ * spec §5.C, ADR-0037). Every store takes them from its constructor; the module wiring passes
4
+ * `AppConfig.schemas` / `FRAMEWORK_SCHEMAS`, so no SQL in guard carries a database literal.
5
+ *
6
+ * SECURITY (Shield #12): names are resolved at CONSTRUCTION (boot), never per request; a store
7
+ * built outside the wiring resolves `SHIELD_DB_NAMESPACE` once here, so it can never split names
8
+ * across two namespaces. An invalid namespace throws (fail closed, Shield #1). Names reach SQL
9
+ * only after core's pattern check, which admits no backtick, quote, dot or whitespace.
10
+ */
11
+ import { type FrameworkSchemas } from '@astram/core';
12
+ /** The four framework database names (core `FrameworkSchemas` without its metadata). */
13
+ export type FrameworkSchemaNames = Pick<FrameworkSchemas, 'app' | 'identity' | 'authz' | 'audit'>;
14
+ /**
15
+ * `given` (each name checked with core `assertSchemaName`), else the names resolved from
16
+ * `SHIELD_DB_NAMESPACE` NOW. Call from a constructor or a boot-time factory only, never on a
17
+ * request path.
18
+ * @throws FrameworkSchemaConfigError on an invalid `SHIELD_DB_NAMESPACE` or an unsafe given name.
19
+ */
20
+ export declare function schemaNamesAtConstruction(given?: FrameworkSchemaNames): FrameworkSchemaNames;
21
+ /**
22
+ * An authz adapter's `opts.schema`: the given name, checked with core `assertSchemaName`, else
23
+ * the authz name resolved from `SHIELD_DB_NAMESPACE` NOW. Constructors / boot factories only.
24
+ * @throws FrameworkSchemaConfigError on an unsafe given name or an invalid namespace.
25
+ */
26
+ export declare function authzSchemaAtConstruction(given?: string): string;
27
+ /** The backtick-quoted `` `schema`.`table` `` reference for a validated database name. */
28
+ export declare function qualifiedTableName(schema: string, table: string): string;
29
+ //# sourceMappingURL=framework-schema-names.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"framework-schema-names.d.ts","sourceRoot":"","sources":["../../src/config/framework-schema-names.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EAIL,KAAK,gBAAgB,EACtB,MAAM,cAAc,CAAC;AAEtB,wFAAwF;AACxF,MAAM,MAAM,oBAAoB,GAAG,IAAI,CAAC,gBAAgB,EAAE,KAAK,GAAG,UAAU,GAAG,OAAO,GAAG,OAAO,CAAC,CAAC;AAElG;;;;;GAKG;AACH,wBAAgB,yBAAyB,CAAC,KAAK,CAAC,EAAE,oBAAoB,GAAG,oBAAoB,CAI5F;AAED;;;;GAIG;AACH,wBAAgB,yBAAyB,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAIhE;AAED,0FAA0F;AAC1F,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAExE"}
@@ -0,0 +1,41 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.schemaNamesAtConstruction = schemaNamesAtConstruction;
4
+ exports.authzSchemaAtConstruction = authzSchemaAtConstruction;
5
+ exports.qualifiedTableName = qualifiedTableName;
6
+ /**
7
+ * The framework database names a guard store or adapter targets (configurable-framework-db,
8
+ * spec §5.C, ADR-0037). Every store takes them from its constructor; the module wiring passes
9
+ * `AppConfig.schemas` / `FRAMEWORK_SCHEMAS`, so no SQL in guard carries a database literal.
10
+ *
11
+ * SECURITY (Shield #12): names are resolved at CONSTRUCTION (boot), never per request; a store
12
+ * built outside the wiring resolves `SHIELD_DB_NAMESPACE` once here, so it can never split names
13
+ * across two namespaces. An invalid namespace throws (fail closed, Shield #1). Names reach SQL
14
+ * only after core's pattern check, which admits no backtick, quote, dot or whitespace.
15
+ */
16
+ const core_1 = require("@astram/core");
17
+ /**
18
+ * `given` (each name checked with core `assertSchemaName`), else the names resolved from
19
+ * `SHIELD_DB_NAMESPACE` NOW. Call from a constructor or a boot-time factory only, never on a
20
+ * request path.
21
+ * @throws FrameworkSchemaConfigError on an invalid `SHIELD_DB_NAMESPACE` or an unsafe given name.
22
+ */
23
+ function schemaNamesAtConstruction(given) {
24
+ return given !== undefined
25
+ ? (0, core_1.assertFrameworkSchemaNames)(given, 'schemas')
26
+ : (0, core_1.resolveFrameworkSchemas)(process.env);
27
+ }
28
+ /**
29
+ * An authz adapter's `opts.schema`: the given name, checked with core `assertSchemaName`, else
30
+ * the authz name resolved from `SHIELD_DB_NAMESPACE` NOW. Constructors / boot factories only.
31
+ * @throws FrameworkSchemaConfigError on an unsafe given name or an invalid namespace.
32
+ */
33
+ function authzSchemaAtConstruction(given) {
34
+ return given !== undefined
35
+ ? (0, core_1.assertSchemaName)(given, 'opts.schema')
36
+ : (0, core_1.resolveFrameworkSchemas)(process.env).authz;
37
+ }
38
+ /** The backtick-quoted `` `schema`.`table` `` reference for a validated database name. */
39
+ function qualifiedTableName(schema, table) {
40
+ return `\`${schema}\`.\`${table}\``;
41
+ }
@@ -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;IAI9D,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"}
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
- * The fixed action vocabulary + module key for the framework's @astram/identity objects.
3
- * Engine ObjectTypes carry no action list, so actions are synthesized here — the ONE source
4
- * the module-registry bridge derives from. Permission keys are `identity:<typeKey>:<action>`.
5
- * (Ported from astram3. `cross_tenant_grant` gets its own override — see below — every other
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
- /** The action vocabulary for `typeKey` — the per-type override if one exists, else the default. */
37
- export declare function actionsFor(typeKey: string): readonly string[];
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;;;;;;GAMG;AACH,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;AAY5E,mGAAmG;AACnG,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,CAE7D"}
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
- * Per-type action-vocabulary override. Every type not listed here defaults to
44
- * {@link IDENTITY_ACTIONS} (the CRUD verbs).
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
- const ACTIONS_BY_TYPE = {
47
- cross_tenant_grant: exports.CROSS_TENANT_GRANT_ACTIONS,
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":"AAQA,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"}
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. ObjectTypes carry no
8
- * action list, so we synthesize per-type actions (actionsFor). Every action is `gated` — nothing
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.actionsFor)(r.typeKey).map((actionKey) => ({
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',
@@ -8,6 +8,7 @@
8
8
  * never a fabricated empty projection (that would silently read as "you have no grants").
9
9
  */
10
10
  import type { Pool } from 'mariadb';
11
+ import { type FrameworkSchemaNames } from '../config/framework-schema-names.js';
11
12
  export interface AuthFactDto {
12
13
  readonly action: string;
13
14
  readonly allowed: boolean;
@@ -29,7 +30,12 @@ export declare class MariaDbPermissionFactsAdapter {
29
30
  private readonly listActions;
30
31
  private readonly controlPlaneKeys;
31
32
  private readonly now;
32
- constructor(pool: Pool, listActions: () => readonly string[], controlPlaneKeys: readonly string[], now?: () => number);
33
+ /** The org (`<app>`) and authorization (`<authz>`) databases, resolved at construction. */
34
+ private readonly app;
35
+ private readonly authz;
36
+ constructor(pool: Pool, listActions: () => readonly string[], controlPlaneKeys: readonly string[], now?: () => number,
37
+ /** Framework database names; absent ⇒ resolved from `SHIELD_DB_NAMESPACE` here, once. */
38
+ schemas?: FrameworkSchemaNames);
33
39
  projectFor(principalId: string, tenantId: string, isRoot: boolean): Promise<AuthFactProjection>;
34
40
  currentVersionFor(_principalId: string, tenantId: string): Promise<number>;
35
41
  private readRoleIdsForTenant;
@@ -1 +1 @@
1
- {"version":3,"file":"permission-facts.adapter.d.ts","sourceRoot":"","sources":["../../src/identity/permission-facts.adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAEpC,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC9B;AACD,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,KAAK,EAAE,SAAS,WAAW,EAAE,CAAC;IACvC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,kBAAkB,CAAC,EAAE,MAAM,CAAC;CACtC;AAED,yFAAyF;AACzF,qBAAa,6BAA8B,SAAQ,KAAK;gBAC1C,OAAO,SAA8B;CAIlD;AAYD,qBAAa,6BAA6B;IAEtC,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,WAAW;IAC5B,OAAO,CAAC,QAAQ,CAAC,gBAAgB;IACjC,OAAO,CAAC,QAAQ,CAAC,GAAG;gBAHH,IAAI,EAAE,IAAI,EACV,WAAW,EAAE,MAAM,SAAS,MAAM,EAAE,EACpC,gBAAgB,EAAE,SAAS,MAAM,EAAE,EACnC,GAAG,GAAE,MAAM,MAAiB;IAGzC,UAAU,CACd,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,OAAO,GACd,OAAO,CAAC,kBAAkB,CAAC;IAmBxB,iBAAiB,CAAC,YAAY,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;YASlE,oBAAoB;YAepB,aAAa;YASb,WAAW;IAQzB;;;;;;OAMG;IACH,OAAO,CAAC,eAAe;CAaxB"}
1
+ {"version":3,"file":"permission-facts.adapter.d.ts","sourceRoot":"","sources":["../../src/identity/permission-facts.adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AACpC,OAAO,EAEL,KAAK,oBAAoB,EAC1B,MAAM,qCAAqC,CAAC;AAE7C,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC9B;AACD,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,KAAK,EAAE,SAAS,WAAW,EAAE,CAAC;IACvC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,kBAAkB,CAAC,EAAE,MAAM,CAAC;CACtC;AAED,yFAAyF;AACzF,qBAAa,6BAA8B,SAAQ,KAAK;gBAC1C,OAAO,SAA8B;CAIlD;AAYD,qBAAa,6BAA6B;IAMtC,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,WAAW;IAC5B,OAAO,CAAC,QAAQ,CAAC,gBAAgB;IACjC,OAAO,CAAC,QAAQ,CAAC,GAAG;IARtB,2FAA2F;IAC3F,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAS;IAC7B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;gBAGZ,IAAI,EAAE,IAAI,EACV,WAAW,EAAE,MAAM,SAAS,MAAM,EAAE,EACpC,gBAAgB,EAAE,SAAS,MAAM,EAAE,EACnC,GAAG,GAAE,MAAM,MAAiB;IAC7C,yFAAyF;IACzF,OAAO,CAAC,EAAE,oBAAoB;IAO1B,UAAU,CACd,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,OAAO,GACd,OAAO,CAAC,kBAAkB,CAAC;IAmBxB,iBAAiB,CAAC,YAAY,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;YASlE,oBAAoB;YAepB,aAAa;YASb,WAAW;IAQzB;;;;;;OAMG;IACH,OAAO,CAAC,eAAe;CAaxB"}
@@ -10,6 +10,7 @@
10
10
  */
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
12
  exports.MariaDbPermissionFactsAdapter = exports.AuthorizationUnavailableError = void 0;
13
+ const framework_schema_names_js_1 = require("../config/framework-schema-names.js");
13
14
  /** A store/dependency outage while computing facts — the controller maps this to 503. */
14
15
  class AuthorizationUnavailableError extends Error {
15
16
  constructor(message = 'authorization_unavailable') {
@@ -23,11 +24,19 @@ class MariaDbPermissionFactsAdapter {
23
24
  listActions;
24
25
  controlPlaneKeys;
25
26
  now;
26
- constructor(pool, listActions, controlPlaneKeys, now = Date.now) {
27
+ /** The org (`<app>`) and authorization (`<authz>`) databases, resolved at construction. */
28
+ app;
29
+ authz;
30
+ constructor(pool, listActions, controlPlaneKeys, now = Date.now,
31
+ /** Framework database names; absent ⇒ resolved from `SHIELD_DB_NAMESPACE` here, once. */
32
+ schemas) {
27
33
  this.pool = pool;
28
34
  this.listActions = listActions;
29
35
  this.controlPlaneKeys = controlPlaneKeys;
30
36
  this.now = now;
37
+ const s = (0, framework_schema_names_js_1.schemaNamesAtConstruction)(schemas);
38
+ this.app = s.app;
39
+ this.authz = s.authz;
31
40
  }
32
41
  async projectFor(principalId, tenantId, isRoot) {
33
42
  try {
@@ -62,9 +71,9 @@ class MariaDbPermissionFactsAdapter {
62
71
  async readRoleIdsForTenant(userUuid, companyUuid) {
63
72
  const nowMs = this.now();
64
73
  const rows = (await this.pool.query(`SELECT DISTINCT mp.mp_role_uuid AS role_uuid
65
- FROM \`shield_app\`.\`company_membership\` cm
66
- JOIN \`shield_app\`.\`users\` u ON u.u_uuid = cm.cm_user_uuid AND u.u_status = 'active'
67
- JOIN \`shield_app\`.\`membership_posting\` mp ON mp.mp_membership_uuid = cm.cm_uuid
74
+ FROM \`${this.app}\`.\`company_membership\` cm
75
+ JOIN \`${this.app}\`.\`users\` u ON u.u_uuid = cm.cm_user_uuid AND u.u_status = 'active'
76
+ JOIN \`${this.app}\`.\`membership_posting\` mp ON mp.mp_membership_uuid = cm.cm_uuid
68
77
  WHERE cm.cm_user_uuid = ? AND cm.cm_company_uuid = ? AND cm.cm_is_active = 1
69
78
  AND (mp.mp_effective_from_ms IS NULL OR mp.mp_effective_from_ms <= ?)
70
79
  AND (mp.mp_effective_until_ms IS NULL OR mp.mp_effective_until_ms > ?)`, [userUuid, companyUuid, nowMs, nowMs]));
@@ -72,11 +81,11 @@ class MariaDbPermissionFactsAdapter {
72
81
  }
73
82
  async readGrantKeys(roleIds) {
74
83
  const placeholders = roleIds.map(() => '?').join(', ');
75
- const rows = (await this.pool.query(`SELECT rp_perm_key FROM \`shield_authz\`.\`role_permission\` WHERE rp_role_uuid IN (${placeholders})`, [...roleIds]));
84
+ const rows = (await this.pool.query(`SELECT rp_perm_key FROM \`${this.authz}\`.\`role_permission\` WHERE rp_role_uuid IN (${placeholders})`, [...roleIds]));
76
85
  return rows.map((r) => r.rp_perm_key);
77
86
  }
78
87
  async readVersion(tenantId) {
79
- const rows = (await this.pool.query('SELECT apv_version FROM `shield_authz`.`authz_permission_version` WHERE apv_tenant_uuid = ? LIMIT 1', [tenantId]));
88
+ const rows = (await this.pool.query(`SELECT apv_version FROM \`${this.authz}\`.\`authz_permission_version\` WHERE apv_tenant_uuid = ? LIMIT 1`, [tenantId]));
80
89
  return rows[0] !== undefined ? Number(rows[0].apv_version) : 0;
81
90
  }
82
91
  /**