@astram/identity 0.1.2 → 0.2.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 (139) hide show
  1. package/dist/auth-facts-keys.d.ts +3 -0
  2. package/dist/auth-facts-keys.d.ts.map +1 -0
  3. package/dist/auth-facts-keys.js +24 -0
  4. package/dist/cross-tenant-grant.object.d.ts +28 -0
  5. package/dist/cross-tenant-grant.object.d.ts.map +1 -0
  6. package/dist/cross-tenant-grant.object.js +64 -0
  7. package/dist/features/access/access-admin.access.d.ts +3 -0
  8. package/dist/features/access/access-admin.access.d.ts.map +1 -0
  9. package/dist/features/access/access-admin.access.js +27 -0
  10. package/dist/features/access/company-admin.access.d.ts +3 -0
  11. package/dist/features/access/company-admin.access.d.ts.map +1 -0
  12. package/dist/features/access/company-admin.access.js +31 -0
  13. package/dist/features/access/index.d.ts +15 -0
  14. package/dist/features/access/index.d.ts.map +1 -0
  15. package/dist/features/access/index.js +26 -0
  16. package/dist/features/access/user-management.access.d.ts +3 -0
  17. package/dist/features/access/user-management.access.d.ts.map +1 -0
  18. package/dist/features/access/user-management.access.js +35 -0
  19. package/dist/features/audit/audit-read.service.d.ts +92 -0
  20. package/dist/features/audit/audit-read.service.d.ts.map +1 -0
  21. package/dist/features/audit/audit-read.service.js +97 -0
  22. package/dist/features/audit/audit.controller.d.ts +35 -0
  23. package/dist/features/audit/audit.controller.d.ts.map +1 -0
  24. package/dist/features/audit/audit.controller.js +73 -0
  25. package/dist/features/audit/directory-label-resolver.d.ts +14 -0
  26. package/dist/features/audit/directory-label-resolver.d.ts.map +1 -0
  27. package/dist/features/audit/directory-label-resolver.js +46 -0
  28. package/dist/features/audit/mariadb-audit-reader.d.ts +46 -0
  29. package/dist/features/audit/mariadb-audit-reader.d.ts.map +1 -0
  30. package/dist/features/audit/mariadb-audit-reader.js +116 -0
  31. package/dist/features/authz/access-bundle-reconcile.job.d.ts +35 -0
  32. package/dist/features/authz/access-bundle-reconcile.job.d.ts.map +1 -0
  33. package/dist/features/authz/access-bundle-reconcile.job.js +84 -0
  34. package/dist/features/authz/access-bundle-store.d.ts +65 -0
  35. package/dist/features/authz/access-bundle-store.d.ts.map +1 -0
  36. package/dist/features/authz/access-bundle-store.js +154 -0
  37. package/dist/features/authz/catalog-bootstrap.provider.d.ts +28 -0
  38. package/dist/features/authz/catalog-bootstrap.provider.d.ts.map +1 -0
  39. package/dist/features/authz/catalog-bootstrap.provider.js +85 -0
  40. package/dist/features/authz/catalog-reconcile.job.d.ts +23 -0
  41. package/dist/features/authz/catalog-reconcile.job.d.ts.map +1 -0
  42. package/dist/features/authz/catalog-reconcile.job.js +47 -0
  43. package/dist/features/authz/db-permission-facts.adapter.d.ts +60 -0
  44. package/dist/features/authz/db-permission-facts.adapter.d.ts.map +1 -0
  45. package/dist/features/authz/db-permission-facts.adapter.js +116 -0
  46. package/dist/features/authz/grant-admin.controller.d.ts +96 -0
  47. package/dist/features/authz/grant-admin.controller.d.ts.map +1 -0
  48. package/dist/features/authz/grant-admin.controller.js +209 -0
  49. package/dist/features/authz/grant-admin.service.d.ts +107 -0
  50. package/dist/features/authz/grant-admin.service.d.ts.map +1 -0
  51. package/dist/features/authz/grant-admin.service.js +297 -0
  52. package/dist/features/authz/mariadb-catalog-store.d.ts +16 -0
  53. package/dist/features/authz/mariadb-catalog-store.d.ts.map +1 -0
  54. package/dist/features/authz/mariadb-catalog-store.js +44 -0
  55. package/dist/features/authz/permission-version.bumper.d.ts +47 -0
  56. package/dist/features/authz/permission-version.bumper.d.ts.map +1 -0
  57. package/dist/features/authz/permission-version.bumper.js +68 -0
  58. package/dist/features/authz/record-authorization.d.ts +31 -0
  59. package/dist/features/authz/record-authorization.d.ts.map +1 -0
  60. package/dist/features/authz/record-authorization.js +59 -0
  61. package/dist/features/authz/session-facts.reader.d.ts +41 -0
  62. package/dist/features/authz/session-facts.reader.d.ts.map +1 -0
  63. package/dist/features/authz/session-facts.reader.js +27 -0
  64. package/dist/features/company/company.controller.d.ts +37 -0
  65. package/dist/features/company/company.controller.d.ts.map +1 -0
  66. package/dist/features/company/company.controller.js +100 -0
  67. package/dist/features/company/company.service.d.ts +25 -0
  68. package/dist/features/company/company.service.d.ts.map +1 -0
  69. package/dist/features/company/company.service.js +53 -0
  70. package/dist/features/cross-tenant/cross-tenant-grant-read.service.d.ts +126 -0
  71. package/dist/features/cross-tenant/cross-tenant-grant-read.service.d.ts.map +1 -0
  72. package/dist/features/cross-tenant/cross-tenant-grant-read.service.js +276 -0
  73. package/dist/features/cross-tenant/cross-tenant-grant.controller.d.ts +65 -0
  74. package/dist/features/cross-tenant/cross-tenant-grant.controller.d.ts.map +1 -0
  75. package/dist/features/cross-tenant/cross-tenant-grant.controller.js +250 -0
  76. package/dist/features/cross-tenant/grant-authorize.d.ts +42 -0
  77. package/dist/features/cross-tenant/grant-authorize.d.ts.map +1 -0
  78. package/dist/features/cross-tenant/grant-authorize.js +17 -0
  79. package/dist/features/identity-feature.module.d.ts +26 -0
  80. package/dist/features/identity-feature.module.d.ts.map +1 -0
  81. package/dist/features/identity-feature.module.js +247 -0
  82. package/dist/features/membership/membership.controller.d.ts +77 -0
  83. package/dist/features/membership/membership.controller.d.ts.map +1 -0
  84. package/dist/features/membership/membership.controller.js +174 -0
  85. package/dist/features/membership/membership.service.d.ts +73 -0
  86. package/dist/features/membership/membership.service.d.ts.map +1 -0
  87. package/dist/features/membership/membership.service.js +173 -0
  88. package/dist/features/membership/posting-version-bump.d.ts +10 -0
  89. package/dist/features/membership/posting-version-bump.d.ts.map +1 -0
  90. package/dist/features/membership/posting-version-bump.js +4 -0
  91. package/dist/features/membership/posting.service.d.ts +59 -0
  92. package/dist/features/membership/posting.service.d.ts.map +1 -0
  93. package/dist/features/membership/posting.service.js +181 -0
  94. package/dist/features/org-structure/org-catalog.controller.d.ts +14 -0
  95. package/dist/features/org-structure/org-catalog.controller.d.ts.map +1 -0
  96. package/dist/features/org-structure/org-catalog.controller.js +62 -0
  97. package/dist/features/org-structure/org-catalog.service.d.ts +19 -0
  98. package/dist/features/org-structure/org-catalog.service.d.ts.map +1 -0
  99. package/dist/features/org-structure/org-catalog.service.js +53 -0
  100. package/dist/features/role/role.controller.d.ts +37 -0
  101. package/dist/features/role/role.controller.d.ts.map +1 -0
  102. package/dist/features/role/role.controller.js +104 -0
  103. package/dist/features/role/role.service.d.ts +24 -0
  104. package/dist/features/role/role.service.d.ts.map +1 -0
  105. package/dist/features/role/role.service.js +53 -0
  106. package/dist/features/shared/http.d.ts +15 -0
  107. package/dist/features/shared/http.d.ts.map +1 -0
  108. package/dist/features/shared/http.js +25 -0
  109. package/dist/features/shared/list-query.d.ts +44 -0
  110. package/dist/features/shared/list-query.d.ts.map +1 -0
  111. package/dist/features/shared/list-query.js +53 -0
  112. package/dist/features/shared/named-option.d.ts +6 -0
  113. package/dist/features/shared/named-option.d.ts.map +1 -0
  114. package/dist/features/shared/named-option.js +2 -0
  115. package/dist/features/shared/record-resolver.d.ts +11 -0
  116. package/dist/features/shared/record-resolver.d.ts.map +1 -0
  117. package/dist/features/shared/record-resolver.js +32 -0
  118. package/dist/features/user/user.controller.d.ts +39 -0
  119. package/dist/features/user/user.controller.d.ts.map +1 -0
  120. package/dist/features/user/user.controller.js +104 -0
  121. package/dist/features/user/user.service.d.ts +26 -0
  122. package/dist/features/user/user.service.d.ts.map +1 -0
  123. package/dist/features/user/user.service.js +53 -0
  124. package/dist/index.d.ts +34 -3
  125. package/dist/index.d.ts.map +1 -1
  126. package/dist/index.js +66 -33
  127. package/dist/objects.d.ts +13 -0
  128. package/dist/objects.d.ts.map +1 -0
  129. package/dist/objects.js +20 -0
  130. package/dist/shared-resource.object.d.ts +28 -0
  131. package/dist/shared-resource.object.d.ts.map +1 -0
  132. package/dist/shared-resource.object.js +63 -0
  133. package/migrations/1710000000010-CreateAuditLog.cjs +42 -0
  134. package/migrations/1710000000011-CreateCrossTenantGrant.cjs +66 -0
  135. package/migrations/1710000000012-SeedCrossTenantGrantPerms.cjs +86 -0
  136. package/migrations/1710000000013-CreateSharedResource.cjs +30 -0
  137. package/migrations/1710000000099-SeedDevIdentity.cjs +130 -0
  138. package/migrations/1710000000100-SeedDevTenantBAndRoot.cjs +212 -0
  139. package/package.json +4 -3
@@ -0,0 +1,59 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.createIdentityAuthzPool = createIdentityAuthzPool;
7
+ exports.buildIdentityRecordAuthorization = buildIdentityRecordAuthorization;
8
+ /**
9
+ * Assemble the record-tier authorization wiring for `@astram/identity`: the lean grant-read
10
+ * pool factory + the `recordAuthorization` inputs the consumer passes to
11
+ * `ShieldModule.forRoot({ recordAuthorization })`. Once wired, `decide()` becomes REAL (the
12
+ * framework's four record-tier adapters are built internally over the supplied pool) instead
13
+ * of the deny-everything stub.
14
+ *
15
+ * Modeled on astram3's `buildAuthorizationInputs` (`server/src/features/authz/authz.wiring.ts`),
16
+ * adapted to `@astram/identity`'s own registrations (`identityObjects`) and env prefix
17
+ * (`APP_DB_*`, per the Task-7 rename).
18
+ */
19
+ const mariadb_1 = __importDefault(require("mariadb"));
20
+ const guard_1 = require("@astram/guard");
21
+ const objects_js_1 = require("../../objects.js");
22
+ const db_permission_facts_adapter_js_1 = require("./db-permission-facts.adapter.js");
23
+ const session_facts_reader_js_1 = require("./session-facts.reader.js");
24
+ /**
25
+ * A lean `mariadb` pool for the authz grant-reads (built from `APP_DB_*` env via guard's
26
+ * `loadDbConfig`). Kept small — these are pre-gate coarse-grant reads, not the main CRUD pool.
27
+ * Opens the pool at CALL time (never at import) — the consumer (app lifecycle) owns closing it.
28
+ */
29
+ function createIdentityAuthzPool() {
30
+ const db = (0, guard_1.loadDbConfig)();
31
+ return mariadb_1.default.createPool({
32
+ host: db.host,
33
+ port: db.port,
34
+ user: db.user,
35
+ password: db.password,
36
+ database: db.database,
37
+ connectionLimit: Number(process.env.APP_AUTHZ_POOL_MAX ?? 4),
38
+ });
39
+ }
40
+ /**
41
+ * Build the `RecordAuthorizationInputs` the consumer passes to
42
+ * `ShieldModule.forRoot({ recordAuthorization })`. The framework owns the four record-tier
43
+ * adapters internally; `@astram/identity` supplies only the app-schema ports:
44
+ * - `permissionFacts` — coarse grants over `shield_authz.role_permission` +
45
+ * `company_membership`/`membership_posting`,
46
+ * - `sessionFacts` — the ADR-0011 ALS-backed per-request reader,
47
+ * - `registry` — bridged from `identityObjects`,
48
+ * - `operationManifest` — empty (identity declares no trusted delegated operations).
49
+ */
50
+ function buildIdentityRecordAuthorization(pool) {
51
+ const registry = (0, guard_1.buildIdentityModuleRegistry)(objects_js_1.identityObjects);
52
+ return {
53
+ pool,
54
+ permissionFacts: new db_permission_facts_adapter_js_1.DbPermissionFactsAdapter(pool, registry),
55
+ sessionFacts: new session_facts_reader_js_1.AlsSessionFactsReader(),
56
+ registry,
57
+ operationManifest: [],
58
+ };
59
+ }
@@ -0,0 +1,41 @@
1
+ /**
2
+ * The per-request session-facts reader (ADR-0011 hot path) — ported from astram3's
3
+ * `AlsSessionFactsReader` (`server/src/features/authz/empty-ports.ts`).
4
+ *
5
+ * Reads THIS request's cached coarse grants out of the framework's SHARED session-facts
6
+ * `AsyncLocalStorage` (`SHIELD_SESSION_FACTS_ALS`, exported from `@astram/guard`) — the SAME
7
+ * instance the enforcement guard ENTERS around its `decide()` call. When the guard entered
8
+ * with the request's projected `cachedFacts`, gate 5 can serve the coarse decision from the
9
+ * cache instead of a DB reload — provided the cached version is still fresh; a stale/absent
10
+ * cache still reloads (gate 5's version gate).
11
+ *
12
+ * ⛔ FAIL-CLOSED + CROSS-PRINCIPAL ISOLATION: the reader holds NO per-request state on `this`
13
+ * — only the shared ALS handle. `getStore()` returns the CURRENT async context's store (the
14
+ * guard's `als.run(...)` around decide() binds it to this request only), so a concurrent
15
+ * request never sees another's facts. Outside any scope, or when the guard entered with
16
+ * `cachedFacts: null` (no/malformed projection), `getStore()` is `undefined` → the reader
17
+ * returns `{ cachedFacts: null, permissionsVersion: 0 }` → gate 5 DB-reloads. A cache miss
18
+ * NEVER fabricates a permit.
19
+ */
20
+ import type { SessionFactsReader } from '@astram/core';
21
+ import { type SessionFactsStore } from '@astram/guard';
22
+ /**
23
+ * A {@link SessionFactsReader} whose getters read the request's facts out of the framework's
24
+ * shared session-facts ALS. Constructed with that exact singleton by default (imported from
25
+ * `@astram/guard`, the SAME one the guard enters), so the reader and the guard share one
26
+ * store — no duplicate ALS.
27
+ */
28
+ export declare class AlsSessionFactsReader implements SessionFactsReader {
29
+ /** The SHARED ALS the enforcement guard enters (default = the framework singleton). */
30
+ private readonly als;
31
+ constructor(
32
+ /** The SHARED ALS the enforcement guard enters (default = the framework singleton). */
33
+ als?: {
34
+ getStore(): SessionFactsStore | undefined;
35
+ });
36
+ /** THIS request's cached coarse grants, or `null` (no scope / no facts → gate 5 reloads). */
37
+ get cachedFacts(): unknown;
38
+ /** The version the cached facts were projected at (0 when absent → treated as stale). */
39
+ get permissionsVersion(): number;
40
+ }
41
+ //# sourceMappingURL=session-facts.reader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-facts.reader.d.ts","sourceRoot":"","sources":["../../../src/features/authz/session-facts.reader.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AACvD,OAAO,EAA8C,KAAK,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAEnG;;;;;GAKG;AACH,qBAAa,qBAAsB,YAAW,kBAAkB;IAE5D,uFAAuF;IACvF,OAAO,CAAC,QAAQ,CAAC,GAAG;;IADpB,uFAAuF;IACtE,GAAG,GAAE;QACpB,QAAQ,IAAI,iBAAiB,GAAG,SAAS,CAAC;KAChB;IAG9B,6FAA6F;IAC7F,IAAI,WAAW,IAAI,OAAO,CAEzB;IAED,yFAAyF;IACzF,IAAI,kBAAkB,IAAI,MAAM,CAE/B;CACF"}
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AlsSessionFactsReader = void 0;
4
+ const guard_1 = require("@astram/guard");
5
+ /**
6
+ * A {@link SessionFactsReader} whose getters read the request's facts out of the framework's
7
+ * shared session-facts ALS. Constructed with that exact singleton by default (imported from
8
+ * `@astram/guard`, the SAME one the guard enters), so the reader and the guard share one
9
+ * store — no duplicate ALS.
10
+ */
11
+ class AlsSessionFactsReader {
12
+ als;
13
+ constructor(
14
+ /** The SHARED ALS the enforcement guard enters (default = the framework singleton). */
15
+ als = guard_1.SHIELD_SESSION_FACTS_ALS) {
16
+ this.als = als;
17
+ }
18
+ /** THIS request's cached coarse grants, or `null` (no scope / no facts → gate 5 reloads). */
19
+ get cachedFacts() {
20
+ return (this.als.getStore() ?? guard_1.NO_SESSION_FACTS).cachedFacts;
21
+ }
22
+ /** The version the cached facts were projected at (0 when absent → treated as stale). */
23
+ get permissionsVersion() {
24
+ return (this.als.getStore() ?? guard_1.NO_SESSION_FACTS).permissionsVersion;
25
+ }
26
+ }
27
+ exports.AlsSessionFactsReader = AlsSessionFactsReader;
@@ -0,0 +1,37 @@
1
+ import { CompanyService } from './company.service.js';
2
+ import { type ListQueryDto } from '../shared/list-query.js';
3
+ export declare class CompanyController {
4
+ private readonly companies;
5
+ constructor(companies: CompanyService);
6
+ /**
7
+ * List companies — gated on `identity:company:read`. Server-side DataTable:
8
+ * `?page&limit&search&sortBy&sortOrder`. Returns `{ total, rows, page, limit }` —
9
+ * `total` reflects the FILTERED set so the client can page the true result count.
10
+ */
11
+ listCompanies(query: ListQueryDto): Promise<{
12
+ total: number;
13
+ rows: readonly import("packages/engine/dist/types.js").RecordView[];
14
+ page: number;
15
+ limit: number;
16
+ }>;
17
+ /** Create a company (a new tenant) — gated on `identity:company:create`. */
18
+ createCompany(body: {
19
+ name?: string;
20
+ }): Promise<{
21
+ id: import("packages/engine/dist/types.js").RecordId;
22
+ }>;
23
+ /** Read one company — gated on `identity:company:read`. */
24
+ getCompany(uuid: string): Promise<import("packages/engine/dist/types.js").RecordView | null>;
25
+ /** Update a company — gated on `identity:company:update`. */
26
+ updateCompany(uuid: string, body: {
27
+ name?: string;
28
+ status?: string;
29
+ }): Promise<{
30
+ success: boolean;
31
+ }>;
32
+ /** Delete a company — gated on `identity:company:delete`. */
33
+ deleteCompany(uuid: string): Promise<{
34
+ success: boolean;
35
+ }>;
36
+ }
37
+ //# sourceMappingURL=company.controller.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"company.controller.d.ts","sourceRoot":"","sources":["../../../src/features/company/company.controller.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAkB,KAAK,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAE5E,qBACa,iBAAiB;IAChB,OAAO,CAAC,QAAQ,CAAC,SAAS;gBAAT,SAAS,EAAE,cAAc;IAEtD;;;;OAIG;IAGG,aAAa,CAAU,KAAK,EAAE,YAAY;;;;;;IAMhD,4EAA4E;IAGtE,aAAa,CAAS,IAAI,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE;;;IAKnD,2DAA2D;IAG3D,UAAU,CAAgB,IAAI,EAAE,MAAM;IAItC,6DAA6D;IAGvD,aAAa,CAAgB,IAAI,EAAE,MAAM,EAAU,IAAI,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE;;;IAKjG,6DAA6D;IAGvD,aAAa,CAAgB,IAAI,EAAE,MAAM;;;CAIhD"}
@@ -0,0 +1,100 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
12
+ return function (target, key) { decorator(target, key, paramIndex); }
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.CompanyController = void 0;
16
+ const common_1 = require("@nestjs/common");
17
+ const guard_1 = require("@astram/guard");
18
+ const company_service_js_1 = require("./company.service.js");
19
+ const list_query_js_1 = require("../shared/list-query.js");
20
+ let CompanyController = class CompanyController {
21
+ companies;
22
+ constructor(companies) {
23
+ this.companies = companies;
24
+ }
25
+ /**
26
+ * List companies — gated on `identity:company:read`. Server-side DataTable:
27
+ * `?page&limit&search&sortBy&sortOrder`. Returns `{ total, rows, page, limit }` —
28
+ * `total` reflects the FILTERED set so the client can page the true result count.
29
+ */
30
+ async listCompanies(query) {
31
+ const { page, limit, params } = (0, list_query_js_1.parseListQuery)(query);
32
+ const result = await this.companies.listCompanies(params);
33
+ return { total: result.total, rows: result.rows, page, limit };
34
+ }
35
+ /** Create a company (a new tenant) — gated on `identity:company:create`. */
36
+ async createCompany(body) {
37
+ const id = await this.companies.createCompany(body?.name ?? 'Untitled');
38
+ return { id };
39
+ }
40
+ /** Read one company — gated on `identity:company:read`. */
41
+ getCompany(uuid) {
42
+ return this.companies.getCompany(uuid);
43
+ }
44
+ /** Update a company — gated on `identity:company:update`. */
45
+ async updateCompany(uuid, body) {
46
+ await this.companies.updateCompany(uuid, body ?? {});
47
+ return { success: true };
48
+ }
49
+ /** Delete a company — gated on `identity:company:delete`. */
50
+ async deleteCompany(uuid) {
51
+ await this.companies.deleteCompany(uuid);
52
+ return { success: true };
53
+ }
54
+ };
55
+ exports.CompanyController = CompanyController;
56
+ __decorate([
57
+ (0, common_1.Get)('companies'),
58
+ (0, guard_1.Classified)({ typeKey: 'company', action: 'read' }),
59
+ __param(0, (0, common_1.Query)()),
60
+ __metadata("design:type", Function),
61
+ __metadata("design:paramtypes", [Object]),
62
+ __metadata("design:returntype", Promise)
63
+ ], CompanyController.prototype, "listCompanies", null);
64
+ __decorate([
65
+ (0, common_1.Post)('companies'),
66
+ (0, guard_1.Classified)({ typeKey: 'company', action: 'create' }),
67
+ __param(0, (0, common_1.Body)()),
68
+ __metadata("design:type", Function),
69
+ __metadata("design:paramtypes", [Object]),
70
+ __metadata("design:returntype", Promise)
71
+ ], CompanyController.prototype, "createCompany", null);
72
+ __decorate([
73
+ (0, common_1.Get)('companies/:uuid'),
74
+ (0, guard_1.Classified)({ typeKey: 'company', action: 'read' }),
75
+ __param(0, (0, common_1.Param)('uuid')),
76
+ __metadata("design:type", Function),
77
+ __metadata("design:paramtypes", [String]),
78
+ __metadata("design:returntype", void 0)
79
+ ], CompanyController.prototype, "getCompany", null);
80
+ __decorate([
81
+ (0, common_1.Put)('companies/:uuid'),
82
+ (0, guard_1.Classified)({ typeKey: 'company', action: 'update' }),
83
+ __param(0, (0, common_1.Param)('uuid')),
84
+ __param(1, (0, common_1.Body)()),
85
+ __metadata("design:type", Function),
86
+ __metadata("design:paramtypes", [String, Object]),
87
+ __metadata("design:returntype", Promise)
88
+ ], CompanyController.prototype, "updateCompany", null);
89
+ __decorate([
90
+ (0, common_1.Delete)('companies/:uuid'),
91
+ (0, guard_1.Classified)({ typeKey: 'company', action: 'delete' }),
92
+ __param(0, (0, common_1.Param)('uuid')),
93
+ __metadata("design:type", Function),
94
+ __metadata("design:paramtypes", [String]),
95
+ __metadata("design:returntype", Promise)
96
+ ], CompanyController.prototype, "deleteCompany", null);
97
+ exports.CompanyController = CompanyController = __decorate([
98
+ (0, common_1.Controller)('identity'),
99
+ __metadata("design:paramtypes", [company_service_js_1.CompanyService])
100
+ ], CompanyController);
@@ -0,0 +1,25 @@
1
+ import { RecordsService, type RecordId } from '@astram/engine';
2
+ import { type ListParams } from '../shared/list-query.js';
3
+ /**
4
+ * CompanyService — company (tenant) CRUD on the paved road, via the framework's
5
+ * RecordsService facade. The facade supplies the acting identity (the request
6
+ * principal) — this service never threads a Principal. No raw SQL, no RECORD_ENGINE.
7
+ */
8
+ export declare class CompanyService {
9
+ private readonly records;
10
+ constructor(records: RecordsService);
11
+ /** Create a company (tenant). */
12
+ createCompany(name: string): Promise<RecordId>;
13
+ /** List companies (tenant-scoped by the pipe), with paging/search/sort. */
14
+ listCompanies(params?: ListParams): Promise<import("@astram/engine").RecordPage>;
15
+ /** Read a single company. */
16
+ getCompany(uuid: string): Promise<import("@astram/engine").RecordView | null>;
17
+ /** Update a company. */
18
+ updateCompany(uuid: string, patch: {
19
+ name?: string;
20
+ status?: string;
21
+ }): Promise<void>;
22
+ /** Delete a company. */
23
+ deleteCompany(uuid: string): Promise<void>;
24
+ }
25
+ //# sourceMappingURL=company.service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"company.service.d.ts","sourceRoot":"","sources":["../../../src/features/company/company.service.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,KAAK,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC/D,OAAO,EAAkB,KAAK,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAK1E;;;;GAIG;AACH,qBACa,cAAc;IACb,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAP,OAAO,EAAE,cAAc;IAEpD,iCAAiC;IACjC,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC;IAI9C,2EAA2E;IAC3E,aAAa,CAAC,MAAM,CAAC,EAAE,UAAU;IAIjC,6BAA6B;IAC7B,UAAU,CAAC,IAAI,EAAE,MAAM;IAIvB,wBAAwB;IACxB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAIrF,wBAAwB;IACxB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAG3C"}
@@ -0,0 +1,53 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.CompanyService = void 0;
13
+ const common_1 = require("@nestjs/common");
14
+ const engine_1 = require("@astram/engine");
15
+ const list_query_js_1 = require("../shared/list-query.js");
16
+ const COMPANY_SEARCH_FIELDS = ['name'];
17
+ const COMPANY_SORT_FIELDS = ['name', 'status', 'createdAt'];
18
+ /**
19
+ * CompanyService — company (tenant) CRUD on the paved road, via the framework's
20
+ * RecordsService facade. The facade supplies the acting identity (the request
21
+ * principal) — this service never threads a Principal. No raw SQL, no RECORD_ENGINE.
22
+ */
23
+ let CompanyService = class CompanyService {
24
+ records;
25
+ constructor(records) {
26
+ this.records = records;
27
+ }
28
+ /** Create a company (tenant). */
29
+ createCompany(name) {
30
+ return this.records.create('company', { fields: { name } });
31
+ }
32
+ /** List companies (tenant-scoped by the pipe), with paging/search/sort. */
33
+ listCompanies(params) {
34
+ return this.records.list('company', (0, list_query_js_1.buildListQuery)(params, COMPANY_SEARCH_FIELDS, COMPANY_SORT_FIELDS));
35
+ }
36
+ /** Read a single company. */
37
+ getCompany(uuid) {
38
+ return this.records.get('company', uuid);
39
+ }
40
+ /** Update a company. */
41
+ updateCompany(uuid, patch) {
42
+ return this.records.update('company', uuid, { fields: patch });
43
+ }
44
+ /** Delete a company. */
45
+ deleteCompany(uuid) {
46
+ return this.records.delete('company', uuid);
47
+ }
48
+ };
49
+ exports.CompanyService = CompanyService;
50
+ exports.CompanyService = CompanyService = __decorate([
51
+ (0, common_1.Injectable)(),
52
+ __metadata("design:paramtypes", [engine_1.RecordsService])
53
+ ], CompanyService);
@@ -0,0 +1,126 @@
1
+ import type { Pool } from 'mariadb';
2
+ import type { ObjectTypeInput } from '@astram/engine';
3
+ /** One row in the grantee's pending-offers inbox. */
4
+ export interface InboxGrantRow {
5
+ uuid: string;
6
+ owningTenantName: string;
7
+ granteeKind: 'user' | 'role';
8
+ granteeName: string;
9
+ typeKey: string;
10
+ typeLabel: string;
11
+ scope: string;
12
+ allowedActions: string[];
13
+ offeredByName: string;
14
+ expiresAtMs: number;
15
+ }
16
+ /** One row of a grant MY tenant owns (any status) — the "outgoing" list. */
17
+ export interface MineGrantRow {
18
+ uuid: string;
19
+ granteeTenantName: string;
20
+ granteeKind: 'user' | 'role';
21
+ granteeName: string;
22
+ typeKey: string;
23
+ typeLabel: string;
24
+ scope: string;
25
+ allowedActions: string[];
26
+ status: string;
27
+ offeredByName: string;
28
+ expiresAtMs: number;
29
+ }
30
+ /** One row of a grant accepted INTO my tenant — the "incoming" (active) list. */
31
+ export interface IncomingGrantRow {
32
+ uuid: string;
33
+ owningTenantName: string;
34
+ granteeKind: 'user' | 'role';
35
+ granteeName: string;
36
+ typeKey: string;
37
+ typeLabel: string;
38
+ scope: string;
39
+ allowedActions: string[];
40
+ offeredByName: string;
41
+ expiresAtMs: number;
42
+ }
43
+ /** One offerable object type for the offer form's picker (decision 1: tenantScoped only). */
44
+ export interface OfferableTypeRow {
45
+ typeKey: string;
46
+ label: string;
47
+ actions: string[];
48
+ }
49
+ export declare class CrossTenantGrantReadService {
50
+ private readonly pool;
51
+ private readonly offerableTypeInputs;
52
+ private readonly actionsByType;
53
+ /**
54
+ * @param pool the shared resolver pool.
55
+ * @param offerableTypeInputs the ENGINE registrations to filter to `access.tenantScoped
56
+ * === true` types for the offer form's picker (decision 1). Passed in (not imported
57
+ * directly) so this service stays a plain constructor-arg class like its siblings —
58
+ * the module supplies the same merged `[...identityObjects]` set the share-type
59
+ * surface uses, so a future tenantScoped type is picked up the same way it becomes
60
+ * shareable.
61
+ * @param actionsByType per-typeKey action vocabulary for the offerable-types read
62
+ * (decision 1: "the actions per type come from the type's own action set"). Callers
63
+ * pass `actionsFor`, which defaults every type to the CRUD `IDENTITY_ACTIONS` except
64
+ * a type with its own override (e.g. `cross_tenant_grant` -> share/accept/revoke) —
65
+ * the single source every action-vocabulary consumer uses, so a per-type set can
66
+ * never drift between callers.
67
+ */
68
+ constructor(pool: Pool, offerableTypeInputs: readonly ObjectTypeInput[], actionsByType: (typeKey: string) => readonly string[]);
69
+ /**
70
+ * Pending offers sitting in the CALLER's tenant inbox: `ctg_grantee_tenant_uuid =
71
+ * tenantId AND ctg_status = 'offered' AND ctg_expires_at_ms > nowMs` (an expired
72
+ * offer is dead — never surfaced as actionable, mirroring the writer's own active-
73
+ * grant filter). Ordered soonest-expiring first (most urgent to act on).
74
+ */
75
+ listInbox(tenantId: string, nowMs: number): Promise<InboxGrantRow[]>;
76
+ /**
77
+ * Every grant MY tenant OWNS (`ctg_owning_tenant_uuid = tenantId`), ANY status — the
78
+ * "outgoing" list (what I've offered, whether still pending, accepted, or revoked).
79
+ * Ordered most-recently-offered first (`ctg_created_at DESC`) — the natural order
80
+ * for "what have I done lately" on an owner's own outgoing list.
81
+ */
82
+ listMine(tenantId: string): Promise<MineGrantRow[]>;
83
+ /**
84
+ * Grants ACCEPTED into my tenant (`ctg_grantee_tenant_uuid = tenantId AND ctg_status
85
+ * = 'accepted'`) — the "incoming" (currently-active reach-in) list, distinct from the
86
+ * pending inbox. Includes grants whose expiry has since passed (this reports status as
87
+ * stored — `CrossTenantGrantPort.findActiveGrant` is the ACTUAL enforcement of
88
+ * liveness; this is a read-model list, not a re-implementation of that check), so the
89
+ * UI can show "expired" via `expiresAtMs` if desired. Ordered soonest-expiring first.
90
+ */
91
+ listIncoming(tenantId: string): Promise<IncomingGrantRow[]>;
92
+ /**
93
+ * The ONE source of truth for "which typeKeys are offerable across tenants" (decision
94
+ * 1: `access.tenantScoped === true`). Reflects the SAME engine registrations the
95
+ * share surface merges (`[...identityObjects]`, passed in by the module) and returns
96
+ * the set of tenantScoped typeKeys. Both the picker read (`listOfferableTypes`) AND the
97
+ * offer-endpoint guard (`isOfferableType`) derive from this — so there is no way for
98
+ * the two to drift (a system type can never be BOTH shown in the picker AND accepted
99
+ * by the offer endpoint, or neither).
100
+ */
101
+ private offerableTypeKeys;
102
+ /**
103
+ * Is `typeKey` offerable across tenants (i.e. a registered tenantScoped type)? The
104
+ * offer controller calls this BEFORE `writer.offer()` so an un-offerable typeKey (any
105
+ * `system` type — e.g. `company`) is rejected with a 400 before any row is written.
106
+ *
107
+ * This closes the H3 defense-in-depth gap: the writer's `assertValidOffer` only checks
108
+ * `typeKey` is a non-empty string, so without this guard a legit own-tenant admin could
109
+ * mint a junk `offered` grant row for a `system` type. It's inert at evaluation TODAY
110
+ * only because no type is both `system` AND `tenantScoped` (system types have no
111
+ * `trusted:'tenantId'`, so the tenant gate never consults a cross-tenant grant for
112
+ * them) — but that safety is a coincidence, not an explicit rule. This makes it
113
+ * explicit, using the SAME tenantScoped filter as the picker (`offerableTypeKeys`).
114
+ */
115
+ isOfferableType(typeKey: string): boolean;
116
+ /**
117
+ * The object-type picker source for the offer form (decision 1): the tenantScoped
118
+ * registered types (from `offerableTypeKeys` — the same source `isOfferableType`
119
+ * uses) paired with each type's action vocabulary. No DB query — pure in-memory
120
+ * reflection, wrapped in a resolved promise so the controller's read-surface shape is
121
+ * uniform. The identity types are all `system` and are excluded by construction —
122
+ * only a tenantScoped type (e.g. `shared_resource`) qualifies.
123
+ */
124
+ listOfferableTypes(): Promise<OfferableTypeRow[]>;
125
+ }
126
+ //# sourceMappingURL=cross-tenant-grant-read.service.d.ts.map
@@ -0,0 +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;AAMtD,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;AA+FD,qBACa,2BAA2B;IAiBpC,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,mBAAmB;IACpC,OAAO,CAAC,QAAQ,CAAC,aAAa;IAlBhC;;;;;;;;;;;;;;OAcG;gBAEgB,IAAI,EAAE,IAAI,EACV,mBAAmB,EAAE,SAAS,eAAe,EAAE,EAC/C,aAAa,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,SAAS,MAAM,EAAE;IAGxE;;;;;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"}