@astram/identity 0.1.2 → 0.2.1

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 +5 -4
@@ -0,0 +1,85 @@
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.CatalogBootstrapProvider = void 0;
16
+ // Ported from astram3 `server/src/features/authz/catalog-bootstrap.provider.ts`.
17
+ // Faithful port; imports repointed to the framework's guard/engine + local jobs. The
18
+ // astram3 `./module-registry.bridge` import is repointed to `buildIdentityModuleRegistry`
19
+ // exported from @astram/guard.
20
+ const common_1 = require("@nestjs/common");
21
+ const core_1 = require("@nestjs/core");
22
+ const guard_1 = require("@astram/guard");
23
+ const engine_1 = require("@astram/engine");
24
+ const catalog_reconcile_job_js_1 = require("./catalog-reconcile.job.js");
25
+ const access_bundle_reconcile_job_js_1 = require("./access-bundle-reconcile.job.js");
26
+ const MODULE_KEY = 'identity';
27
+ const RECONCILE_INTERVAL_MS = 60 * 60 * 1000; // hourly; honors DISABLE_ALL_CRONS below
28
+ /**
29
+ * Boot: read-only verifyGatable (fail-closed — a stranded/unclassified route aborts
30
+ * boot). Post-boot: kick the mutating reconcile once, then on an interval (single
31
+ * process = single runner in dev; a clustered deploy would gate this behind a leader
32
+ * lock — deferred). DISABLE_ALL_CRONS disables the interval (boot verify still runs).
33
+ *
34
+ * The mutating half (the `CatalogReconcileJob`, holding the latest report for
35
+ * /health/catalog) is supplied by DI via the module factory — the SAME instance the
36
+ * health controller injects — so verify, the post-boot pass, and the endpoint all share
37
+ * one job. This provider owns only the boot verify + the schedule.
38
+ */
39
+ let CatalogBootstrapProvider = class CatalogBootstrapProvider {
40
+ discovery;
41
+ scanner;
42
+ reflector;
43
+ job;
44
+ accessBundleJob;
45
+ registrations;
46
+ timer = null;
47
+ constructor(discovery, scanner, reflector, job, accessBundleJob, registrations) {
48
+ this.discovery = discovery;
49
+ this.scanner = scanner;
50
+ this.reflector = reflector;
51
+ this.job = job;
52
+ this.accessBundleJob = accessBundleJob;
53
+ this.registrations = registrations;
54
+ }
55
+ async onApplicationBootstrap() {
56
+ const registry = (0, guard_1.buildIdentityModuleRegistry)(this.registrations);
57
+ const inventoryScanner = new guard_1.OperationInventoryScanner(this.discovery, this.scanner, this.reflector);
58
+ const inventory = inventoryScanner.scan(MODULE_KEY);
59
+ // Fail-closed (ADR-0002): a route that can't be gated aborts boot.
60
+ new guard_1.CatalogReconciler().verifyGatable(inventory, registry);
61
+ // Mutating reconcile runs AFTER boot (not blocking, not fail-closed).
62
+ const nowIso = new Date().toISOString();
63
+ await this.job.runOnce(nowIso);
64
+ // Bundles reconcile AFTER permissions (same post-boot pass): a bundle validates
65
+ // its keys against the live permission catalog, so permissions must reconcile first.
66
+ await this.accessBundleJob.runOnce(nowIso);
67
+ if (process.env.DISABLE_ALL_CRONS !== 'true') {
68
+ this.timer = setInterval(() => {
69
+ const tickIso = new Date().toISOString();
70
+ void this.job.runOnce(tickIso).then(() => this.accessBundleJob.runOnce(tickIso));
71
+ }, RECONCILE_INTERVAL_MS);
72
+ this.timer.unref?.();
73
+ }
74
+ }
75
+ };
76
+ exports.CatalogBootstrapProvider = CatalogBootstrapProvider;
77
+ exports.CatalogBootstrapProvider = CatalogBootstrapProvider = __decorate([
78
+ (0, common_1.Injectable)(),
79
+ __param(5, (0, common_1.Inject)(engine_1.ENGINE_ALL_REGISTRATIONS)),
80
+ __metadata("design:paramtypes", [core_1.DiscoveryService,
81
+ core_1.MetadataScanner,
82
+ core_1.Reflector,
83
+ catalog_reconcile_job_js_1.CatalogReconcileJob,
84
+ access_bundle_reconcile_job_js_1.AccessBundleReconcileJob, Array])
85
+ ], CatalogBootstrapProvider);
@@ -0,0 +1,23 @@
1
+ import type { Pool } from 'mariadb';
2
+ import { type ReconcileReport } from '@astram/guard';
3
+ import type { ModuleRegistry } from '@astram/core';
4
+ /**
5
+ * Single-runner wrapper around the framework CatalogReconciler.reconcile (the mutating
6
+ * half). Called once shortly after boot and on a cron. Holds the latest report for
7
+ * /health/catalog. NOT run at boot (boot only does the read-only verifyGatable).
8
+ *
9
+ * Timestamp is supplied by the caller (policy: pass new Date().toISOString() from the
10
+ * scheduler, not inside the reconciler — keeps the framework fn pure).
11
+ */
12
+ export declare class CatalogReconcileJob {
13
+ private readonly registry;
14
+ private readonly reconciler;
15
+ private readonly store;
16
+ private latest;
17
+ private running;
18
+ constructor(pool: Pool, registry: ModuleRegistry);
19
+ /** Idempotent single pass. Guarded so overlapping ticks don't double-run. */
20
+ runOnce(nowIso: string): Promise<ReconcileReport | null>;
21
+ getLatest(): ReconcileReport | null;
22
+ }
23
+ //# sourceMappingURL=catalog-reconcile.job.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"catalog-reconcile.job.d.ts","sourceRoot":"","sources":["../../../src/features/authz/catalog-reconcile.job.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AACpC,OAAO,EAAqB,KAAK,eAAe,EAAE,MAAM,eAAe,CAAC;AACxE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAGnD;;;;;;;GAOG;AACH,qBAAa,mBAAmB;IAMN,OAAO,CAAC,QAAQ,CAAC,QAAQ;IALjD,OAAO,CAAC,QAAQ,CAAC,UAAU,CAA2B;IACtD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAsB;IAC5C,OAAO,CAAC,MAAM,CAAgC;IAC9C,OAAO,CAAC,OAAO,CAAS;gBAEZ,IAAI,EAAE,IAAI,EAAmB,QAAQ,EAAE,cAAc;IAIjE,6EAA6E;IACvE,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC;IAmB9D,SAAS,IAAI,eAAe,GAAG,IAAI;CAGpC"}
@@ -0,0 +1,47 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CatalogReconcileJob = void 0;
4
+ const guard_1 = require("@astram/guard");
5
+ const mariadb_catalog_store_js_1 = require("./mariadb-catalog-store.js");
6
+ /**
7
+ * Single-runner wrapper around the framework CatalogReconciler.reconcile (the mutating
8
+ * half). Called once shortly after boot and on a cron. Holds the latest report for
9
+ * /health/catalog. NOT run at boot (boot only does the read-only verifyGatable).
10
+ *
11
+ * Timestamp is supplied by the caller (policy: pass new Date().toISOString() from the
12
+ * scheduler, not inside the reconciler — keeps the framework fn pure).
13
+ */
14
+ class CatalogReconcileJob {
15
+ registry;
16
+ reconciler = new guard_1.CatalogReconciler();
17
+ store;
18
+ latest = null;
19
+ running = false;
20
+ constructor(pool, registry) {
21
+ this.registry = registry;
22
+ this.store = new mariadb_catalog_store_js_1.MariaDbCatalogStore(pool);
23
+ }
24
+ /** Idempotent single pass. Guarded so overlapping ticks don't double-run. */
25
+ async runOnce(nowIso) {
26
+ if (this.running)
27
+ return this.latest;
28
+ this.running = true;
29
+ try {
30
+ const report = await this.reconciler.reconcile(this.registry, this.store, nowIso);
31
+ this.latest = report;
32
+ const orphanNote = report.orphaned.length
33
+ ? ` — ORPHANED: ${report.orphaned.join(', ')}`
34
+ : '';
35
+ console.log(`[catalog] reconcile: +${report.added.length} added, ${report.orphaned.length} orphaned, ` +
36
+ `${report.reactivated.length} reactivated${orphanNote}`);
37
+ return report;
38
+ }
39
+ finally {
40
+ this.running = false;
41
+ }
42
+ }
43
+ getLatest() {
44
+ return this.latest;
45
+ }
46
+ }
47
+ exports.CatalogReconcileJob = CatalogReconcileJob;
@@ -0,0 +1,60 @@
1
+ /**
2
+ * PermissionFactsPort adapter — resolves a principal's coarse grants (the set of exact
3
+ * permission keys it holds via its roles) for core's object-type gate.
4
+ *
5
+ * roleIds → role_permission rows (exact OR wildcard strings) → expand wildcards against the
6
+ * registry's real action universe → de-duplicated exact keys. Core then does exact `.includes`.
7
+ *
8
+ * FAIL-CLOSED (ADR-0016): a store OUTAGE throws AuthorizationUnavailableError (→ 503).
9
+ * Data-absence ("no roles"/"no grants") returns EMPTY keys — a legitimate deny input.
10
+ * NEVER return empty to mask an outage; NEVER fabricate a key.
11
+ *
12
+ * Ported from astram3 `server/src/features/authz/permission-facts.adapter.ts`.
13
+ */
14
+ import type { Pool } from 'mariadb';
15
+ import type { PermissionFactsPort, CoarseGrants, ModuleRegistry } from '@astram/core';
16
+ /**
17
+ * Expand raw grant strings (exact or wildcard) to the set of EXACT registered keys.
18
+ * Bounded to `allKeys` — a wildcard can never yield a key that isn't registered.
19
+ * exact `identity:company:view` → itself (if registered)
20
+ * `identity:company:*` → every registered `identity:company:<action>`
21
+ * `identity:*` / bare `identity` → every registered `identity:<type>:<action>`
22
+ */
23
+ export declare function expandGrantKeys(rawKeys: string[], allKeys: string[]): string[];
24
+ /**
25
+ * The principal's role uuids from its ACTIVE membership's in-effect postings in
26
+ * `tenantId` — the SAME in-effect-posting window query `DbPermissionFactsAdapter` uses,
27
+ * extracted so any OTHER caller that needs a principal's roleIds for a tenant reuses this
28
+ * ONE timezone-safe epoch-ms window read instead of re-inlining the SQL.
29
+ *
30
+ * @param now injectable clock (epoch-ms), defaults to `Date.now` — bound as a `?`
31
+ * integer against the BIGINT `mp_effective_*_ms` columns (timezone-immune).
32
+ */
33
+ export declare function readRoleIdsForTenant(pool: Pool, principalId: string, tenantId: string, now?: () => number): Promise<string[]>;
34
+ export declare class DbPermissionFactsAdapter implements PermissionFactsPort {
35
+ private readonly pool;
36
+ private readonly registry;
37
+ /**
38
+ * Injectable clock (epoch-ms) for the posting effective-window compare. Defaults to
39
+ * `Date.now`. Bound as a `?` integer against the BIGINT `_ms` columns — timezone-immune.
40
+ * Injectable so a test can fix the clock relative to a seeded expired/active posting.
41
+ */
42
+ private readonly now;
43
+ constructor(pool: Pool, registry: ModuleRegistry,
44
+ /**
45
+ * Injectable clock (epoch-ms) for the posting effective-window compare. Defaults to
46
+ * `Date.now`. Bound as a `?` integer against the BIGINT `_ms` columns — timezone-immune.
47
+ * Injectable so a test can fix the clock relative to a seeded expired/active posting.
48
+ */
49
+ now?: () => number);
50
+ currentPermissionsVersion(_principalId: string, tenantId: string): Promise<number>;
51
+ loadCoarseGrants(principalId: string, tenantId: string): Promise<CoarseGrants>;
52
+ private readVersion;
53
+ /**
54
+ * The principal's role uuids from its ACTIVE membership's in-effect postings in this
55
+ * tenant — delegates to the shared {@link readRoleIdsForTenant} (single source).
56
+ */
57
+ private readRoleIds;
58
+ private readGrantKeys;
59
+ }
60
+ //# sourceMappingURL=db-permission-facts.adapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"db-permission-facts.adapter.d.ts","sourceRoot":"","sources":["../../../src/features/authz/db-permission-facts.adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AACpC,OAAO,KAAK,EAAE,mBAAmB,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAKtF;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,CAe9E;AAED;;;;;;;;GAQG;AACH,wBAAsB,oBAAoB,CACxC,IAAI,EAAE,IAAI,EACV,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,EAChB,GAAG,GAAE,MAAM,MAAiB,GAC3B,OAAO,CAAC,MAAM,EAAE,CAAC,CAYnB;AAED,qBAAa,wBAAyB,YAAW,mBAAmB;IAEhE,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB;;;;OAIG;IACH,OAAO,CAAC,QAAQ,CAAC,GAAG;gBAPH,IAAI,EAAE,IAAI,EACV,QAAQ,EAAE,cAAc;IACzC;;;;OAIG;IACc,GAAG,GAAE,MAAM,MAAiB;IAGzC,yBAAyB,CAAC,YAAY,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAalF,gBAAgB,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;YAgBtE,WAAW;IASzB;;;OAGG;YACW,WAAW;YAIX,aAAa;CAS5B"}
@@ -0,0 +1,116 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DbPermissionFactsAdapter = void 0;
4
+ exports.expandGrantKeys = expandGrantKeys;
5
+ exports.readRoleIdsForTenant = readRoleIdsForTenant;
6
+ const core_1 = require("@astram/core");
7
+ const MODULE_PREFIX = 'identity';
8
+ /**
9
+ * Expand raw grant strings (exact or wildcard) to the set of EXACT registered keys.
10
+ * Bounded to `allKeys` — a wildcard can never yield a key that isn't registered.
11
+ * exact `identity:company:view` → itself (if registered)
12
+ * `identity:company:*` → every registered `identity:company:<action>`
13
+ * `identity:*` / bare `identity` → every registered `identity:<type>:<action>`
14
+ */
15
+ function expandGrantKeys(rawKeys, allKeys) {
16
+ const out = new Set();
17
+ for (const raw of rawKeys) {
18
+ if (raw === MODULE_PREFIX || raw === `${MODULE_PREFIX}:*`) {
19
+ for (const k of allKeys)
20
+ if (k.startsWith(`${MODULE_PREFIX}:`))
21
+ out.add(k);
22
+ continue;
23
+ }
24
+ if (raw.endsWith(':*')) {
25
+ const prefix = raw.slice(0, -1); // keep trailing ':' → 'identity:company:'
26
+ for (const k of allKeys)
27
+ if (k.startsWith(prefix))
28
+ out.add(k);
29
+ continue;
30
+ }
31
+ if (allKeys.includes(raw))
32
+ out.add(raw); // exact, only if real
33
+ }
34
+ return [...out];
35
+ }
36
+ /**
37
+ * The principal's role uuids from its ACTIVE membership's in-effect postings in
38
+ * `tenantId` — the SAME in-effect-posting window query `DbPermissionFactsAdapter` uses,
39
+ * extracted so any OTHER caller that needs a principal's roleIds for a tenant reuses this
40
+ * ONE timezone-safe epoch-ms window read instead of re-inlining the SQL.
41
+ *
42
+ * @param now injectable clock (epoch-ms), defaults to `Date.now` — bound as a `?`
43
+ * integer against the BIGINT `mp_effective_*_ms` columns (timezone-immune).
44
+ */
45
+ async function readRoleIdsForTenant(pool, principalId, tenantId, now = Date.now) {
46
+ const nowMs = now();
47
+ const rows = (await pool.query(`SELECT DISTINCT mp.mp_role_uuid AS role_uuid
48
+ FROM \`shield_app\`.\`company_membership\` cm
49
+ JOIN \`shield_app\`.\`membership_posting\` mp ON mp.mp_membership_uuid = cm.cm_uuid
50
+ WHERE cm.cm_user_uuid = ? AND cm.cm_company_uuid = ? AND cm.cm_is_active = 1
51
+ AND (mp.mp_effective_from_ms IS NULL OR mp.mp_effective_from_ms <= ?)
52
+ AND (mp.mp_effective_until_ms IS NULL OR mp.mp_effective_until_ms > ?)`, [principalId, tenantId, nowMs, nowMs]));
53
+ return rows.map((r) => r.role_uuid);
54
+ }
55
+ class DbPermissionFactsAdapter {
56
+ pool;
57
+ registry;
58
+ now;
59
+ constructor(pool, registry,
60
+ /**
61
+ * Injectable clock (epoch-ms) for the posting effective-window compare. Defaults to
62
+ * `Date.now`. Bound as a `?` integer against the BIGINT `_ms` columns — timezone-immune.
63
+ * Injectable so a test can fix the clock relative to a seeded expired/active posting.
64
+ */
65
+ now = Date.now) {
66
+ this.pool = pool;
67
+ this.registry = registry;
68
+ this.now = now;
69
+ }
70
+ async currentPermissionsVersion(_principalId, tenantId) {
71
+ try {
72
+ const rows = (await this.pool.query(`SELECT apv_version FROM \`shield_authz\`.\`authz_permission_version\`
73
+ WHERE apv_tenant_uuid = ? LIMIT 1`, [tenantId]));
74
+ return rows.length > 0 ? Number(rows[0]?.apv_version) : 0;
75
+ }
76
+ catch (cause) {
77
+ throw new core_1.AuthorizationUnavailableError('permission version store unavailable', cause);
78
+ }
79
+ }
80
+ async loadCoarseGrants(principalId, tenantId) {
81
+ let version;
82
+ let roleIds;
83
+ let rawKeys;
84
+ try {
85
+ version = await this.readVersion(tenantId);
86
+ roleIds = await this.readRoleIds(principalId, tenantId);
87
+ rawKeys = roleIds.length === 0 ? [] : await this.readGrantKeys(roleIds);
88
+ }
89
+ catch (cause) {
90
+ if (cause instanceof core_1.AuthorizationUnavailableError)
91
+ throw cause;
92
+ throw new core_1.AuthorizationUnavailableError('permission facts store unavailable', cause);
93
+ }
94
+ const allKeys = this.registry.listActions().map((a) => a.permissionKey);
95
+ return { permissionsVersion: version, grantedPermissionKeys: expandGrantKeys(rawKeys, allKeys) };
96
+ }
97
+ async readVersion(tenantId) {
98
+ const rows = (await this.pool.query(`SELECT apv_version FROM \`shield_authz\`.\`authz_permission_version\`
99
+ WHERE apv_tenant_uuid = ? LIMIT 1`, [tenantId]));
100
+ return rows.length > 0 ? Number(rows[0]?.apv_version) : 0;
101
+ }
102
+ /**
103
+ * The principal's role uuids from its ACTIVE membership's in-effect postings in this
104
+ * tenant — delegates to the shared {@link readRoleIdsForTenant} (single source).
105
+ */
106
+ async readRoleIds(principalId, tenantId) {
107
+ return readRoleIdsForTenant(this.pool, principalId, tenantId, this.now);
108
+ }
109
+ async readGrantKeys(roleIds) {
110
+ const placeholders = roleIds.map(() => '?').join(', ');
111
+ const rows = (await this.pool.query(`SELECT rp_perm_key FROM \`shield_authz\`.\`role_permission\`
112
+ WHERE rp_role_uuid IN (${placeholders})`, roleIds));
113
+ return rows.map((r) => r.rp_perm_key);
114
+ }
115
+ }
116
+ exports.DbPermissionFactsAdapter = DbPermissionFactsAdapter;
@@ -0,0 +1,96 @@
1
+ import type { Principal } from '@astram/engine';
2
+ import { GrantAdminService } from './grant-admin.service.js';
3
+ /** The request shape after the enforcement guard attaches the resolved principal. */
4
+ interface AuthedRequest {
5
+ principal: Principal;
6
+ }
7
+ /**
8
+ * Grant administration — list/grant/revoke a role's permission keys. Gated purely on
9
+ * `@Classified({ typeKey: 'role', ... })` + `decide()` (G1: no handler-level root
10
+ * check). Managing grants IS role administration, so it keys to the `role` object type:
11
+ * read routes need `identity:role:read`, mutating routes need `identity:role:update`.
12
+ * `role` is a CONTROL-plane type, so the capped root is allowed without an explicit
13
+ * grant; a non-root caller needs the grant. (`role_permission` itself is not a
14
+ * registered object type — it is administered THROUGH `role`, never classified directly.)
15
+ *
16
+ * Mutating routes pass `req.principal.id` as the grantor so `role_permission` records
17
+ * who granted what (surfaced by the grants-summary listing as "by <name>").
18
+ */
19
+ export declare class GrantAdminController {
20
+ private readonly grants;
21
+ constructor(grants: GrantAdminService);
22
+ /**
23
+ * Per-role access summary for the Access listing: every role that holds ≥1 grant, with
24
+ * its object/permission counts, access chips, covering preset, and last-modified date +
25
+ * grantor NAME (resolved server-side — no raw uuid). Gated `role:read`.
26
+ *
27
+ * ⚠️ Path is `access/role-grants` (NOT under `roles/...`) on purpose: a `roles/<x>` path
28
+ * would be captured by IdentityController's `@Get('roles/:uuid')` (registered first),
29
+ * routing this to getRole('role-grants') → a 500. Keep it off the `roles/:uuid` shape.
30
+ */
31
+ grantsSummary(query: {
32
+ page?: string;
33
+ limit?: string;
34
+ search?: string;
35
+ sortBy?: string;
36
+ sortOrder?: string;
37
+ }): Promise<{
38
+ rows: import("./grant-admin.service.js").RoleGrantSummary[];
39
+ total: number;
40
+ page: number;
41
+ limit: number;
42
+ }>;
43
+ /** List all grantable permission keys + wildcards. */
44
+ catalog(): Promise<{
45
+ catalog: {
46
+ key: string;
47
+ label: string;
48
+ module: string;
49
+ typeKey: string;
50
+ action: string;
51
+ }[];
52
+ wildcards: string[];
53
+ }>;
54
+ /** List a role's current grants. */
55
+ list(uuid: string): Promise<{
56
+ grants: string[];
57
+ }>;
58
+ /** Grant one permission key to a role. */
59
+ grant(req: AuthedRequest, uuid: string, body: {
60
+ key?: string;
61
+ }): Promise<{
62
+ success: boolean;
63
+ }>;
64
+ /** Revoke one permission key from a role. `:key` is URL-encoded (contains ':'). */
65
+ revoke(uuid: string, key: string): Promise<{
66
+ success: boolean;
67
+ }>;
68
+ /**
69
+ * List all active access bundles (named, admin-grantable key sets) + their resolved
70
+ * atomic keys, for the Access UI. Bundles are identified by `key` — no `ab_uuid` is
71
+ * ever exposed. Gated `role:read` (managing/reading grants is role administration).
72
+ */
73
+ bundles(): Promise<{
74
+ bundles: import("./grant-admin.service.js").BundleView[];
75
+ }>;
76
+ /**
77
+ * Grant an access bundle to a role — expands the bundle to its atomic keys in
78
+ * `role_permission` (idempotent, one transaction, one version bump). Body: `{ key }`
79
+ * (the bundle slug). Unknown bundle → 400. Gated `role:update`.
80
+ */
81
+ grantBundle(req: AuthedRequest, uuid: string, body: {
82
+ key?: string;
83
+ }): Promise<{
84
+ success: boolean;
85
+ }>;
86
+ /**
87
+ * Revoke an access bundle from a role — removes exactly the bundle's atomic keys from
88
+ * `role_permission` (see design §5.1 caveat re: shared keys). `:key` is the bundle slug.
89
+ * Unknown bundle → 400. Gated `role:update`.
90
+ */
91
+ revokeBundle(uuid: string, key: string): Promise<{
92
+ success: boolean;
93
+ }>;
94
+ }
95
+ export {};
96
+ //# sourceMappingURL=grant-admin.controller.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"grant-admin.controller.d.ts","sourceRoot":"","sources":["../../../src/features/authz/grant-admin.controller.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,EAAE,iBAAiB,EAAe,MAAM,0BAA0B,CAAC;AAE1E,qFAAqF;AACrF,UAAU,aAAa;IACrB,SAAS,EAAE,SAAS,CAAC;CACtB;AAED;;;;;;;;;;;GAWG;AACH,qBACa,oBAAoB;IACnB,OAAO,CAAC,QAAQ,CAAC,MAAM;gBAAN,MAAM,EAAE,iBAAiB;IAEtD;;;;;;;;OAQG;IAGG,aAAa,CAEjB,KAAK,EAAE;QACL,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB;;;;;;IAcH,sDAAsD;IAGhD,OAAO;;;;;;;;;;IAIb,oCAAoC;IAG9B,IAAI,CAAgB,IAAI,EAAE,MAAM;;;IAItC,0CAA0C;IAGpC,KAAK,CAAQ,GAAG,EAAE,aAAa,EAAiB,IAAI,EAAE,MAAM,EAAU,IAAI,EAAE;QAAE,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE;;;IAYlG,mFAAmF;IAG7E,MAAM,CAAgB,IAAI,EAAE,MAAM,EAAgB,GAAG,EAAE,MAAM;;;IAOnE;;;;OAIG;IAGG,OAAO;;;IAIb;;;;OAIG;IAGG,WAAW,CAAQ,GAAG,EAAE,aAAa,EAAiB,IAAI,EAAE,MAAM,EAAU,IAAI,EAAE;QAAE,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE;;;IAYxG;;;;OAIG;IAGG,YAAY,CAAgB,IAAI,EAAE,MAAM,EAAgB,GAAG,EAAE,MAAM;;;CAS1E"}
@@ -0,0 +1,209 @@
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.GrantAdminController = void 0;
16
+ // Ported from astram3 `server/src/features/authz/grant-admin.controller.ts`.
17
+ // Faithful port: same routes, same gating, same error mapping. Imports repointed to the
18
+ // framework's guard/engine building blocks + the local grant-admin.service.
19
+ const common_1 = require("@nestjs/common");
20
+ const guard_1 = require("@astram/guard");
21
+ const grant_admin_service_js_1 = require("./grant-admin.service.js");
22
+ /**
23
+ * Grant administration — list/grant/revoke a role's permission keys. Gated purely on
24
+ * `@Classified({ typeKey: 'role', ... })` + `decide()` (G1: no handler-level root
25
+ * check). Managing grants IS role administration, so it keys to the `role` object type:
26
+ * read routes need `identity:role:read`, mutating routes need `identity:role:update`.
27
+ * `role` is a CONTROL-plane type, so the capped root is allowed without an explicit
28
+ * grant; a non-root caller needs the grant. (`role_permission` itself is not a
29
+ * registered object type — it is administered THROUGH `role`, never classified directly.)
30
+ *
31
+ * Mutating routes pass `req.principal.id` as the grantor so `role_permission` records
32
+ * who granted what (surfaced by the grants-summary listing as "by <name>").
33
+ */
34
+ let GrantAdminController = class GrantAdminController {
35
+ grants;
36
+ constructor(grants) {
37
+ this.grants = grants;
38
+ }
39
+ /**
40
+ * Per-role access summary for the Access listing: every role that holds ≥1 grant, with
41
+ * its object/permission counts, access chips, covering preset, and last-modified date +
42
+ * grantor NAME (resolved server-side — no raw uuid). Gated `role:read`.
43
+ *
44
+ * ⚠️ Path is `access/role-grants` (NOT under `roles/...`) on purpose: a `roles/<x>` path
45
+ * would be captured by IdentityController's `@Get('roles/:uuid')` (registered first),
46
+ * routing this to getRole('role-grants') → a 500. Keep it off the `roles/:uuid` shape.
47
+ */
48
+ async grantsSummary(query) {
49
+ const page = Number.parseInt(query?.page ?? '', 10);
50
+ const limit = Number.parseInt(query?.limit ?? '', 10);
51
+ const params = {
52
+ sortOrder: query?.sortOrder === 'desc' ? 'desc' : 'asc',
53
+ };
54
+ if (Number.isFinite(page))
55
+ params.page = page;
56
+ if (Number.isFinite(limit))
57
+ params.limit = limit;
58
+ if (typeof query?.search === 'string')
59
+ params.search = query.search;
60
+ if (typeof query?.sortBy === 'string')
61
+ params.sortBy = query.sortBy;
62
+ return this.grants.listGrantsSummary(params);
63
+ }
64
+ /** List all grantable permission keys + wildcards. */
65
+ async catalog() {
66
+ return this.grants.listCatalog();
67
+ }
68
+ /** List a role's current grants. */
69
+ async list(uuid) {
70
+ return this.grants.listRoleGrants(uuid);
71
+ }
72
+ /** Grant one permission key to a role. */
73
+ async grant(req, uuid, body) {
74
+ const key = body?.key;
75
+ if (!key)
76
+ throw new common_1.BadRequestException('key required');
77
+ try {
78
+ await this.grants.grant(uuid, key, req.principal?.id);
79
+ }
80
+ catch (e) {
81
+ if (e instanceof grant_admin_service_js_1.BadKeyError)
82
+ throw new common_1.BadRequestException(e.message);
83
+ throw e;
84
+ }
85
+ return { success: true };
86
+ }
87
+ /** Revoke one permission key from a role. `:key` is URL-encoded (contains ':'). */
88
+ async revoke(uuid, key) {
89
+ await this.grants.revoke(uuid, decodeURIComponent(key));
90
+ return { success: true };
91
+ }
92
+ // ── Access bundles ────────────────────────────────────────────────────────
93
+ /**
94
+ * List all active access bundles (named, admin-grantable key sets) + their resolved
95
+ * atomic keys, for the Access UI. Bundles are identified by `key` — no `ab_uuid` is
96
+ * ever exposed. Gated `role:read` (managing/reading grants is role administration).
97
+ */
98
+ async bundles() {
99
+ return this.grants.listBundles();
100
+ }
101
+ /**
102
+ * Grant an access bundle to a role — expands the bundle to its atomic keys in
103
+ * `role_permission` (idempotent, one transaction, one version bump). Body: `{ key }`
104
+ * (the bundle slug). Unknown bundle → 400. Gated `role:update`.
105
+ */
106
+ async grantBundle(req, uuid, body) {
107
+ const key = body?.key;
108
+ if (!key)
109
+ throw new common_1.BadRequestException('key required');
110
+ try {
111
+ await this.grants.grantBundle(uuid, key, req.principal?.id);
112
+ }
113
+ catch (e) {
114
+ if (e instanceof grant_admin_service_js_1.BadKeyError)
115
+ throw new common_1.BadRequestException(e.message);
116
+ throw e;
117
+ }
118
+ return { success: true };
119
+ }
120
+ /**
121
+ * Revoke an access bundle from a role — removes exactly the bundle's atomic keys from
122
+ * `role_permission` (see design §5.1 caveat re: shared keys). `:key` is the bundle slug.
123
+ * Unknown bundle → 400. Gated `role:update`.
124
+ */
125
+ async revokeBundle(uuid, key) {
126
+ try {
127
+ await this.grants.revokeBundle(uuid, decodeURIComponent(key));
128
+ }
129
+ catch (e) {
130
+ if (e instanceof grant_admin_service_js_1.BadKeyError)
131
+ throw new common_1.BadRequestException(e.message);
132
+ throw e;
133
+ }
134
+ return { success: true };
135
+ }
136
+ };
137
+ exports.GrantAdminController = GrantAdminController;
138
+ __decorate([
139
+ (0, common_1.Get)('access/role-grants'),
140
+ (0, guard_1.Classified)({ typeKey: 'role', action: 'read' }),
141
+ __param(0, (0, common_1.Query)()),
142
+ __metadata("design:type", Function),
143
+ __metadata("design:paramtypes", [Object]),
144
+ __metadata("design:returntype", Promise)
145
+ ], GrantAdminController.prototype, "grantsSummary", null);
146
+ __decorate([
147
+ (0, common_1.Get)('permission-catalog'),
148
+ (0, guard_1.Classified)({ typeKey: 'role', action: 'read' }),
149
+ __metadata("design:type", Function),
150
+ __metadata("design:paramtypes", []),
151
+ __metadata("design:returntype", Promise)
152
+ ], GrantAdminController.prototype, "catalog", null);
153
+ __decorate([
154
+ (0, common_1.Get)('roles/:uuid/permissions'),
155
+ (0, guard_1.Classified)({ typeKey: 'role', action: 'read' }),
156
+ __param(0, (0, common_1.Param)('uuid')),
157
+ __metadata("design:type", Function),
158
+ __metadata("design:paramtypes", [String]),
159
+ __metadata("design:returntype", Promise)
160
+ ], GrantAdminController.prototype, "list", null);
161
+ __decorate([
162
+ (0, common_1.Post)('roles/:uuid/permissions'),
163
+ (0, guard_1.Classified)({ typeKey: 'role', action: 'update' }),
164
+ __param(0, (0, common_1.Req)()),
165
+ __param(1, (0, common_1.Param)('uuid')),
166
+ __param(2, (0, common_1.Body)()),
167
+ __metadata("design:type", Function),
168
+ __metadata("design:paramtypes", [Object, String, Object]),
169
+ __metadata("design:returntype", Promise)
170
+ ], GrantAdminController.prototype, "grant", null);
171
+ __decorate([
172
+ (0, common_1.Delete)('roles/:uuid/permissions/:key'),
173
+ (0, guard_1.Classified)({ typeKey: 'role', action: 'update' }),
174
+ __param(0, (0, common_1.Param)('uuid')),
175
+ __param(1, (0, common_1.Param)('key')),
176
+ __metadata("design:type", Function),
177
+ __metadata("design:paramtypes", [String, String]),
178
+ __metadata("design:returntype", Promise)
179
+ ], GrantAdminController.prototype, "revoke", null);
180
+ __decorate([
181
+ (0, common_1.Get)('access-bundles'),
182
+ (0, guard_1.Classified)({ typeKey: 'role', action: 'read' }),
183
+ __metadata("design:type", Function),
184
+ __metadata("design:paramtypes", []),
185
+ __metadata("design:returntype", Promise)
186
+ ], GrantAdminController.prototype, "bundles", null);
187
+ __decorate([
188
+ (0, common_1.Post)('roles/:uuid/access-bundles'),
189
+ (0, guard_1.Classified)({ typeKey: 'role', action: 'update' }),
190
+ __param(0, (0, common_1.Req)()),
191
+ __param(1, (0, common_1.Param)('uuid')),
192
+ __param(2, (0, common_1.Body)()),
193
+ __metadata("design:type", Function),
194
+ __metadata("design:paramtypes", [Object, String, Object]),
195
+ __metadata("design:returntype", Promise)
196
+ ], GrantAdminController.prototype, "grantBundle", null);
197
+ __decorate([
198
+ (0, common_1.Delete)('roles/:uuid/access-bundles/:key'),
199
+ (0, guard_1.Classified)({ typeKey: 'role', action: 'update' }),
200
+ __param(0, (0, common_1.Param)('uuid')),
201
+ __param(1, (0, common_1.Param)('key')),
202
+ __metadata("design:type", Function),
203
+ __metadata("design:paramtypes", [String, String]),
204
+ __metadata("design:returntype", Promise)
205
+ ], GrantAdminController.prototype, "revokeBundle", null);
206
+ exports.GrantAdminController = GrantAdminController = __decorate([
207
+ (0, common_1.Controller)('identity'),
208
+ __metadata("design:paramtypes", [grant_admin_service_js_1.GrantAdminService])
209
+ ], GrantAdminController);