@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.
- package/dist/auth/auth.module.d.ts.map +1 -1
- package/dist/auth/auth.module.js +57 -30
- package/dist/auth/connections.d.ts +2 -2
- package/dist/auth/connections.d.ts.map +1 -1
- package/dist/auth/connections.js +4 -13
- package/dist/authentication/mariadb-api-key-store.d.ts +10 -1
- package/dist/authentication/mariadb-api-key-store.d.ts.map +1 -1
- package/dist/authentication/mariadb-api-key-store.js +20 -10
- package/dist/authentication/mariadb-credential-resolver.d.ts +8 -1
- package/dist/authentication/mariadb-credential-resolver.d.ts.map +1 -1
- package/dist/authentication/mariadb-credential-resolver.js +15 -5
- package/dist/authentication/mariadb-credential-writer.d.ts +6 -1
- package/dist/authentication/mariadb-credential-writer.d.ts.map +1 -1
- package/dist/authentication/mariadb-credential-writer.js +9 -3
- package/dist/authentication/mariadb-email-address-changer.d.ts +10 -1
- package/dist/authentication/mariadb-email-address-changer.d.ts.map +1 -1
- package/dist/authentication/mariadb-email-address-changer.js +22 -9
- package/dist/authentication/mariadb-email-change-token-store.d.ts +8 -1
- package/dist/authentication/mariadb-email-change-token-store.d.ts.map +1 -1
- package/dist/authentication/mariadb-email-change-token-store.js +18 -9
- package/dist/authentication/mariadb-mfa-device-store.d.ts +6 -1
- package/dist/authentication/mariadb-mfa-device-store.d.ts.map +1 -1
- package/dist/authentication/mariadb-mfa-device-store.js +22 -13
- package/dist/authentication/mariadb-profile-resolver.d.ts +6 -1
- package/dist/authentication/mariadb-profile-resolver.d.ts.map +1 -1
- package/dist/authentication/mariadb-profile-resolver.js +12 -6
- package/dist/authentication/mariadb-profile-writer.d.ts +6 -1
- package/dist/authentication/mariadb-profile-writer.d.ts.map +1 -1
- package/dist/authentication/mariadb-profile-writer.js +11 -4
- package/dist/authentication/mariadb-recovery-code-store.d.ts +6 -1
- package/dist/authentication/mariadb-recovery-code-store.d.ts.map +1 -1
- package/dist/authentication/mariadb-recovery-code-store.js +11 -5
- package/dist/authentication/mariadb-reset-token-store.d.ts +6 -1
- package/dist/authentication/mariadb-reset-token-store.d.ts.map +1 -1
- package/dist/authentication/mariadb-reset-token-store.js +11 -5
- package/dist/authentication/mariadb-session-sweeper.d.ts +6 -1
- package/dist/authentication/mariadb-session-sweeper.d.ts.map +1 -1
- package/dist/authentication/mariadb-session-sweeper.js +11 -5
- package/dist/authz/cross-tenant-grant-writer.d.ts.map +1 -1
- package/dist/authz/cross-tenant-grant-writer.js +4 -3
- package/dist/authz/list-scope-sql.d.ts +10 -2
- package/dist/authz/list-scope-sql.d.ts.map +1 -1
- package/dist/authz/list-scope-sql.js +4 -4
- package/dist/authz/mariadb-cross-tenant-grant-adapter.d.ts.map +1 -1
- package/dist/authz/mariadb-cross-tenant-grant-adapter.js +4 -3
- package/dist/authz/mariadb-list-scope-adapter.d.ts.map +1 -1
- package/dist/authz/mariadb-list-scope-adapter.js +4 -3
- package/dist/authz/mariadb-policy-store.d.ts.map +1 -1
- package/dist/authz/mariadb-policy-store.js +4 -3
- package/dist/authz/mariadb-record-rule-adapter.d.ts.map +1 -1
- package/dist/authz/mariadb-record-rule-adapter.js +4 -3
- package/dist/authz/mariadb-record-share-adapter.d.ts.map +1 -1
- package/dist/authz/mariadb-record-share-adapter.js +4 -3
- package/dist/authz/record-share-cascade.d.ts.map +1 -1
- package/dist/authz/record-share-cascade.js +4 -3
- package/dist/authz/record-share-writer.d.ts.map +1 -1
- package/dist/authz/record-share-writer.js +4 -3
- package/dist/authz/wire-record-authorization.d.ts +8 -0
- package/dist/authz/wire-record-authorization.d.ts.map +1 -1
- package/dist/authz/wire-record-authorization.js +14 -6
- package/dist/boot/boot-sequence.d.ts +4 -0
- package/dist/boot/boot-sequence.d.ts.map +1 -1
- package/dist/boot/boot-sequence.js +13 -1
- package/dist/boot/migration-ledger-check.d.ts +94 -0
- package/dist/boot/migration-ledger-check.d.ts.map +1 -0
- package/dist/boot/migration-ledger-check.js +280 -0
- package/dist/config/app-config.d.ts +72 -3
- package/dist/config/app-config.d.ts.map +1 -1
- package/dist/config/app-config.js +129 -7
- package/dist/config/config.module.d.ts +7 -0
- package/dist/config/config.module.d.ts.map +1 -1
- package/dist/config/config.module.js +13 -2
- package/dist/config/framework-schema-names.d.ts +29 -0
- package/dist/config/framework-schema-names.d.ts.map +1 -0
- package/dist/config/framework-schema-names.js +41 -0
- 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/identity/permission-facts.adapter.d.ts +7 -1
- package/dist/identity/permission-facts.adapter.d.ts.map +1 -1
- package/dist/identity/permission-facts.adapter.js +15 -6
- package/dist/index.d.ts +14 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +68 -22
- 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 +78 -12
- 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/session/mariadb-session-record-store.d.ts +8 -1
- package/dist/session/mariadb-session-record-store.d.ts.map +1 -1
- package/dist/session/mariadb-session-record-store.js +23 -13
- package/dist/session/session-restriction-store.d.ts +8 -1
- package/dist/session/session-restriction-store.d.ts.map +1 -1
- package/dist/session/session-restriction-store.js +15 -5
- 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/migrations/1710000000006-CreateAuthzSchema.cjs +15 -4
- package/migrations/1710000000007-ExtendPermissionAndRolePermission.cjs +11 -3
- package/migrations/1710000000008-CreateAccessBundleTables.cjs +14 -4
- package/migrations/1710000000009-CreateRecordPermissionRule.cjs +15 -4
- package/migrations/1710000000014-CreateRecordShare.cjs +15 -4
- package/migrations/1710000000015-CreateScopeClosure.cjs +14 -4
- package/migrations/1710000000016-AlterCompanyParentFkRestrict.cjs +20 -9
- package/migrations/1710000000017-CreateServiceAccountKey.cjs +14 -4
- 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
|
-
/**
|
|
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: '
|
|
108
|
-
database:
|
|
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,
|
|
177
|
+
user: parseString(env.APP_DB_USER, (0, core_1.frameworkAccountName)(schemas.namespace, 'core')),
|
|
146
178
|
password: parseString(env.APP_DB_PASSWORD, ''),
|
|
147
|
-
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":"
|
|
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: () =>
|
|
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;
|
|
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',
|
|
@@ -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
|
-
|
|
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;
|
|
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
|
-
|
|
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
|
|
66
|
-
JOIN
|
|
67
|
-
JOIN
|
|
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
|
|
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(
|
|
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
|
/**
|