@astram/identity 0.11.0 → 0.13.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/cross-tenant-grant.object.d.ts.map +1 -1
- package/dist/cross-tenant-grant.object.js +3 -2
- package/dist/features/audit/directory-label-resolver.d.ts +4 -1
- package/dist/features/audit/directory-label-resolver.d.ts.map +1 -1
- package/dist/features/audit/directory-label-resolver.js +8 -5
- package/dist/features/audit/mariadb-audit-reader.d.ts +5 -1
- package/dist/features/audit/mariadb-audit-reader.d.ts.map +1 -1
- package/dist/features/audit/mariadb-audit-reader.js +10 -2
- package/dist/features/authz/access-bundle-reconcile.job.d.ts +4 -1
- package/dist/features/authz/access-bundle-reconcile.job.d.ts.map +1 -1
- package/dist/features/authz/access-bundle-reconcile.job.js +7 -4
- package/dist/features/authz/access-bundle-store.d.ts +4 -1
- package/dist/features/authz/access-bundle-store.d.ts.map +1 -1
- package/dist/features/authz/access-bundle-store.js +19 -16
- package/dist/features/authz/catalog-reconcile.job.d.ts +3 -1
- package/dist/features/authz/catalog-reconcile.job.d.ts.map +1 -1
- package/dist/features/authz/catalog-reconcile.job.js +3 -2
- package/dist/features/authz/db-permission-facts.adapter.d.ts +9 -3
- package/dist/features/authz/db-permission-facts.adapter.d.ts.map +1 -1
- package/dist/features/authz/db-permission-facts.adapter.js +22 -16
- package/dist/features/authz/grant-admin.service.d.ts +4 -1
- package/dist/features/authz/grant-admin.service.d.ts.map +1 -1
- package/dist/features/authz/grant-admin.service.js +32 -37
- package/dist/features/authz/grant-ceiling.d.ts +6 -1
- package/dist/features/authz/grant-ceiling.d.ts.map +1 -1
- package/dist/features/authz/grant-ceiling.js +19 -16
- package/dist/features/authz/mariadb-catalog-store.d.ts +4 -1
- package/dist/features/authz/mariadb-catalog-store.d.ts.map +1 -1
- package/dist/features/authz/mariadb-catalog-store.js +10 -7
- package/dist/features/authz/permission-version.bumper.d.ts +6 -1
- package/dist/features/authz/permission-version.bumper.d.ts.map +1 -1
- package/dist/features/authz/permission-version.bumper.js +13 -11
- package/dist/features/authz/record-authorization.d.ts +16 -2
- package/dist/features/authz/record-authorization.d.ts.map +1 -1
- package/dist/features/authz/record-authorization.js +29 -5
- package/dist/features/authz/role-binding.d.ts +3 -2
- package/dist/features/authz/role-binding.d.ts.map +1 -1
- package/dist/features/authz/role-binding.js +7 -8
- package/dist/features/company/company-hierarchy.service.d.ts +6 -1
- package/dist/features/company/company-hierarchy.service.d.ts.map +1 -1
- package/dist/features/company/company-hierarchy.service.js +17 -16
- package/dist/features/cross-tenant/cross-tenant-grant-read.service.d.ts +5 -1
- package/dist/features/cross-tenant/cross-tenant-grant-read.service.d.ts.map +1 -1
- package/dist/features/cross-tenant/cross-tenant-grant-read.service.js +18 -14
- package/dist/features/framework-schemas.token.d.ts +8 -0
- package/dist/features/framework-schemas.token.d.ts.map +1 -0
- package/dist/features/framework-schemas.token.js +10 -0
- package/dist/features/identity-feature.module.d.ts +36 -2
- package/dist/features/identity-feature.module.d.ts.map +1 -1
- package/dist/features/identity-feature.module.js +142 -34
- package/dist/features/membership/membership-checker.d.ts +7 -1
- package/dist/features/membership/membership-checker.d.ts.map +1 -1
- package/dist/features/membership/membership-checker.js +9 -4
- package/dist/features/migration-ledger-check.d.ts +15 -0
- package/dist/features/migration-ledger-check.d.ts.map +1 -0
- package/dist/features/migration-ledger-check.js +26 -0
- package/dist/features/scope/scope-closure.service.d.ts +8 -1
- package/dist/features/scope/scope-closure.service.d.ts.map +1 -1
- package/dist/features/scope/scope-closure.service.js +22 -19
- package/dist/features/scope/scope-subtree.resolver.d.ts +7 -1
- package/dist/features/scope/scope-subtree.resolver.d.ts.map +1 -1
- package/dist/features/scope/scope-subtree.resolver.js +9 -4
- package/dist/features/service-account/lock-service-accounts.d.ts +3 -1
- package/dist/features/service-account/lock-service-accounts.d.ts.map +1 -1
- package/dist/features/service-account/lock-service-accounts.js +4 -4
- package/dist/features/service-account/no-expiry-key-report.d.ts +9 -3
- package/dist/features/service-account/no-expiry-key-report.d.ts.map +1 -1
- package/dist/features/service-account/no-expiry-key-report.js +17 -10
- package/dist/features/service-account/service-account.store.d.ts +8 -1
- package/dist/features/service-account/service-account.store.d.ts.map +1 -1
- package/dist/features/service-account/service-account.store.js +19 -14
- package/dist/features/user/user.service.d.ts +7 -3
- package/dist/features/user/user.service.d.ts.map +1 -1
- package/dist/features/user/user.service.js +12 -4
- package/dist/record-share.object.d.ts.map +1 -1
- package/dist/record-share.object.js +3 -2
- package/dist/schema.d.ts +15 -2
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +17 -2
- package/migrations/1710000000001-CreateIdentityOrgModel.cjs +14 -4
- package/migrations/1710000000002-CreateMembershipAndPostings.cjs +14 -4
- package/migrations/1710000000003-CreateIdentityCredential.cjs +14 -4
- package/migrations/1710000000004-CreateSessionTables.cjs +14 -4
- package/migrations/1710000000005-CreateMfaAndTokenSurface.cjs +14 -4
- package/migrations/1710000000010-CreateAuditLog.cjs +14 -4
- package/migrations/1710000000011-CreateCrossTenantGrant.cjs +15 -4
- package/migrations/1710000000012-SeedCrossTenantGrantPerms.cjs +15 -4
- package/migrations/1710000000013-CreateSharedResource.cjs +14 -4
- package/migrations/1710000000018-CreateServiceAccount.cjs +13 -4
- package/migrations/1710000000019-AddSessionMustReenroll.cjs +14 -4
- package/migrations/1710000000020-CreateEmailChangeToken.cjs +15 -5
- package/migrations/1710000000021-AddUsersEmailUniqueIndex.cjs +24 -13
- package/migrations/1710000000022-CreateStoredFile.cjs +14 -4
- package/migrations/1710000000099-SeedDevIdentity.cjs +14 -6
- package/migrations/1710000000100-SeedDevTenantBAndRoot.cjs +14 -6
- package/package.json +4 -4
|
@@ -3,6 +3,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.strictPoolSize = strictPoolSize;
|
|
7
|
+
exports.authzPoolLimit = authzPoolLimit;
|
|
6
8
|
exports.createIdentityAuthzPool = createIdentityAuthzPool;
|
|
7
9
|
exports.buildRecordAuthorizationInputs = buildRecordAuthorizationInputs;
|
|
8
10
|
/**
|
|
@@ -23,13 +25,33 @@ const guard_1 = require("@astram/guard");
|
|
|
23
25
|
const objects_js_1 = require("../../objects.js");
|
|
24
26
|
const db_permission_facts_adapter_js_1 = require("./db-permission-facts.adapter.js");
|
|
25
27
|
const session_facts_reader_js_1 = require("./session-facts.reader.js");
|
|
28
|
+
/**
|
|
29
|
+
* A pool-size variable: unset or blank gives `undefined`; otherwise plain digits, a positive safe
|
|
30
|
+
* integer, or it throws naming the variable (never the value). The single-layout pool budget in
|
|
31
|
+
* `@astram/guard` (`loadPoolBudget`) applies the same rule, so the budget counts the pools that open.
|
|
32
|
+
*/
|
|
33
|
+
function strictPoolSize(raw, name) {
|
|
34
|
+
if (raw === undefined || raw.trim() === '')
|
|
35
|
+
return undefined;
|
|
36
|
+
const t = raw.trim();
|
|
37
|
+
const n = /^[0-9]+$/.test(t) ? Number(t) : Number.NaN;
|
|
38
|
+
if (!Number.isSafeInteger(n) || n < 1)
|
|
39
|
+
throw new Error(`${name} must be a positive integer`);
|
|
40
|
+
return n;
|
|
41
|
+
}
|
|
42
|
+
/** The authz pool cap: `APP_AUTHZ_POOL_MAX` (strict), else 4. */
|
|
43
|
+
function authzPoolLimit(env = process.env) {
|
|
44
|
+
return strictPoolSize(env.APP_AUTHZ_POOL_MAX, 'APP_AUTHZ_POOL_MAX') ?? 4;
|
|
45
|
+
}
|
|
26
46
|
/**
|
|
27
47
|
* A lean `mariadb` pool for the authz grant-reads (built from `APP_DB_*` env via guard's
|
|
28
48
|
* `loadDbConfig`). Kept small — these are pre-gate coarse-grant reads, not the main CRUD pool.
|
|
29
49
|
* Opens the pool at CALL time (never at import) — the caller owns closing it.
|
|
50
|
+
* `schemas` (the names `forRoot` resolved or was given) set the default database and user; absent
|
|
51
|
+
* ⇒ from the env namespace. An explicit `APP_DB_USER` / `APP_DB_NAME` wins (QA P2-5).
|
|
30
52
|
*/
|
|
31
|
-
function createIdentityAuthzPool() {
|
|
32
|
-
const db = (0, guard_1.loadDbConfig)();
|
|
53
|
+
function createIdentityAuthzPool(schemas) {
|
|
54
|
+
const db = schemas === undefined ? (0, guard_1.loadDbConfig)() : (0, guard_1.loadDbConfig)(process.env, schemas);
|
|
33
55
|
const ssl = (0, guard_1.mariadbSslOption)(db.tls);
|
|
34
56
|
return mariadb_1.default.createPool({
|
|
35
57
|
host: db.host,
|
|
@@ -37,7 +59,7 @@ function createIdentityAuthzPool() {
|
|
|
37
59
|
user: db.user,
|
|
38
60
|
password: db.password,
|
|
39
61
|
database: db.database,
|
|
40
|
-
connectionLimit:
|
|
62
|
+
connectionLimit: authzPoolLimit(process.env),
|
|
41
63
|
// Phase 16: APP_DB_TLS / APP_DB_TLS_CA (production accounts are REQUIRE SSL).
|
|
42
64
|
...(ssl ? { ssl } : {}),
|
|
43
65
|
});
|
|
@@ -53,11 +75,13 @@ function createIdentityAuthzPool() {
|
|
|
53
75
|
* consumer type is `resource_not_registered`),
|
|
54
76
|
* - `operationManifest` — empty (identity declares no trusted delegated operations).
|
|
55
77
|
*/
|
|
56
|
-
function buildRecordAuthorizationInputs(pool, extraObjects = []
|
|
78
|
+
function buildRecordAuthorizationInputs(pool, extraObjects = [],
|
|
79
|
+
/** Resolved framework names for the facts adapter (absent ⇒ from the namespace, once). */
|
|
80
|
+
schemas) {
|
|
57
81
|
const registry = (0, guard_1.buildIdentityModuleRegistry)([...objects_js_1.identityObjects, ...extraObjects]);
|
|
58
82
|
return {
|
|
59
83
|
pool,
|
|
60
|
-
permissionFacts: new db_permission_facts_adapter_js_1.DbPermissionFactsAdapter(pool, registry),
|
|
84
|
+
permissionFacts: new db_permission_facts_adapter_js_1.DbPermissionFactsAdapter(pool, registry, undefined, schemas),
|
|
61
85
|
sessionFacts: new session_facts_reader_js_1.AlsSessionFactsReader(),
|
|
62
86
|
registry,
|
|
63
87
|
operationManifest: [],
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
* the role id are `?`-bound (never interpolated).
|
|
14
14
|
*/
|
|
15
15
|
import type { Queryable } from './permission-version.bumper.js';
|
|
16
|
+
import type { IdentitySchemaNames } from '../../schema.js';
|
|
16
17
|
/** Where a role is bound relative to a set of subtree nodes. */
|
|
17
18
|
export interface RoleBinding {
|
|
18
19
|
readonly inside: boolean;
|
|
@@ -23,7 +24,7 @@ export interface RoleBinding {
|
|
|
23
24
|
* qualifies only if it is bound (`membership_posting → company_membership`) to a node in the
|
|
24
25
|
* subtree. The caller splices `params` where `sql` sits in the surrounding query.
|
|
25
26
|
*/
|
|
26
|
-
export declare function subtreeExists(nodeIds: readonly string[]): {
|
|
27
|
+
export declare function subtreeExists(nodeIds: readonly string[], schemas: IdentitySchemaNames): {
|
|
27
28
|
sql: string;
|
|
28
29
|
params: string[];
|
|
29
30
|
};
|
|
@@ -32,5 +33,5 @@ export declare function subtreeExists(nodeIds: readonly string[]): {
|
|
|
32
33
|
* query. An empty `nodeIds` is treated as "nothing is inside" (every binding is outside) — fail
|
|
33
34
|
* closed, never an `IN ()` syntax error.
|
|
34
35
|
*/
|
|
35
|
-
export declare function readRoleBinding(q: Queryable, roleUuid: string, nodeIds: readonly string[]): Promise<RoleBinding>;
|
|
36
|
+
export declare function readRoleBinding(q: Queryable, roleUuid: string, nodeIds: readonly string[], schemas: IdentitySchemaNames): Promise<RoleBinding>;
|
|
36
37
|
//# sourceMappingURL=role-binding.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"role-binding.d.ts","sourceRoot":"","sources":["../../../src/features/authz/role-binding.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gCAAgC,CAAC;
|
|
1
|
+
{"version":3,"file":"role-binding.d.ts","sourceRoot":"","sources":["../../../src/features/authz/role-binding.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gCAAgC,CAAC;AAChE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAE3D,gEAAgE;AAChE,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;CAC3B;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAC3B,OAAO,EAAE,SAAS,MAAM,EAAE,EAC1B,OAAO,EAAE,mBAAmB,GAC3B;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,EAAE,CAAA;CAAE,CAQnC;AASD;;;;GAIG;AACH,wBAAsB,eAAe,CACnC,CAAC,EAAE,SAAS,EACZ,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,SAAS,MAAM,EAAE,EAC1B,OAAO,EAAE,mBAAmB,GAC3B,OAAO,CAAC,WAAW,CAAC,CAwBtB"}
|
|
@@ -2,17 +2,16 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.subtreeExists = subtreeExists;
|
|
4
4
|
exports.readRoleBinding = readRoleBinding;
|
|
5
|
-
const APP = 'shield_app';
|
|
6
5
|
/**
|
|
7
6
|
* The shared EXISTS-subtree fragment for a non-root caller's READS (Task 4.2): a role row `r`
|
|
8
7
|
* qualifies only if it is bound (`membership_posting → company_membership`) to a node in the
|
|
9
8
|
* subtree. The caller splices `params` where `sql` sits in the surrounding query.
|
|
10
9
|
*/
|
|
11
|
-
function subtreeExists(nodeIds) {
|
|
10
|
+
function subtreeExists(nodeIds, schemas) {
|
|
12
11
|
const ph = nodeIds.map(() => '?').join(', ');
|
|
13
12
|
const sql = ` AND EXISTS (` +
|
|
14
|
-
`SELECT 1 FROM \`${
|
|
15
|
-
` JOIN \`${
|
|
13
|
+
`SELECT 1 FROM \`${schemas.app}\`.\`membership_posting\` mp` +
|
|
14
|
+
` JOIN \`${schemas.app}\`.\`company_membership\` cm ON cm.cm_uuid = mp.mp_membership_uuid` +
|
|
16
15
|
` WHERE mp.mp_role_uuid = r.r_uuid AND cm.cm_company_uuid IN (${ph}))`;
|
|
17
16
|
return { sql, params: [...nodeIds] };
|
|
18
17
|
}
|
|
@@ -29,7 +28,7 @@ function truthy(v) {
|
|
|
29
28
|
* query. An empty `nodeIds` is treated as "nothing is inside" (every binding is outside) — fail
|
|
30
29
|
* closed, never an `IN ()` syntax error.
|
|
31
30
|
*/
|
|
32
|
-
async function readRoleBinding(q, roleUuid, nodeIds) {
|
|
31
|
+
async function readRoleBinding(q, roleUuid, nodeIds, schemas) {
|
|
33
32
|
const ids = nodeIds.length > 0 ? [...nodeIds] : [];
|
|
34
33
|
const inList = ids.length > 0 ? ids.map(() => '?').join(', ') : 'NULL';
|
|
35
34
|
// `x IN (NULL)` is NULL (never true) and `x NOT IN (NULL)` is NULL too, so for an empty list the
|
|
@@ -40,12 +39,12 @@ async function readRoleBinding(q, roleUuid, nodeIds) {
|
|
|
40
39
|
COALESCE(MAX(${outsideExpr}), 0) AS bound_outside
|
|
41
40
|
FROM (
|
|
42
41
|
SELECT cm.cm_company_uuid AS node
|
|
43
|
-
FROM \`${
|
|
44
|
-
JOIN \`${
|
|
42
|
+
FROM \`${schemas.app}\`.\`membership_posting\` mp
|
|
43
|
+
JOIN \`${schemas.app}\`.\`company_membership\` cm ON cm.cm_uuid = mp.mp_membership_uuid
|
|
45
44
|
WHERE mp.mp_role_uuid = ?
|
|
46
45
|
UNION ALL
|
|
47
46
|
SELECT sa.sa_tenant_id AS node
|
|
48
|
-
FROM \`${
|
|
47
|
+
FROM \`${schemas.app}\`.\`service_account\` sa
|
|
49
48
|
WHERE JSON_CONTAINS(sa.sa_role_ids, JSON_QUOTE(?))
|
|
50
49
|
) b`, [...ids, ...ids, roleUuid, roleUuid]));
|
|
51
50
|
const row = rows[0];
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
import type { Principal } from '@astram/core';
|
|
22
22
|
import type { AuditPort } from '@astram/engine';
|
|
23
23
|
import { ScopeClosureService } from '../scope/scope-closure.service.js';
|
|
24
|
+
import { type IdentitySchemaNames } from '../../schema.js';
|
|
24
25
|
/** The read surface used for existence/name lookups (the shared authz pool satisfies it). */
|
|
25
26
|
export interface HierarchyQueryable {
|
|
26
27
|
query(sql: string, params?: readonly unknown[]): Promise<unknown>;
|
|
@@ -49,9 +50,13 @@ export declare class CompanyHierarchyService {
|
|
|
49
50
|
private readonly ceiling;
|
|
50
51
|
private readonly audit?;
|
|
51
52
|
private readonly now;
|
|
53
|
+
/** Resolved framework names (absent ⇒ resolved from the namespace, once, here). */
|
|
54
|
+
private readonly schemas;
|
|
52
55
|
constructor(pool: HierarchyQueryable, closure: ScopeClosureService,
|
|
53
56
|
/** Phase 8 #3: required — a company delete must dominate every member (no silent skip). */
|
|
54
|
-
ceiling: MemberDominance, audit?: AuditPort | undefined, now?: () => number
|
|
57
|
+
ceiling: MemberDominance, audit?: AuditPort | undefined, now?: () => number,
|
|
58
|
+
/** Resolved framework names (absent ⇒ resolved from the namespace, once, here). */
|
|
59
|
+
schemas?: IdentitySchemaNames);
|
|
55
60
|
/** The actor's own root node (see {@link ownRootOf}). */
|
|
56
61
|
private ownRoot;
|
|
57
62
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"company-hierarchy.service.d.ts","sourceRoot":"","sources":["../../../src/features/company/company-hierarchy.service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAEhD,OAAO,EAAE,mBAAmB,EAAkB,MAAM,mCAAmC,CAAC;
|
|
1
|
+
{"version":3,"file":"company-hierarchy.service.d.ts","sourceRoot":"","sources":["../../../src/features/company/company-hierarchy.service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAEhD,OAAO,EAAE,mBAAmB,EAAkB,MAAM,mCAAmC,CAAC;AAQxF,OAAO,EAA2B,KAAK,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAEpF,6FAA6F;AAC7F,MAAM,WAAW,kBAAkB;IACjC,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,SAAS,OAAO,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACnE;AAED;;;GAGG;AACH,MAAM,WAAW,eAAe;IAC9B,eAAe,CAAC,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACvE,8BAA8B,CAC5B,KAAK,EAAE,SAAS,EAChB,QAAQ,EAAE,aAAa,CAAC;QACtB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;QAC1B,QAAQ,CAAC,mBAAmB,EAAE,SAAS,MAAM,EAAE,CAAC;QAChD,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;KACrC,CAAC,GACD,OAAO,CAAC,IAAI,CAAC,CAAC;CAClB;AAqCD;;;GAGG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,SAAS,GAAG,MAAM,CAElD;AAED,qBAAa,uBAAuB;IAEhC,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,2FAA2F;IAC3F,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC;IACvB,OAAO,CAAC,QAAQ,CAAC,GAAG;IACpB,mFAAmF;IACnF,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAPP,IAAI,EAAE,kBAAkB,EACxB,OAAO,EAAE,mBAAmB;IAC7C,2FAA2F;IAC1E,OAAO,EAAE,eAAe,EACxB,KAAK,CAAC,EAAE,SAAS,YAAA,EACjB,GAAG,GAAE,MAAM,MAAiB;IAC7C,mFAAmF;IAClE,OAAO,GAAE,mBAA+C;IAG3E,yDAAyD;IACzD,OAAO,CAAC,OAAO;IAIf;;;;;OAKG;YACW,UAAU;IAQxB,uDAAuD;YACzC,KAAK;YAUL,WAAW;IAqBzB,iGAAiG;IAC3F,IAAI,CAAC,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAoD3F;;;;;;;;;;;;;;;;;OAiBG;IACG,MAAM,CAAC,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAoB/D;;;;;;;;;;;;;;;;;;;;;;;OAuBG;YACW,gBAAgB;IAkF9B,0FAA0F;YAC5E,kBAAkB;IAmDhC,wFAAwF;YAC1E,sBAAsB;IAgCpC,wFAAwF;IAClF,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAGhD"}
|
|
@@ -6,8 +6,7 @@ const http_js_1 = require("../shared/http.js");
|
|
|
6
6
|
const grant_ceiling_js_1 = require("../authz/grant-ceiling.js");
|
|
7
7
|
const lock_service_accounts_js_1 = require("../service-account/lock-service-accounts.js");
|
|
8
8
|
const identity_errors_js_1 = require("../shared/identity-errors.js");
|
|
9
|
-
const
|
|
10
|
-
const AUTHZ = 'shield_authz';
|
|
9
|
+
const schema_js_1 = require("../../schema.js");
|
|
11
10
|
/** A JSON-array column (the driver returns an array OR a string) → its string items; else []. */
|
|
12
11
|
function jsonIdArray(v) {
|
|
13
12
|
let arr = v;
|
|
@@ -34,14 +33,18 @@ class CompanyHierarchyService {
|
|
|
34
33
|
ceiling;
|
|
35
34
|
audit;
|
|
36
35
|
now;
|
|
36
|
+
schemas;
|
|
37
37
|
constructor(pool, closure,
|
|
38
38
|
/** Phase 8 #3: required — a company delete must dominate every member (no silent skip). */
|
|
39
|
-
ceiling, audit, now = Date.now
|
|
39
|
+
ceiling, audit, now = Date.now,
|
|
40
|
+
/** Resolved framework names (absent ⇒ resolved from the namespace, once, here). */
|
|
41
|
+
schemas = (0, schema_js_1.frameworkSchemasFromEnv)()) {
|
|
40
42
|
this.pool = pool;
|
|
41
43
|
this.closure = closure;
|
|
42
44
|
this.ceiling = ceiling;
|
|
43
45
|
this.audit = audit;
|
|
44
46
|
this.now = now;
|
|
47
|
+
this.schemas = schemas;
|
|
45
48
|
}
|
|
46
49
|
/** The actor's own root node (see {@link ownRootOf}). */
|
|
47
50
|
ownRoot(actor) {
|
|
@@ -54,7 +57,7 @@ class CompanyHierarchyService {
|
|
|
54
57
|
* stored spelling once and passes that down. Unknown → the input unchanged (later steps 404).
|
|
55
58
|
*/
|
|
56
59
|
async storedUuid(uuid) {
|
|
57
|
-
const rows = (await this.pool.query(`SELECT c_uuid FROM \`${
|
|
60
|
+
const rows = (await this.pool.query(`SELECT c_uuid FROM \`${this.schemas.app}\`.\`companies\` WHERE c_uuid = ? LIMIT 1`, [uuid]));
|
|
58
61
|
return rows[0]?.c_uuid ?? uuid;
|
|
59
62
|
}
|
|
60
63
|
/** Names of the given companies (unknown → absent). */
|
|
@@ -62,7 +65,7 @@ class CompanyHierarchyService {
|
|
|
62
65
|
const unique = [...new Set(ids)];
|
|
63
66
|
if (unique.length === 0)
|
|
64
67
|
return new Map();
|
|
65
|
-
const rows = (await q.query(`SELECT c_uuid, c_name FROM \`${
|
|
68
|
+
const rows = (await q.query(`SELECT c_uuid, c_name FROM \`${this.schemas.app}\`.\`companies\` WHERE c_uuid IN (${unique.map(() => '?').join(', ')})`, unique));
|
|
66
69
|
return new Map(rows.map((r) => [r.c_uuid, r.c_name]));
|
|
67
70
|
}
|
|
68
71
|
async recordAudit(conn, actor, action, attributes, tenantId = actor.tenantId) {
|
|
@@ -109,7 +112,7 @@ class CompanyHierarchyService {
|
|
|
109
112
|
if (newParentUuid !== null && !names.has(newParentUuid)) {
|
|
110
113
|
throw new identity_errors_js_1.IdentityNotFoundError('Parent company not found.');
|
|
111
114
|
}
|
|
112
|
-
const oldParentRows = (await this.pool.query(`SELECT c_parent_uuid FROM \`${
|
|
115
|
+
const oldParentRows = (await this.pool.query(`SELECT c_parent_uuid FROM \`${this.schemas.app}\`.\`companies\` WHERE c_uuid = ? LIMIT 1`, [nodeUuid]));
|
|
113
116
|
const oldParent = oldParentRows[0]?.c_parent_uuid ?? null;
|
|
114
117
|
const oldParentName = oldParent
|
|
115
118
|
? (await this.names(this.pool, [oldParent])).get(oldParent)
|
|
@@ -183,7 +186,7 @@ class CompanyHierarchyService {
|
|
|
183
186
|
const agents = await (0, lock_service_accounts_js_1.lockServiceAccountsWhere)(conn, 'sa_id, sa_tenant_id, sa_label, sa_scope_subtree, sa_role_ids, sa_updated_at_ms', {
|
|
184
187
|
sql: 'sa_tenant_id = ? OR JSON_CONTAINS(sa_scope_subtree, JSON_QUOTE(?))',
|
|
185
188
|
params: [nodeUuid, nodeUuid],
|
|
186
|
-
});
|
|
189
|
+
}, this.schemas);
|
|
187
190
|
const own = agents.filter((a) => a.sa_tenant_id === nodeUuid);
|
|
188
191
|
const others = agents.filter((a) => a.sa_tenant_id !== nodeUuid);
|
|
189
192
|
// Root bypasses inside; a refusal (403, names no agent) rolls the whole delete back.
|
|
@@ -196,8 +199,8 @@ class CompanyHierarchyService {
|
|
|
196
199
|
const touched = new Set();
|
|
197
200
|
const editStamp = (a) => Math.max(nowMs, (a.sa_updated_at_ms == null ? 0 : Number(a.sa_updated_at_ms)) + 1);
|
|
198
201
|
const retire = async (a, reason) => {
|
|
199
|
-
await conn.query(`UPDATE \`${
|
|
200
|
-
const res = (await conn.query(`UPDATE \`${
|
|
202
|
+
await conn.query(`UPDATE \`${this.schemas.app}\`.\`service_account\` SET sa_status = 'disabled', sa_updated_at_ms = ? WHERE sa_id = ?`, [editStamp(a), a.sa_id]);
|
|
203
|
+
const res = (await conn.query(`UPDATE \`${this.schemas.authz}\`.\`service_account_key\`
|
|
201
204
|
SET sak_revoked_at_ms = ?
|
|
202
205
|
WHERE sak_account_id = ? AND sak_revoked_at_ms IS NULL`, [nowMs, a.sa_id]));
|
|
203
206
|
touched.add(a.sa_tenant_id);
|
|
@@ -218,7 +221,7 @@ class CompanyHierarchyService {
|
|
|
218
221
|
await retire(a, 'scope_company_deleted');
|
|
219
222
|
continue;
|
|
220
223
|
}
|
|
221
|
-
await conn.query(`UPDATE \`${
|
|
224
|
+
await conn.query(`UPDATE \`${this.schemas.app}\`.\`service_account\` SET sa_scope_subtree = ?, sa_updated_at_ms = ? WHERE sa_id = ?`, [JSON.stringify(remaining), editStamp(a), a.sa_id]);
|
|
222
225
|
touched.add(a.sa_tenant_id);
|
|
223
226
|
await this.recordAudit(conn, actor, 'service_account.updated', {
|
|
224
227
|
eventType: 'service_account',
|
|
@@ -236,7 +239,7 @@ class CompanyHierarchyService {
|
|
|
236
239
|
async revokeGrantsNaming(conn, actor, nodeUuid, companyName) {
|
|
237
240
|
const grants = (await conn.query(`SELECT ctg_uuid, ctg_owning_tenant_uuid, ctg_grantee_tenant_uuid, ctg_grantee_kind,
|
|
238
241
|
ctg_grantee_uuid, ctg_type_key, ctg_scope, ctg_allowed_actions
|
|
239
|
-
FROM \`${
|
|
242
|
+
FROM \`${this.schemas.authz}\`.\`cross_tenant_grant\`
|
|
240
243
|
WHERE (ctg_owning_tenant_uuid = ? OR ctg_grantee_tenant_uuid = ?)
|
|
241
244
|
AND ctg_status <> 'revoked'
|
|
242
245
|
ORDER BY ctg_uuid
|
|
@@ -247,7 +250,7 @@ class CompanyHierarchyService {
|
|
|
247
250
|
const otherSides = grants.map((g) => g.ctg_owning_tenant_uuid === nodeUuid ? g.ctg_grantee_tenant_uuid : g.ctg_owning_tenant_uuid);
|
|
248
251
|
const names = await this.names(conn, otherSides);
|
|
249
252
|
for (const [i, g] of grants.entries()) {
|
|
250
|
-
await conn.query(`UPDATE \`${
|
|
253
|
+
await conn.query(`UPDATE \`${this.schemas.authz}\`.\`cross_tenant_grant\`
|
|
251
254
|
SET ctg_status = 'revoked', ctg_revoked_at = ?
|
|
252
255
|
WHERE ctg_uuid = ? AND ctg_status <> 'revoked'`, [revokedAt, g.ctg_uuid]);
|
|
253
256
|
const other = otherSides[i];
|
|
@@ -273,12 +276,10 @@ class CompanyHierarchyService {
|
|
|
273
276
|
}
|
|
274
277
|
/** Lock the company, then require dominance over each of its members (see `remove`). */
|
|
275
278
|
async assertMayDeleteMembers(conn, actor, nodeUuid) {
|
|
276
|
-
await conn.query(`SELECT c_uuid FROM \`${
|
|
277
|
-
nodeUuid,
|
|
278
|
-
]);
|
|
279
|
+
await conn.query(`SELECT c_uuid FROM \`${this.schemas.app}\`.\`companies\` WHERE c_uuid = ? FOR UPDATE`, [nodeUuid]);
|
|
279
280
|
if (actor.isRoot === true)
|
|
280
281
|
return;
|
|
281
|
-
const rows = (await conn.query(`SELECT cm_user_uuid AS member FROM \`${
|
|
282
|
+
const rows = (await conn.query(`SELECT cm_user_uuid AS member FROM \`${this.schemas.app}\`.\`company_membership\`
|
|
282
283
|
WHERE cm_company_uuid = ? FOR UPDATE`, [nodeUuid]));
|
|
283
284
|
const members = [...new Set(rows.map((r) => r.member))].sort();
|
|
284
285
|
for (const member of members) {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Pool } from 'mariadb';
|
|
2
2
|
import type { ObjectTypeInput } from '@astram/engine';
|
|
3
|
+
import { type IdentitySchemaNames } from '../../schema.js';
|
|
3
4
|
/** One row in the grantee's pending-offers inbox. */
|
|
4
5
|
export interface InboxGrantRow {
|
|
5
6
|
uuid: string;
|
|
@@ -66,7 +67,10 @@ export declare class CrossTenantGrantReadService {
|
|
|
66
67
|
* `cross_tenant_grant` -> share/accept/revoke) — the single source every
|
|
67
68
|
* action-vocabulary consumer uses, so a per-type set can never drift between callers.
|
|
68
69
|
*/
|
|
69
|
-
|
|
70
|
+
/** The shared FROM/JOIN block, built once from the resolved names. */
|
|
71
|
+
private readonly joins;
|
|
72
|
+
/** (`schemas`: resolved framework names; absent ⇒ resolved from the namespace, once, here.) */
|
|
73
|
+
constructor(pool: Pool, offerableTypeInputs: readonly ObjectTypeInput[], actionsByType: (typeKey: string) => readonly string[], schemas?: IdentitySchemaNames);
|
|
70
74
|
/**
|
|
71
75
|
* Pending offers sitting in the CALLER's tenant inbox: `ctg_grantee_tenant_uuid =
|
|
72
76
|
* tenantId AND ctg_status = 'offered' AND ctg_expires_at_ms > nowMs` (an expired
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cross-tenant-grant-read.service.d.ts","sourceRoot":"","sources":["../../../src/features/cross-tenant/cross-tenant-grant-read.service.ts"],"names":[],"mappings":"AAyBA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AACpC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"cross-tenant-grant-read.service.d.ts","sourceRoot":"","sources":["../../../src/features/cross-tenant/cross-tenant-grant-read.service.ts"],"names":[],"mappings":"AAyBA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AACpC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAEtD,OAAO,EAA2B,KAAK,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAEpF,qDAAqD;AACrD,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,gBAAgB,EAAE,MAAM,CAAC;IACzB,WAAW,EAAE,MAAM,GAAG,MAAM,CAAC;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,4EAA4E;AAC5E,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,iBAAiB,EAAE,MAAM,CAAC;IAC1B,WAAW,EAAE,MAAM,GAAG,MAAM,CAAC;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,iFAAiF;AACjF,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,gBAAgB,EAAE,MAAM,CAAC;IACzB,WAAW,EAAE,MAAM,GAAG,MAAM,CAAC;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,6FAA6F;AAC7F,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAgGD,qBACa,2BAA2B;IAsBpC,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,mBAAmB;IACpC,OAAO,CAAC,QAAQ,CAAC,aAAa;IAvBhC;;;;;;;;;;;;;;;OAeG;IACH,sEAAsE;IACtE,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;IAE/B,+FAA+F;gBAE5E,IAAI,EAAE,IAAI,EACV,mBAAmB,EAAE,SAAS,eAAe,EAAE,EAC/C,aAAa,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,SAAS,MAAM,EAAE,EACtE,OAAO,GAAE,mBAA+C;IAK1D;;;;;OAKG;IACG,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC;IA0B1E;;;;;OAKG;IACG,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;IAyBzD;;;;;;;OAOG;IACG,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC;IAyBjE;;;;;;;;OAQG;IACH,OAAO,CAAC,iBAAiB;IASzB;;;;;;;;;;;;OAYG;IACH,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IAIzC;;;;;;;OAOG;IACG,kBAAkB,IAAI,OAAO,CAAC,gBAAgB,EAAE,CAAC;CAWxD"}
|
|
@@ -36,8 +36,7 @@ exports.CrossTenantGrantReadService = void 0;
|
|
|
36
36
|
*/
|
|
37
37
|
const common_1 = require("@nestjs/common");
|
|
38
38
|
const engine_1 = require("@astram/engine");
|
|
39
|
-
const
|
|
40
|
-
const APP = 'shield_app';
|
|
39
|
+
const schema_js_1 = require("../../schema.js");
|
|
41
40
|
/**
|
|
42
41
|
* Parse `ctg_allowed_actions` to a `string[]`, accepting BOTH an already-parsed array
|
|
43
42
|
* (production — the mariadb driver auto-parses the JSON column) and a JSON string
|
|
@@ -99,13 +98,14 @@ const SELECT_COLUMNS = `
|
|
|
99
98
|
ctg.ctg_expires_at_ms,
|
|
100
99
|
ctg.ctg_created_at
|
|
101
100
|
`;
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
LEFT JOIN \`${
|
|
106
|
-
LEFT JOIN \`${
|
|
107
|
-
LEFT JOIN \`${
|
|
108
|
-
LEFT JOIN \`${
|
|
101
|
+
/** The FROM/JOIN block every read shares, over the resolved framework names. */
|
|
102
|
+
const joinsFor = ({ app, authz }) => `
|
|
103
|
+
FROM \`${authz}\`.\`cross_tenant_grant\` ctg
|
|
104
|
+
LEFT JOIN \`${app}\`.\`companies\` owner_co ON owner_co.c_uuid = ctg.ctg_owning_tenant_uuid
|
|
105
|
+
LEFT JOIN \`${app}\`.\`companies\` grantee_co ON grantee_co.c_uuid = ctg.ctg_grantee_tenant_uuid
|
|
106
|
+
LEFT JOIN \`${app}\`.\`users\` gu ON ctg.ctg_grantee_kind = 'user' AND gu.u_uuid = ctg.ctg_grantee_uuid
|
|
107
|
+
LEFT JOIN \`${app}\`.\`roles\` gr ON ctg.ctg_grantee_kind = 'role' AND gr.r_uuid = ctg.ctg_grantee_uuid
|
|
108
|
+
LEFT JOIN \`${app}\`.\`users\` offerer ON offerer.u_uuid = ctg.ctg_offered_by
|
|
109
109
|
`;
|
|
110
110
|
let CrossTenantGrantReadService = class CrossTenantGrantReadService {
|
|
111
111
|
pool;
|
|
@@ -127,10 +127,14 @@ let CrossTenantGrantReadService = class CrossTenantGrantReadService {
|
|
|
127
127
|
* `cross_tenant_grant` -> share/accept/revoke) — the single source every
|
|
128
128
|
* action-vocabulary consumer uses, so a per-type set can never drift between callers.
|
|
129
129
|
*/
|
|
130
|
-
|
|
130
|
+
/** The shared FROM/JOIN block, built once from the resolved names. */
|
|
131
|
+
joins;
|
|
132
|
+
/** (`schemas`: resolved framework names; absent ⇒ resolved from the namespace, once, here.) */
|
|
133
|
+
constructor(pool, offerableTypeInputs, actionsByType, schemas = (0, schema_js_1.frameworkSchemasFromEnv)()) {
|
|
131
134
|
this.pool = pool;
|
|
132
135
|
this.offerableTypeInputs = offerableTypeInputs;
|
|
133
136
|
this.actionsByType = actionsByType;
|
|
137
|
+
this.joins = joinsFor(schemas);
|
|
134
138
|
}
|
|
135
139
|
/**
|
|
136
140
|
* Pending offers sitting in the CALLER's tenant inbox: `ctg_grantee_tenant_uuid =
|
|
@@ -142,7 +146,7 @@ let CrossTenantGrantReadService = class CrossTenantGrantReadService {
|
|
|
142
146
|
if (!tenantId)
|
|
143
147
|
return [];
|
|
144
148
|
const rows = (await this.pool.query(`SELECT ${SELECT_COLUMNS}
|
|
145
|
-
${
|
|
149
|
+
${this.joins}
|
|
146
150
|
WHERE ctg.ctg_grantee_tenant_uuid = ?
|
|
147
151
|
AND ctg.ctg_status = 'offered'
|
|
148
152
|
AND ctg.ctg_expires_at_ms > ?
|
|
@@ -170,7 +174,7 @@ let CrossTenantGrantReadService = class CrossTenantGrantReadService {
|
|
|
170
174
|
if (!tenantId)
|
|
171
175
|
return [];
|
|
172
176
|
const rows = (await this.pool.query(`SELECT ${SELECT_COLUMNS}
|
|
173
|
-
${
|
|
177
|
+
${this.joins}
|
|
174
178
|
WHERE ctg.ctg_owning_tenant_uuid = ?
|
|
175
179
|
ORDER BY ctg.ctg_created_at DESC`, [tenantId]));
|
|
176
180
|
return rows.map((row) => ({
|
|
@@ -199,7 +203,7 @@ let CrossTenantGrantReadService = class CrossTenantGrantReadService {
|
|
|
199
203
|
if (!tenantId)
|
|
200
204
|
return [];
|
|
201
205
|
const rows = (await this.pool.query(`SELECT ${SELECT_COLUMNS}
|
|
202
|
-
${
|
|
206
|
+
${this.joins}
|
|
203
207
|
WHERE ctg.ctg_grantee_tenant_uuid = ?
|
|
204
208
|
AND ctg.ctg_status = 'accepted'
|
|
205
209
|
ORDER BY ctg.ctg_expires_at_ms ASC`, [tenantId]));
|
|
@@ -273,5 +277,5 @@ let CrossTenantGrantReadService = class CrossTenantGrantReadService {
|
|
|
273
277
|
exports.CrossTenantGrantReadService = CrossTenantGrantReadService;
|
|
274
278
|
exports.CrossTenantGrantReadService = CrossTenantGrantReadService = __decorate([
|
|
275
279
|
(0, common_1.Injectable)(),
|
|
276
|
-
__metadata("design:paramtypes", [Object, Array, Function])
|
|
280
|
+
__metadata("design:paramtypes", [Object, Array, Function, Object])
|
|
277
281
|
], CrossTenantGrantReadService);
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The framework database names `IdentityFeatureModule.forRoot` resolved (configurable-framework-db,
|
|
3
|
+
* ADR-0037), for identity services Nest constructs (e.g. `UserService`'s email-change audit sink),
|
|
4
|
+
* so they use the SAME set as every store forRoot builds, not a second env read. Internal to
|
|
5
|
+
* identity; guard's `FRAMEWORK_SCHEMAS` is checked equal at boot.
|
|
6
|
+
*/
|
|
7
|
+
export declare const IDENTITY_FRAMEWORK_SCHEMAS: unique symbol;
|
|
8
|
+
//# sourceMappingURL=framework-schemas.token.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"framework-schemas.token.d.ts","sourceRoot":"","sources":["../../src/features/framework-schemas.token.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,eAAO,MAAM,0BAA0B,eAAuC,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.IDENTITY_FRAMEWORK_SCHEMAS = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* The framework database names `IdentityFeatureModule.forRoot` resolved (configurable-framework-db,
|
|
6
|
+
* ADR-0037), for identity services Nest constructs (e.g. `UserService`'s email-change audit sink),
|
|
7
|
+
* so they use the SAME set as every store forRoot builds, not a second env read. Internal to
|
|
8
|
+
* identity; guard's `FRAMEWORK_SCHEMAS` is checked equal at boot.
|
|
9
|
+
*/
|
|
10
|
+
exports.IDENTITY_FRAMEWORK_SCHEMAS = Symbol('IDENTITY_FRAMEWORK_SCHEMAS');
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type DynamicModule } from '@nestjs/common';
|
|
2
|
-
import { type
|
|
2
|
+
import { type FrameworkSchemas } from '@astram/core';
|
|
3
|
+
import { type EngineDbConfig, type ObjectTypeInput } from '@astram/engine';
|
|
3
4
|
/**
|
|
4
5
|
* The boot check behind {@link IDENTITY_DECISION_AUTHORITY_CHECK}: with this module wired, the
|
|
5
6
|
* request path must decide with the authority guard built from this module's inputs. A missing
|
|
@@ -8,6 +9,33 @@ import { type ObjectTypeInput } from '@astram/engine';
|
|
|
8
9
|
* different registry. (Another source next to this one is refused by `ShieldModule` itself.)
|
|
9
10
|
*/
|
|
10
11
|
export declare function assertRequestPathUsesIdentityAuthorization(authority: unknown): true;
|
|
12
|
+
/**
|
|
13
|
+
* The boot check behind {@link IDENTITY_FRAMEWORK_SCHEMAS_CHECK} (configurable-framework-db,
|
|
14
|
+
* ADR-0037; reviewer R2): the names `IdentityFeatureModule.forRoot` resolved — and handed to the
|
|
15
|
+
* engine, the audit sinks, the reader and every identity store — must equal guard's
|
|
16
|
+
* `FRAMEWORK_SCHEMAS`, or engine/guard/identity would split across databases. A guard that
|
|
17
|
+
* provides no `FRAMEWORK_SCHEMAS` (older than this seam) may only run on the default names.
|
|
18
|
+
* Throws (fail closed) with the namespaces and names only.
|
|
19
|
+
*/
|
|
20
|
+
export declare function assertFrameworkSchemasAgree(identity: FrameworkSchemas, guard: FrameworkSchemas | null | undefined): true;
|
|
21
|
+
/**
|
|
22
|
+
* The boot check behind {@link IDENTITY_FRAMEWORK_DATABASE_CHECK} (G-007 QA P2-5): the runtime
|
|
23
|
+
* database identity's engine and authz pool connect to (derived from `forRoot({ schemas })`)
|
|
24
|
+
* must equal guard's `APP_CONFIG.db.database`, or unqualified queries of guard and identity would
|
|
25
|
+
* land in different databases. No guard `APP_CONFIG` visible ⇒ nothing to compare. Throws (fail
|
|
26
|
+
* closed) with database names only.
|
|
27
|
+
*/
|
|
28
|
+
export declare function assertFrameworkDatabaseAgrees(identityDatabase: string, guardConfig: {
|
|
29
|
+
readonly db?: {
|
|
30
|
+
readonly database?: string;
|
|
31
|
+
};
|
|
32
|
+
} | null | undefined): true;
|
|
33
|
+
/**
|
|
34
|
+
* Map guard's DbConfig → the engine's EngineDbConfig (adds the pool cap the engine needs). The
|
|
35
|
+
* database and default user follow `schemas` (the names `forRoot` resolved or was given); an
|
|
36
|
+
* explicit `APP_DB_USER` / `APP_DB_NAME` still wins (QA P2-5).
|
|
37
|
+
*/
|
|
38
|
+
export declare function engineDbFromEnv(schemas: FrameworkSchemas, env?: NodeJS.ProcessEnv): EngineDbConfig;
|
|
11
39
|
/**
|
|
12
40
|
* IdentityFeatureModule — the framework-owned identity FEATURE surface (company/user/role CRUD;
|
|
13
41
|
* more in later phases) as thin controllers over the engine's RecordsService. The CONSUMER imports
|
|
@@ -34,10 +62,16 @@ export declare class IdentityFeatureModule {
|
|
|
34
62
|
* cross-tenant / policy authority, grant ceiling, guided rule-builder schema and owner-share
|
|
35
63
|
* resolver. Without them a consumer type is unregistered everywhere (denied), not policy-
|
|
36
64
|
* authorable and not shareable ("type '…' is not registered — cannot share").
|
|
65
|
+
* @param schemas the resolved framework database names (configurable-framework-db, ADR-0037).
|
|
66
|
+
* Absent ⇒ resolved ONCE here from `SHIELD_DB_NAMESPACE` (unset ⇒ `shield_*`; invalid ⇒
|
|
67
|
+
* throws). The one set is handed to the engine (registrations + its audit sink), the seven
|
|
68
|
+
* audit sinks, the audit reader and every identity store; a boot check refuses names that
|
|
69
|
+
* differ from guard's `FRAMEWORK_SCHEMAS`. Never read per request.
|
|
37
70
|
*/
|
|
38
|
-
static forRoot({ rootIds, extraObjects, }?: {
|
|
71
|
+
static forRoot({ rootIds, extraObjects, schemas: givenSchemas, }?: {
|
|
39
72
|
rootIds?: readonly string[];
|
|
40
73
|
extraObjects?: readonly ObjectTypeInput[];
|
|
74
|
+
schemas?: FrameworkSchemas;
|
|
41
75
|
}): DynamicModule;
|
|
42
76
|
}
|
|
43
77
|
//# sourceMappingURL=identity-feature.module.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"identity-feature.module.d.ts","sourceRoot":"","sources":["../../src/features/identity-feature.module.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkB,KAAK,aAAa,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"identity-feature.module.d.ts","sourceRoot":"","sources":["../../src/features/identity-feature.module.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkB,KAAK,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAEpE,OAAO,EAGL,KAAK,gBAAgB,EAEtB,MAAM,cAAc,CAAC;AACtB,OAAO,EAYL,KAAK,cAAc,EAGnB,KAAK,eAAe,EACrB,MAAM,gBAAgB,CAAC;AA2GxB;;;;;;GAMG;AACH,wBAAgB,0CAA0C,CAAC,SAAS,EAAE,OAAO,GAAG,IAAI,CAUnF;AAKD;;;;;;;GAOG;AACH,wBAAgB,2BAA2B,CACzC,QAAQ,EAAE,gBAAgB,EAC1B,KAAK,EAAE,gBAAgB,GAAG,IAAI,GAAG,SAAS,GACzC,IAAI,CAwBN;AAKD;;;;;;GAMG;AACH,wBAAgB,6BAA6B,CAC3C,gBAAgB,EAAE,MAAM,EACxB,WAAW,EAAE;IAAE,QAAQ,CAAC,EAAE,CAAC,EAAE;QAAE,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,GAAG,IAAI,GAAG,SAAS,GAC/E,IAAI,CAQN;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAC7B,OAAO,EAAE,gBAAgB,EACzB,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,cAAc,CAqBhB;AAED;;;;;;;;;;GAUG;AACH,qBACa,qBAAqB;IAChC;;;;;;;;;;;;;;;;;;;OAmBG;IACH,MAAM,CAAC,OAAO,CAAC,EACb,OAAO,EACP,YAAiB,EACjB,OAAO,EAAE,YAAY,GACtB,GAAE;QACD,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;QAC5B,YAAY,CAAC,EAAE,SAAS,eAAe,EAAE,CAAC;QAC1C,OAAO,CAAC,EAAE,gBAAgB,CAAC;KACvB,GAAG,aAAa;CA2jBvB"}
|