@astram/identity 0.12.0 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (89) hide show
  1. package/dist/features/authz/grant-admin.service.d.ts +1 -1
  2. package/dist/features/authz/grant-admin.service.d.ts.map +1 -1
  3. package/dist/features/authz/grant-admin.service.js +8 -0
  4. package/dist/features/authz/grant-ceiling.d.ts +11 -3
  5. package/dist/features/authz/grant-ceiling.d.ts.map +1 -1
  6. package/dist/features/authz/grant-ceiling.js +20 -3
  7. package/dist/features/authz/record-authorization.d.ts +9 -1
  8. package/dist/features/authz/record-authorization.d.ts.map +1 -1
  9. package/dist/features/authz/record-authorization.js +24 -3
  10. package/dist/features/company/company-hierarchy.service.d.ts +6 -0
  11. package/dist/features/company/company-hierarchy.service.d.ts.map +1 -1
  12. package/dist/features/company/company-hierarchy.service.js +19 -0
  13. package/dist/features/identity-feature.module.d.ts +7 -1
  14. package/dist/features/identity-feature.module.d.ts.map +1 -1
  15. package/dist/features/identity-feature.module.js +25 -5
  16. package/dist/features/membership/membership.service.d.ts +9 -0
  17. package/dist/features/membership/membership.service.d.ts.map +1 -1
  18. package/dist/features/membership/membership.service.js +40 -0
  19. package/dist/features/membership/posting.service.d.ts +18 -0
  20. package/dist/features/membership/posting.service.d.ts.map +1 -1
  21. package/dist/features/membership/posting.service.js +85 -27
  22. package/dist/features/org-bootstrap/org-bootstrap-error.d.ts +20 -0
  23. package/dist/features/org-bootstrap/org-bootstrap-error.d.ts.map +1 -0
  24. package/dist/features/org-bootstrap/org-bootstrap-error.js +24 -0
  25. package/dist/features/org-bootstrap/org-bootstrap-file.d.ts +58 -0
  26. package/dist/features/org-bootstrap/org-bootstrap-file.d.ts.map +1 -0
  27. package/dist/features/org-bootstrap/org-bootstrap-file.js +323 -0
  28. package/dist/features/org-bootstrap/org-bootstrap-plan.d.ts +79 -0
  29. package/dist/features/org-bootstrap/org-bootstrap-plan.d.ts.map +1 -0
  30. package/dist/features/org-bootstrap/org-bootstrap-plan.js +288 -0
  31. package/dist/features/org-bootstrap/org-bootstrap-runner.harness.d.ts +259 -0
  32. package/dist/features/org-bootstrap/org-bootstrap-runner.harness.d.ts.map +1 -0
  33. package/dist/features/org-bootstrap/org-bootstrap-runner.harness.js +604 -0
  34. package/dist/features/org-bootstrap/org-bootstrap-runner.mocks.d.ts +24 -0
  35. package/dist/features/org-bootstrap/org-bootstrap-runner.mocks.d.ts.map +1 -0
  36. package/dist/features/org-bootstrap/org-bootstrap-runner.mocks.js +39 -0
  37. package/dist/features/org-bootstrap/org-bootstrap.manifest.d.ts +19 -0
  38. package/dist/features/org-bootstrap/org-bootstrap.manifest.d.ts.map +1 -0
  39. package/dist/features/org-bootstrap/org-bootstrap.manifest.js +30 -0
  40. package/dist/features/org-bootstrap/org-bootstrap.tokens.d.ts +8 -0
  41. package/dist/features/org-bootstrap/org-bootstrap.tokens.d.ts.map +1 -0
  42. package/dist/features/org-bootstrap/org-bootstrap.tokens.js +10 -0
  43. package/dist/features/org-bootstrap/org-names.d.ts +12 -0
  44. package/dist/features/org-bootstrap/org-names.d.ts.map +1 -0
  45. package/dist/features/org-bootstrap/org-names.js +26 -0
  46. package/dist/features/org-bootstrap/org-state.reader.d.ts +15 -0
  47. package/dist/features/org-bootstrap/org-state.reader.d.ts.map +1 -0
  48. package/dist/features/org-bootstrap/org-state.reader.js +225 -0
  49. package/dist/features/org-bootstrap/root-password-source.d.ts +9 -0
  50. package/dist/features/org-bootstrap/root-password-source.d.ts.map +1 -0
  51. package/dist/features/org-bootstrap/root-password-source.js +57 -0
  52. package/dist/features/org-bootstrap/run-org-bootstrap.d.ts +42 -0
  53. package/dist/features/org-bootstrap/run-org-bootstrap.d.ts.map +1 -0
  54. package/dist/features/org-bootstrap/run-org-bootstrap.js +688 -0
  55. package/dist/features/org-bootstrap/set-password-links.d.ts +30 -0
  56. package/dist/features/org-bootstrap/set-password-links.d.ts.map +1 -0
  57. package/dist/features/org-bootstrap/set-password-links.js +55 -0
  58. package/dist/features/org-structure/org-catalog.service.d.ts +13 -0
  59. package/dist/features/org-structure/org-catalog.service.d.ts.map +1 -1
  60. package/dist/features/org-structure/org-catalog.service.js +40 -0
  61. package/dist/features/role/role-owner-transition.d.ts +17 -0
  62. package/dist/features/role/role-owner-transition.d.ts.map +1 -0
  63. package/dist/features/role/role-owner-transition.js +23 -0
  64. package/dist/features/role/role.controller.d.ts +1 -1
  65. package/dist/features/role/role.controller.d.ts.map +1 -1
  66. package/dist/features/role/role.controller.js +6 -4
  67. package/dist/features/role/role.service.d.ts +8 -2
  68. package/dist/features/role/role.service.d.ts.map +1 -1
  69. package/dist/features/role/role.service.js +13 -3
  70. package/dist/features/user/credential-provisioner.token.d.ts +8 -0
  71. package/dist/features/user/credential-provisioner.token.d.ts.map +1 -0
  72. package/dist/features/user/credential-provisioner.token.js +10 -0
  73. package/dist/features/user/user.controller.d.ts +1 -3
  74. package/dist/features/user/user.controller.d.ts.map +1 -1
  75. package/dist/features/user/user.controller.js +2 -2
  76. package/dist/features/user/user.service.d.ts +28 -5
  77. package/dist/features/user/user.service.d.ts.map +1 -1
  78. package/dist/features/user/user.service.js +45 -12
  79. package/dist/index.d.ts +6 -0
  80. package/dist/index.d.ts.map +1 -1
  81. package/dist/index.js +9 -1
  82. package/dist/internal.d.ts +2 -0
  83. package/dist/internal.d.ts.map +1 -1
  84. package/dist/internal.js +3 -1
  85. package/dist/role.object.d.ts +1 -0
  86. package/dist/role.object.d.ts.map +1 -1
  87. package/dist/role.object.js +8 -0
  88. package/migrations/1710000000101-AddRoleOwnerCompany.cjs +98 -0
  89. package/package.json +4 -4
@@ -1,6 +1,6 @@
1
1
  import type { Pool } from 'mariadb';
2
2
  import type { Principal } from '@astram/core';
3
- import type { AuditPort } from '@astram/engine';
3
+ import { type AuditPort } from '@astram/engine';
4
4
  import { PermissionVersionBumper } from './permission-version.bumper.js';
5
5
  import { AccessBundleStore } from './access-bundle-store.js';
6
6
  import { GrantCeiling } from './grant-ceiling.js';
@@ -1 +1 @@
1
- {"version":3,"file":"grant-admin.service.d.ts","sourceRoot":"","sources":["../../../src/features/authz/grant-admin.service.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"grant-admin.service.d.ts","sourceRoot":"","sources":["../../../src/features/authz/grant-admin.service.ts"],"names":[],"mappings":"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"}
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.GrantAdminService = exports.BadKeyError = void 0;
4
4
  const core_1 = require("@astram/core");
5
+ const engine_1 = require("@astram/engine");
5
6
  const access_bundle_store_js_1 = require("./access-bundle-store.js");
6
7
  const db_permission_facts_adapter_js_1 = require("./db-permission-facts.adapter.js");
7
8
  const grant_ceiling_js_1 = require("./grant-ceiling.js");
@@ -216,6 +217,12 @@ class GrantAdminService {
216
217
  return [labelOf(rowTenant)];
217
218
  return actor.isRoot === true ? tenants.map(labelOf) : [];
218
219
  };
220
+ // G-009 R6: a grantor bound to a declared operation (org bootstrap) is attributed in the row;
221
+ // an unbound grantor adds nothing, so today's attributes stay byte-identical.
222
+ const op = (0, engine_1.operationOf)(actor);
223
+ const operationAttributes = !op
224
+ ? {}
225
+ : { operation: op.operationKey, step: op.stepKey, onBehalfOf: op.onBehalfOf };
219
226
  const occurredAtMs = Date.now();
220
227
  for (const tenantId of new Set([actor.tenantId, ...tenants])) {
221
228
  const tenantNames = tenantsFor(tenantId);
@@ -231,6 +238,7 @@ class GrantAdminService {
231
238
  keys: [...keys],
232
239
  tenants: tenantNames,
233
240
  ...extra,
241
+ ...operationAttributes,
234
242
  },
235
243
  }, query);
236
244
  }
@@ -20,6 +20,14 @@ import { type IdentitySchemaNames } from '../../schema.js';
20
20
  export declare class GrantCeilingError extends Error {
21
21
  constructor(key: string, message?: string);
22
22
  }
23
+ /**
24
+ * The org-bootstrap exception (G-009 AC-7): `operationRoleIds` is the allow-set the runner computed
25
+ * from the file's roles for the company. It relaxes a role check ONLY for a principal bound (engine
26
+ * WeakMap binding, unforgeable) to `shield:org-bootstrap`, and only when EVERY id of the call is in the set.
27
+ */
28
+ export interface OperationRoleOptions {
29
+ readonly operationRoleIds?: readonly string[];
30
+ }
23
31
  /** The actor's reachable subtree: the session's cached closure, else the self node (non-widening). */
24
32
  export declare function subtreeOf(actor: Principal): readonly string[];
25
33
  /**
@@ -89,7 +97,7 @@ export declare class GrantCeiling {
89
97
  * A role holding nothing assigns nothing and passes; a role key the registry does not know
90
98
  * counts as not held (Phase 8 #4). Root bypasses. Store outage → 503.
91
99
  */
92
- assertCanAssignRoles(actor: Principal, roleIds: readonly string[]): Promise<void>;
100
+ assertCanAssignRoles(actor: Principal, roleIds: readonly string[], opts?: OperationRoleOptions): Promise<void>;
93
101
  /**
94
102
  * The first of a TARGET's raw keys (a role's `role_permission` rows) the actor does not cover,
95
103
  * or `undefined` when all are covered. Phase 8 #4: judged per RAW key with `coversKey`, so a key
@@ -166,12 +174,12 @@ export declare class GrantCeiling {
166
174
  * role not bound in the subtree is "not found" for the actor (no existence leak); a role also
167
175
  * used outside (a shared role) is root-only. Root bypasses.
168
176
  */
169
- assertRoleAdministrable(actor: Principal, roleUuid: string): Promise<void>;
177
+ assertRoleAdministrable(actor: Principal, roleUuid: string, opts?: OperationRoleOptions): Promise<void>;
170
178
  /**
171
179
  * Assert the actor may POST someone to the role (#11): the role is bound within the actor's
172
180
  * subtree or not bound at all — never a role also used outside it (a shared role's assignment
173
181
  * would widen another tenant's role). Pair with {@link assertCanAssignRoles}. Root bypasses.
174
182
  */
175
- assertRoleAssignable(actor: Principal, roleUuid: string): Promise<void>;
183
+ assertRoleAssignable(actor: Principal, roleUuid: string, opts?: OperationRoleOptions): Promise<void>;
176
184
  }
177
185
  //# sourceMappingURL=grant-ceiling.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"grant-ceiling.d.ts","sourceRoot":"","sources":["../../../src/features/authz/grant-ceiling.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"grant-ceiling.d.ts","sourceRoot":"","sources":["../../../src/features/authz/grant-ceiling.ts"],"names":[],"mappings":"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"}
@@ -4,6 +4,8 @@ exports.GrantCeiling = exports.GrantCeilingError = void 0;
4
4
  exports.subtreeOf = subtreeOf;
5
5
  exports.coversKey = coversKey;
6
6
  const core_1 = require("@astram/core");
7
+ const engine_1 = require("@astram/engine");
8
+ const org_bootstrap_manifest_js_1 = require("../org-bootstrap/org-bootstrap.manifest.js");
7
9
  const http_js_1 = require("../shared/http.js");
8
10
  const db_permission_facts_adapter_js_1 = require("./db-permission-facts.adapter.js");
9
11
  const role_binding_js_1 = require("./role-binding.js");
@@ -18,6 +20,15 @@ class GrantCeilingError extends Error {
18
20
  }
19
21
  }
20
22
  exports.GrantCeilingError = GrantCeilingError;
23
+ function operationCovers(actor, roleIds, opts) {
24
+ const allowed = opts?.operationRoleIds;
25
+ if (allowed === undefined || allowed.length === 0 || roleIds.length === 0)
26
+ return false;
27
+ if ((0, engine_1.operationOf)(actor)?.operationKey !== org_bootstrap_manifest_js_1.ORG_BOOTSTRAP_OPERATION)
28
+ return false;
29
+ const set = new Set(allowed);
30
+ return roleIds.every((id) => set.has(id));
31
+ }
21
32
  /** The actor's reachable subtree: the session's cached closure, else the self node (non-widening). */
22
33
  function subtreeOf(actor) {
23
34
  return actor.scopeSubtreeNodeIds?.length ? actor.scopeSubtreeNodeIds : [actor.tenantId];
@@ -160,9 +171,11 @@ class GrantCeiling {
160
171
  * A role holding nothing assigns nothing and passes; a role key the registry does not know
161
172
  * counts as not held (Phase 8 #4). Root bypasses. Store outage → 503.
162
173
  */
163
- async assertCanAssignRoles(actor, roleIds) {
174
+ async assertCanAssignRoles(actor, roleIds, opts) {
164
175
  if (actor.isRoot === true)
165
176
  return;
177
+ if (operationCovers(actor, roleIds, opts))
178
+ return;
166
179
  const ids = [...new Set(roleIds)];
167
180
  if (ids.length === 0)
168
181
  return;
@@ -372,9 +385,11 @@ class GrantCeiling {
372
385
  * role not bound in the subtree is "not found" for the actor (no existence leak); a role also
373
386
  * used outside (a shared role) is root-only. Root bypasses.
374
387
  */
375
- async assertRoleAdministrable(actor, roleUuid) {
388
+ async assertRoleAdministrable(actor, roleUuid, opts) {
376
389
  if (actor.isRoot === true)
377
390
  return;
391
+ if (operationCovers(actor, [roleUuid], opts))
392
+ return;
378
393
  GrantCeiling.checkAdministrable(await this.roleBinding(actor, roleUuid));
379
394
  }
380
395
  /**
@@ -382,9 +397,11 @@ class GrantCeiling {
382
397
  * subtree or not bound at all — never a role also used outside it (a shared role's assignment
383
398
  * would widen another tenant's role). Pair with {@link assertCanAssignRoles}. Root bypasses.
384
399
  */
385
- async assertRoleAssignable(actor, roleUuid) {
400
+ async assertRoleAssignable(actor, roleUuid, opts) {
386
401
  if (actor.isRoot === true)
387
402
  return;
403
+ if (operationCovers(actor, [roleUuid], opts))
404
+ return;
388
405
  GrantCeiling.checkAssignable(await this.roleBinding(actor, roleUuid));
389
406
  }
390
407
  }
@@ -16,6 +16,14 @@ import type { FrameworkSchemas } from '@astram/core';
16
16
  import { type RecordAuthorizationInputs } from '@astram/guard';
17
17
  import type { ObjectTypeInput } from '@astram/engine';
18
18
  import type { IdentitySchemaNames } from '../../schema.js';
19
+ /**
20
+ * A pool-size variable: unset or blank gives `undefined`; otherwise plain digits, a positive safe
21
+ * integer, or it throws naming the variable (never the value). The single-layout pool budget in
22
+ * `@astram/guard` (`loadPoolBudget`) applies the same rule, so the budget counts the pools that open.
23
+ */
24
+ export declare function strictPoolSize(raw: string | undefined, name: string): number | undefined;
25
+ /** The authz pool cap: `APP_AUTHZ_POOL_MAX` (strict), else 4. */
26
+ export declare function authzPoolLimit(env?: NodeJS.ProcessEnv): number;
19
27
  /**
20
28
  * A lean `mariadb` pool for the authz grant-reads (built from `APP_DB_*` env via guard's
21
29
  * `loadDbConfig`). Kept small — these are pre-gate coarse-grant reads, not the main CRUD pool.
@@ -33,7 +41,7 @@ export declare function createIdentityAuthzPool(schemas?: FrameworkSchemas): Poo
33
41
  * - `registry` — bridged from `identityObjects` PLUS the consumer's `extraObjects` (their
34
42
  * `<module>:<type>:<action>` keys project into a principal's grants at gate 5; without them a
35
43
  * consumer type is `resource_not_registered`),
36
- * - `operationManifest` — empty (identity declares no trusted delegated operations).
44
+ * - `operationManifest` — the `shield:org-bootstrap` steps (G-009); no other delegated operation.
37
45
  */
38
46
  export declare function buildRecordAuthorizationInputs(pool: Pool, extraObjects?: readonly ObjectTypeInput[],
39
47
  /** Resolved framework names for the facts adapter (absent ⇒ from the namespace, once). */
@@ -1 +1 @@
1
- {"version":3,"file":"record-authorization.d.ts","sourceRoot":"","sources":["../../../src/features/authz/record-authorization.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAgB,EAAE,KAAK,IAAI,EAAE,MAAM,SAAS,CAAC;AAC7C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACrD,OAAO,EAIL,KAAK,yBAAyB,EAC/B,MAAM,eAAe,CAAC;AACvB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAItD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAE3D;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,CAAC,EAAE,gBAAgB,GAAG,IAAI,CAaxE;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,8BAA8B,CAC5C,IAAI,EAAE,IAAI,EACV,YAAY,GAAE,SAAS,eAAe,EAAO;AAC7C,0FAA0F;AAC1F,OAAO,CAAC,EAAE,mBAAmB,GAC5B,yBAAyB,CAS3B"}
1
+ {"version":3,"file":"record-authorization.d.ts","sourceRoot":"","sources":["../../../src/features/authz/record-authorization.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAgB,EAAE,KAAK,IAAI,EAAE,MAAM,SAAS,CAAC;AAC7C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACrD,OAAO,EAIL,KAAK,yBAAyB,EAC/B,MAAM,eAAe,CAAC;AACvB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAKtD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAE3D;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAMxF;AAED,iEAAiE;AACjE,wBAAgB,cAAc,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,MAAM,CAE3E;AAED;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,CAAC,EAAE,gBAAgB,GAAG,IAAI,CAaxE;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,8BAA8B,CAC5C,IAAI,EAAE,IAAI,EACV,YAAY,GAAE,SAAS,eAAe,EAAO;AAC7C,0FAA0F;AAC1F,OAAO,CAAC,EAAE,mBAAmB,GAC5B,yBAAyB,CAS3B"}
@@ -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
  /**
@@ -20,9 +22,28 @@ exports.buildRecordAuthorizationInputs = buildRecordAuthorizationInputs;
20
22
  */
21
23
  const mariadb_1 = __importDefault(require("mariadb"));
22
24
  const guard_1 = require("@astram/guard");
25
+ const org_bootstrap_manifest_js_1 = require("../org-bootstrap/org-bootstrap.manifest.js");
23
26
  const objects_js_1 = require("../../objects.js");
24
27
  const db_permission_facts_adapter_js_1 = require("./db-permission-facts.adapter.js");
25
28
  const session_facts_reader_js_1 = require("./session-facts.reader.js");
29
+ /**
30
+ * A pool-size variable: unset or blank gives `undefined`; otherwise plain digits, a positive safe
31
+ * integer, or it throws naming the variable (never the value). The single-layout pool budget in
32
+ * `@astram/guard` (`loadPoolBudget`) applies the same rule, so the budget counts the pools that open.
33
+ */
34
+ function strictPoolSize(raw, name) {
35
+ if (raw === undefined || raw.trim() === '')
36
+ return undefined;
37
+ const t = raw.trim();
38
+ const n = /^[0-9]+$/.test(t) ? Number(t) : Number.NaN;
39
+ if (!Number.isSafeInteger(n) || n < 1)
40
+ throw new Error(`${name} must be a positive integer`);
41
+ return n;
42
+ }
43
+ /** The authz pool cap: `APP_AUTHZ_POOL_MAX` (strict), else 4. */
44
+ function authzPoolLimit(env = process.env) {
45
+ return strictPoolSize(env.APP_AUTHZ_POOL_MAX, 'APP_AUTHZ_POOL_MAX') ?? 4;
46
+ }
26
47
  /**
27
48
  * A lean `mariadb` pool for the authz grant-reads (built from `APP_DB_*` env via guard's
28
49
  * `loadDbConfig`). Kept small — these are pre-gate coarse-grant reads, not the main CRUD pool.
@@ -39,7 +60,7 @@ function createIdentityAuthzPool(schemas) {
39
60
  user: db.user,
40
61
  password: db.password,
41
62
  database: db.database,
42
- connectionLimit: Number(process.env.APP_AUTHZ_POOL_MAX ?? 4),
63
+ connectionLimit: authzPoolLimit(process.env),
43
64
  // Phase 16: APP_DB_TLS / APP_DB_TLS_CA (production accounts are REQUIRE SSL).
44
65
  ...(ssl ? { ssl } : {}),
45
66
  });
@@ -53,7 +74,7 @@ function createIdentityAuthzPool(schemas) {
53
74
  * - `registry` — bridged from `identityObjects` PLUS the consumer's `extraObjects` (their
54
75
  * `<module>:<type>:<action>` keys project into a principal's grants at gate 5; without them a
55
76
  * consumer type is `resource_not_registered`),
56
- * - `operationManifest` — empty (identity declares no trusted delegated operations).
77
+ * - `operationManifest` — the `shield:org-bootstrap` steps (G-009); no other delegated operation.
57
78
  */
58
79
  function buildRecordAuthorizationInputs(pool, extraObjects = [],
59
80
  /** Resolved framework names for the facts adapter (absent ⇒ from the namespace, once). */
@@ -64,6 +85,6 @@ schemas) {
64
85
  permissionFacts: new db_permission_facts_adapter_js_1.DbPermissionFactsAdapter(pool, registry, undefined, schemas),
65
86
  sessionFacts: new session_facts_reader_js_1.AlsSessionFactsReader(),
66
87
  registry,
67
- operationManifest: [],
88
+ operationManifest: org_bootstrap_manifest_js_1.ORG_BOOTSTRAP_MANIFEST,
68
89
  };
69
90
  }
@@ -115,6 +115,12 @@ export declare class CompanyHierarchyService {
115
115
  * Every change is audited on this transaction (names; ids only in structured attributes).
116
116
  */
117
117
  private retireDependents;
118
+ /**
119
+ * Step 5 of {@link retireDependents} (G-009 T14, SPEC §5 v4): the roles the company owns become
120
+ * shared. Each owner is cleared through the audited `transitionRoleOwner` seam on the delete's
121
+ * connection (the FK's SET NULL would do it silently, without an audit row).
122
+ */
123
+ private clearRoleOwners;
118
124
  /** Step 4 of {@link retireDependents}: revoke and audit the grants naming the company. */
119
125
  private revokeGrantsNaming;
120
126
  /** Lock the company, then require dominance over each of its members (see `remove`). */
@@ -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;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"}
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;AASxF,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;IAmF9B;;;;OAIG;YACW,eAAe;IA6B7B,0FAA0F;YAC5E,kBAAkB;IAmDhC,wFAAwF;YAC1E,sBAAsB;IAgCpC,wFAAwF;IAClF,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAGhD"}
@@ -6,6 +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 role_owner_transition_js_1 = require("../role/role-owner-transition.js");
9
10
  const schema_js_1 = require("../../schema.js");
10
11
  /** A JSON-array column (the driver returns an array OR a string) → its string items; else []. */
11
12
  function jsonIdArray(v) {
@@ -233,8 +234,26 @@ class CompanyHierarchyService {
233
234
  });
234
235
  }
235
236
  await this.revokeGrantsNaming(conn, actor, nodeUuid, companyName);
237
+ await this.clearRoleOwners(conn, actor, nodeUuid, companyName);
236
238
  return [...touched];
237
239
  }
240
+ /**
241
+ * Step 5 of {@link retireDependents} (G-009 T14, SPEC §5 v4): the roles the company owns become
242
+ * shared. Each owner is cleared through the audited `transitionRoleOwner` seam on the delete's
243
+ * connection (the FK's SET NULL would do it silently, without an audit row).
244
+ */
245
+ async clearRoleOwners(conn, actor, nodeUuid, companyName) {
246
+ const owned = (await conn.query(`SELECT r_uuid, r_name FROM \`${this.schemas.app}\`.\`roles\` WHERE r_owner_company_uuid = ? ORDER BY r_uuid FOR UPDATE`, [nodeUuid]));
247
+ for (const role of owned) {
248
+ await (0, role_owner_transition_js_1.transitionRoleOwner)((sql, params) => conn.query(sql, params), role.r_uuid, nodeUuid, null, (action, attributes, query) => this.recordAudit({ query }, actor, action, {
249
+ eventType: 'role',
250
+ ...attributes,
251
+ role: role.r_name,
252
+ company: companyName,
253
+ reason: 'company_deleted',
254
+ }), this.schemas);
255
+ }
256
+ }
238
257
  /** Step 4 of {@link retireDependents}: revoke and audit the grants naming the company. */
239
258
  async revokeGrantsNaming(conn, actor, nodeUuid, companyName) {
240
259
  const grants = (await conn.query(`SELECT ctg_uuid, ctg_owning_tenant_uuid, ctg_grantee_tenant_uuid, ctg_grantee_kind,
@@ -1,6 +1,6 @@
1
1
  import { type DynamicModule } from '@nestjs/common';
2
2
  import { type FrameworkSchemas } from '@astram/core';
3
- import { type ObjectTypeInput } from '@astram/engine';
3
+ import { type EngineDbConfig, type ObjectTypeInput } from '@astram/engine';
4
4
  /**
5
5
  * The boot check behind {@link IDENTITY_DECISION_AUTHORITY_CHECK}: with this module wired, the
6
6
  * request path must decide with the authority guard built from this module's inputs. A missing
@@ -30,6 +30,12 @@ export declare function assertFrameworkDatabaseAgrees(identityDatabase: string,
30
30
  readonly database?: string;
31
31
  };
32
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;
33
39
  /**
34
40
  * IdentityFeatureModule — the framework-owned identity FEATURE surface (company/user/role CRUD;
35
41
  * more in later phases) as thin controllers over the engine's RecordsService. The CONSUMER imports
@@ -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;AAEpE,OAAO,EAGL,KAAK,gBAAgB,EAEtB,MAAM,cAAc,CAAC;AACtB,OAAO,EAeL,KAAK,eAAe,EACrB,MAAM,gBAAgB,CAAC;AA0GxB;;;;;;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,CAuBN;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;AAwBD;;;;;;;;;;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"}
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;AA+GxB;;;;;;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;CAqkBvB"}
@@ -11,11 +11,13 @@ exports.IdentityFeatureModule = void 0;
11
11
  exports.assertRequestPathUsesIdentityAuthorization = assertRequestPathUsesIdentityAuthorization;
12
12
  exports.assertFrameworkSchemasAgree = assertFrameworkSchemasAgree;
13
13
  exports.assertFrameworkDatabaseAgrees = assertFrameworkDatabaseAgrees;
14
+ exports.engineDbFromEnv = engineDbFromEnv;
14
15
  const common_1 = require("@nestjs/common");
15
16
  const core_1 = require("@nestjs/core");
16
17
  const core_2 = require("@astram/core");
17
18
  const engine_1 = require("@astram/engine");
18
19
  const guard_1 = require("@astram/guard");
20
+ const internal_1 = require("@astram/guard/internal");
19
21
  const objects_js_1 = require("../objects.js");
20
22
  const index_js_1 = require("./access/index.js");
21
23
  const company_service_js_1 = require("./company/company.service.js");
@@ -25,7 +27,9 @@ const role_service_js_1 = require("./role/role.service.js");
25
27
  const role_controller_js_1 = require("./role/role.controller.js");
26
28
  const user_service_js_1 = require("./user/user.service.js");
27
29
  const email_address_changer_token_js_1 = require("./user/email-address-changer.token.js");
30
+ const credential_provisioner_token_js_1 = require("./user/credential-provisioner.token.js");
28
31
  const framework_schemas_token_js_1 = require("./framework-schemas.token.js");
32
+ const org_bootstrap_tokens_js_1 = require("./org-bootstrap/org-bootstrap.tokens.js");
29
33
  const user_controller_js_1 = require("./user/user.controller.js");
30
34
  const membership_controller_js_1 = require("./membership/membership.controller.js");
31
35
  const membership_service_js_1 = require("./membership/membership.service.js");
@@ -107,7 +111,7 @@ const IDENTITY_FRAMEWORK_SCHEMAS_CHECK = Symbol('IDENTITY_FRAMEWORK_SCHEMAS_CHEC
107
111
  * Throws (fail closed) with the namespaces and names only.
108
112
  */
109
113
  function assertFrameworkSchemasAgree(identity, guard) {
110
- const names = (s) => `namespace=${s.namespace} app=${s.app} identity=${s.identity} authz=${s.authz} audit=${s.audit}`;
114
+ const names = (s) => `namespace=${s.namespace} app=${s.app} identity=${s.identity} authz=${s.authz} audit=${s.audit} layout=${s.layout ?? 'four'}`;
111
115
  if (guard === undefined || guard === null) {
112
116
  if (identity.isDefault)
113
117
  return true;
@@ -117,7 +121,8 @@ function assertFrameworkSchemasAgree(identity, guard) {
117
121
  const same = identity.app === guard.app &&
118
122
  identity.identity === guard.identity &&
119
123
  identity.authz === guard.authz &&
120
- identity.audit === guard.audit;
124
+ identity.audit === guard.audit &&
125
+ (identity.layout ?? 'four') === (guard.layout ?? 'four');
121
126
  if (!same) {
122
127
  throw new Error(`IdentityFeatureModule: framework database names disagree — identity ${names(identity)}; ` +
123
128
  `guard ${names(guard)}. Pass the same names (or none) to ShieldModule and ` +
@@ -147,8 +152,13 @@ function assertFrameworkDatabaseAgrees(identityDatabase, guardConfig) {
147
152
  * database and default user follow `schemas` (the names `forRoot` resolved or was given); an
148
153
  * explicit `APP_DB_USER` / `APP_DB_NAME` still wins (QA P2-5).
149
154
  */
150
- function engineDbFromEnv(schemas) {
151
- const db = (0, guard_1.loadDbConfig)(process.env, schemas);
155
+ function engineDbFromEnv(schemas, env = process.env) {
156
+ const db = (0, guard_1.loadDbConfig)(env, schemas);
157
+ // POOL_MAX_ENGINE, else APP_DB_MAX_CONNECTIONS, else 10; a set but invalid value throws naming
158
+ // the variable. Guard's single-layout pool budget parses these the same way.
159
+ const connectionLimit = (0, record_authorization_js_1.strictPoolSize)(env.POOL_MAX_ENGINE, 'POOL_MAX_ENGINE') ??
160
+ (0, record_authorization_js_1.strictPoolSize)(env.APP_DB_MAX_CONNECTIONS, 'APP_DB_MAX_CONNECTIONS') ??
161
+ 10;
152
162
  const ssl = (0, guard_1.mariadbSslOption)(db.tls);
153
163
  return {
154
164
  host: db.host,
@@ -158,7 +168,7 @@ function engineDbFromEnv(schemas) {
158
168
  database: db.database,
159
169
  // The engine's runtime CRUD pool cap. A conservative default; the app can raise it later via
160
170
  // an explicit EngineModule.forRoot if it needs more. (ADR-0018 budget lives app-side.)
161
- connectionLimit: Number(process.env.APP_DB_MAX_CONNECTIONS ?? 10),
171
+ connectionLimit,
162
172
  // Phase 16: APP_DB_TLS / APP_DB_TLS_CA (production accounts are REQUIRE SSL).
163
173
  ...(ssl ? { ssl } : {}),
164
174
  };
@@ -196,6 +206,8 @@ let IdentityFeatureModule = IdentityFeatureModule_1 = class IdentityFeatureModul
196
206
  * differ from guard's `FRAMEWORK_SCHEMAS`. Never read per request.
197
207
  */
198
208
  static forRoot({ rootIds, extraObjects = [], schemas: givenSchemas, } = {}) {
209
+ // G-009 N4: a reserved operation principal is never a root, whatever variable `rootIds` came from.
210
+ (0, guard_1.assertNoReservedRootId)(rootIds);
199
211
  // The framework database names, resolved ONCE (boot) for every SQL path built below.
200
212
  // Caller-supplied names are checked before any SQL is built (fail closed, Shield #1).
201
213
  const schemas = givenSchemas !== undefined
@@ -578,9 +590,17 @@ let IdentityFeatureModule = IdentityFeatureModule_1 = class IdentityFeatureModul
578
590
  provide: email_address_changer_token_js_1.IDENTITY_EMAIL_ADDRESS_CHANGER,
579
591
  useValue: new guard_1.MariaDbEmailAddressChanger(authzPool, Date.now, schemas),
580
592
  },
593
+ // G-009 T7 (OD-6): the locked-credential writer UserService runs on the create transaction.
594
+ {
595
+ provide: credential_provisioner_token_js_1.IDENTITY_CREDENTIAL_PROVISIONER,
596
+ useValue: new internal_1.MariaDbCredentialProvisioner(authzPool, Date.now, schemas),
597
+ },
581
598
  // The same resolved names for identity services Nest constructs (UserService's
582
599
  // email-change audit sink), never a second env read.
583
600
  { provide: framework_schemas_token_js_1.IDENTITY_FRAMEWORK_SCHEMAS, useValue: schemas },
601
+ // G-009 T10: the ONE shared authz pool, for the org bootstrap runner (named lock, state reads,
602
+ // claim and root-flag writes). Not exported; the runner reads it with `app.get({ strict: false })`.
603
+ { provide: org_bootstrap_tokens_js_1.IDENTITY_AUTHZ_POOL, useValue: authzPool },
584
604
  // ADR-0035 (GAPS #6): attach the guard's Redis permission-version cache to the ONE shared
585
605
  // bumper, so every committed bump (postings, grants, hierarchy) is published to Redis,
586
606
  // where gate 5 reads it. Provided by the auth surface under `auth: true` (through the
@@ -41,6 +41,15 @@ export declare class MembershipService {
41
41
  * swallowed here). `createdBy` is stamped server-side (actorId-trusted).
42
42
  */
43
43
  addMembership(actor: Principal, userUuid: string, companyUuid: string): Promise<RecordId>;
44
+ /**
45
+ * Add an ACTIVE membership for `(userUuid, companyUuid)` in one create (G-009 R4): a brand-new
46
+ * membership has no other active card to displace, so no create-then-activate pair. A
47
+ * membership for that pair already existing is an `IdentityConflictError`, from the pre-check
48
+ * and from the `(cm_user_uuid, cm_company_uuid)` unique index (the pre-check read is SQL-scoped
49
+ * and can miss a row for an operation-bound principal); the driver message is never echoed.
50
+ * Bumps the company's tenant. The reach checks of `addMembership` apply unchanged.
51
+ */
52
+ addActiveMembership(actor: Principal, userUuid: string, companyUuid: string): Promise<RecordId>;
44
53
  /** List a user's membership cards (tenant-scoped by the pipe). */
45
54
  listMemberships(userUuid: string): Promise<readonly RecordView[]>;
46
55
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"membership.service.d.ts","sourceRoot":"","sources":["../../../src/features/membership/membership.service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,EAAE,cAAc,EAAE,KAAK,QAAQ,EAAE,KAAK,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAChF,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAwB,KAAK,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAE1F,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAEzD,qBACa,iBAAiB;IAE1B,OAAO,CAAC,QAAQ,CAAC,OAAO;IAGxB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAQxB,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC;gBAXZ,OAAO,EAAE,cAAc,EAGvB,OAAO,EAAE,YAAY,EAQrB,WAAW,CAAC,GAAE,kBAAkB,GAAG,IAAI,aAAA;IAG1D;;;;OAIG;YACW,WAAW;IAQzB;;;;;;OAMG;YACW,WAAW;IAYzB;;;;;;OAMG;IACG,aAAa,CAAC,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC;IAkB/F,kEAAkE;IAC5D,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,UAAU,EAAE,CAAC;IAKvE;;;;;;;;;;;;;;;;;OAiBG;IACG,SAAS,CAAC,KAAK,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAsC1F;;;;OAIG;IACG,gBAAgB,CACpB,KAAK,EAAE,SAAS,EAChB,cAAc,EAAE,MAAM,EACtB,KAAK,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,GACzB,OAAO,CAAC,IAAI,CAAC;IAchB;;;OAGG;IACG,gBAAgB,CAAC,KAAK,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAYhF"}
1
+ {"version":3,"file":"membership.service.d.ts","sourceRoot":"","sources":["../../../src/features/membership/membership.service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,EAAE,cAAc,EAAE,KAAK,QAAQ,EAAE,KAAK,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAChF,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAwB,KAAK,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAE1F,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AASzD,qBACa,iBAAiB;IAE1B,OAAO,CAAC,QAAQ,CAAC,OAAO;IAGxB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAQxB,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC;gBAXZ,OAAO,EAAE,cAAc,EAGvB,OAAO,EAAE,YAAY,EAQrB,WAAW,CAAC,GAAE,kBAAkB,GAAG,IAAI,aAAA;IAG1D;;;;OAIG;YACW,WAAW;IAQzB;;;;;;OAMG;YACW,WAAW;IAYzB;;;;;;OAMG;IACG,aAAa,CAAC,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC;IAkB/F;;;;;;;OAOG;IACG,mBAAmB,CACvB,KAAK,EAAE,SAAS,EAChB,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,QAAQ,CAAC;IAwBpB,kEAAkE;IAC5D,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,UAAU,EAAE,CAAC;IAKvE;;;;;;;;;;;;;;;;;OAiBG;IACG,SAAS,CAAC,KAAK,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAsC1F;;;;OAIG;IACG,gBAAgB,CACpB,KAAK,EAAE,SAAS,EAChB,cAAc,EAAE,MAAM,EACtB,KAAK,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,GACzB,OAAO,CAAC,IAAI,CAAC;IAchB;;;OAGG;IACG,gBAAgB,CAAC,KAAK,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAYhF"}
@@ -30,6 +30,13 @@ const engine_1 = require("@astram/engine");
30
30
  const posting_version_bump_js_1 = require("./posting-version-bump.js");
31
31
  const identity_errors_js_1 = require("../shared/identity-errors.js");
32
32
  const grant_ceiling_js_1 = require("../authz/grant-ceiling.js");
33
+ /** A driver duplicate-key error (errno 1062), recognised by errno/code only, never its message. */
34
+ function isDuplicateKey(err) {
35
+ if (typeof err !== 'object' || err === null)
36
+ return false;
37
+ const e = err;
38
+ return e.errno === 1062 || e.code === 'ER_DUP_ENTRY';
39
+ }
33
40
  let MembershipService = class MembershipService {
34
41
  records;
35
42
  ceiling;
@@ -101,6 +108,39 @@ let MembershipService = class MembershipService {
101
108
  fields: { userUuid, companyUuid, isActive: false, status: 'active' },
102
109
  });
103
110
  }
111
+ /**
112
+ * Add an ACTIVE membership for `(userUuid, companyUuid)` in one create (G-009 R4): a brand-new
113
+ * membership has no other active card to displace, so no create-then-activate pair. A
114
+ * membership for that pair already existing is an `IdentityConflictError`, from the pre-check
115
+ * and from the `(cm_user_uuid, cm_company_uuid)` unique index (the pre-check read is SQL-scoped
116
+ * and can miss a row for an operation-bound principal); the driver message is never echoed.
117
+ * Bumps the company's tenant. The reach checks of `addMembership` apply unchanged.
118
+ */
119
+ async addActiveMembership(actor, userUuid, companyUuid) {
120
+ if (actor.isRoot !== true) {
121
+ const user = await this.records.get('user', userUuid);
122
+ if (user === null)
123
+ throw new identity_errors_js_1.IdentityNotFoundError('User not found.');
124
+ }
125
+ const existing = await this.records.list('company_membership', { where: { userUuid } });
126
+ if (existing.rows.some((m) => m.fields?.companyUuid === companyUuid)) {
127
+ throw new identity_errors_js_1.IdentityConflictError('That user already has a membership in this company.');
128
+ }
129
+ let id;
130
+ try {
131
+ id = await this.records.create('company_membership', {
132
+ fields: { userUuid, companyUuid, isActive: true, status: 'active' },
133
+ });
134
+ }
135
+ catch (err) {
136
+ if (isDuplicateKey(err)) {
137
+ throw new identity_errors_js_1.IdentityConflictError('That user already has a membership in this company.');
138
+ }
139
+ throw err;
140
+ }
141
+ await this.bumpTenants([companyUuid]);
142
+ return id;
143
+ }
104
144
  /** List a user's membership cards (tenant-scoped by the pipe). */
105
145
  async listMemberships(userUuid) {
106
146
  const page = await this.records.list('company_membership', { where: { userUuid } });
@@ -53,5 +53,23 @@ export declare class PostingService {
53
53
  * runs BEFORE any write; the writes run in one transaction (see the file header).
54
54
  */
55
55
  replacePostings(actor: Principal, membershipUuid: string, desired: readonly PostingInput[]): Promise<void>;
56
+ /**
57
+ * Add postings to a membership WITHOUT touching the rest of its set (G-009 R4/R5): only tuples
58
+ * (role|department|branch) not already present are created; nothing is updated or deleted. The
59
+ * pre-checks, the in-transaction lock + re-check, the atomicity and the version bump are those
60
+ * of `replacePostings` (shared helpers). `opts.operationRoleIds` is forwarded to the ceiling
61
+ * calls (the org-bootstrap operation's declared roles). An inactive membership is refused.
62
+ */
63
+ addPostings(actor: Principal, membershipUuid: string, toAdd: readonly PostingInput[], opts?: {
64
+ operationRoleIds?: ReadonlySet<string> | readonly string[];
65
+ }): Promise<void>;
66
+ private assertEveryRole;
67
+ /** The membership through the subtree read wall (404 when out of reach) and its company. */
68
+ private reachMembership;
69
+ /** Authority for ADDED postings (binding rule, ceiling, org units); returns the distinct roles. */
70
+ private assertMayAdd;
71
+ /** Lock each added role row on the transaction, then re-run the binding rule (review F2). */
72
+ private lockAndRecheck;
73
+ private bumpTenant;
56
74
  }
57
75
  //# sourceMappingURL=posting.service.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"posting.service.d.ts","sourceRoot":"","sources":["../../../src/features/membership/posting.service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAGH,OAAO,EAAE,cAAc,EAAE,KAAK,UAAU,EAAE,MAAM,gBAAgB,CAAC;AACjE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAwB,KAAK,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC1F,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAGzD;;;;;GAKG;AACH,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,aAAa,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;IACvC,cAAc,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;CACzC;AA0ED,qBACa,cAAc;IAEvB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAGxB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAKxB,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC;gBARZ,OAAO,EAAE,cAAc,EAGvB,OAAO,EAAE,YAAY,EAKrB,WAAW,CAAC,GAAE,kBAAkB,GAAG,IAAI,aAAA;IAG1D,gEAAgE;IAC1D,YAAY,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,UAAU,EAAE,CAAC;IAK1E;;;;;OAKG;IACG,eAAe,CACnB,KAAK,EAAE,SAAS,EAChB,cAAc,EAAE,MAAM,EACtB,OAAO,EAAE,SAAS,YAAY,EAAE,GAC/B,OAAO,CAAC,IAAI,CAAC;CA8GjB"}
1
+ {"version":3,"file":"posting.service.d.ts","sourceRoot":"","sources":["../../../src/features/membership/posting.service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAGH,OAAO,EAAE,cAAc,EAAE,KAAK,UAAU,EAAE,MAAM,gBAAgB,CAAC;AACjE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAwB,KAAK,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC1F,OAAO,EAAE,YAAY,EAA6B,MAAM,2BAA2B,CAAC;AAOpF;;;;;GAKG;AACH,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,aAAa,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;IACvC,cAAc,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;CACzC;AA0ED,qBACa,cAAc;IAEvB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAGxB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAKxB,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC;gBARZ,OAAO,EAAE,cAAc,EAGvB,OAAO,EAAE,YAAY,EAKrB,WAAW,CAAC,GAAE,kBAAkB,GAAG,IAAI,aAAA;IAG1D,gEAAgE;IAC1D,YAAY,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,UAAU,EAAE,CAAC;IAK1E;;;;;OAKG;IACG,eAAe,CACnB,KAAK,EAAE,SAAS,EAChB,cAAc,EAAE,MAAM,EACtB,OAAO,EAAE,SAAS,YAAY,EAAE,GAC/B,OAAO,CAAC,IAAI,CAAC;IAyEhB;;;;;;OAMG;IACG,WAAW,CACf,KAAK,EAAE,SAAS,EAChB,cAAc,EAAE,MAAM,EACtB,KAAK,EAAE,SAAS,YAAY,EAAE,EAC9B,IAAI,CAAC,EAAE;QAAE,gBAAgB,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,GAAG,SAAS,MAAM,EAAE,CAAA;KAAE,GACpE,OAAO,CAAC,IAAI,CAAC;IA2ChB,OAAO,CAAC,eAAe;IAQvB,4FAA4F;YAC9E,eAAe;IAY7B,mGAAmG;YACrF,YAAY;IAqB1B,6FAA6F;YAC/E,cAAc;YAYd,UAAU;CAkBzB"}