@astram/identity 0.3.1 → 0.5.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 (118) hide show
  1. package/dist/company-membership.object.d.ts.map +1 -1
  2. package/dist/company-membership.object.js +3 -2
  3. package/dist/company.object.d.ts.map +1 -1
  4. package/dist/company.object.js +8 -3
  5. package/dist/features/access/index.d.ts.map +1 -1
  6. package/dist/features/access/index.js +1 -5
  7. package/dist/features/audit/audit-read.service.d.ts.map +1 -1
  8. package/dist/features/audit/audit-read.service.js +13 -6
  9. package/dist/features/audit/audit.controller.d.ts.map +1 -1
  10. package/dist/features/audit/audit.controller.js +2 -0
  11. package/dist/features/audit/mariadb-audit-reader.d.ts.map +1 -1
  12. package/dist/features/authz/access-bundle-reconcile.job.d.ts.map +1 -1
  13. package/dist/features/authz/catalog-reconcile.job.d.ts.map +1 -1
  14. package/dist/features/authz/catalog-reconcile.job.js +1 -3
  15. package/dist/features/authz/db-permission-facts.adapter.d.ts +24 -4
  16. package/dist/features/authz/db-permission-facts.adapter.d.ts.map +1 -1
  17. package/dist/features/authz/db-permission-facts.adapter.js +92 -5
  18. package/dist/features/authz/grant-admin.controller.d.ts +8 -5
  19. package/dist/features/authz/grant-admin.controller.d.ts.map +1 -1
  20. package/dist/features/authz/grant-admin.controller.js +22 -15
  21. package/dist/features/authz/grant-admin.service.d.ts +57 -11
  22. package/dist/features/authz/grant-admin.service.d.ts.map +1 -1
  23. package/dist/features/authz/grant-admin.service.js +163 -85
  24. package/dist/features/authz/grant-ceiling.d.ts +111 -0
  25. package/dist/features/authz/grant-ceiling.d.ts.map +1 -0
  26. package/dist/features/authz/grant-ceiling.js +248 -0
  27. package/dist/features/authz/mariadb-catalog-store.d.ts.map +1 -1
  28. package/dist/features/authz/mariadb-catalog-store.js +15 -3
  29. package/dist/features/authz/permission-version.bumper.d.ts +45 -1
  30. package/dist/features/authz/permission-version.bumper.d.ts.map +1 -1
  31. package/dist/features/authz/permission-version.bumper.js +106 -16
  32. package/dist/features/authz/record-authorization.d.ts.map +1 -1
  33. package/dist/features/authz/role-binding.d.ts +36 -0
  34. package/dist/features/authz/role-binding.d.ts.map +1 -0
  35. package/dist/features/authz/role-binding.js +53 -0
  36. package/dist/features/company/company-hierarchy.service.d.ts +46 -0
  37. package/dist/features/company/company-hierarchy.service.d.ts.map +1 -0
  38. package/dist/features/company/company-hierarchy.service.js +100 -0
  39. package/dist/features/company/company.controller.d.ts +23 -5
  40. package/dist/features/company/company.controller.d.ts.map +1 -1
  41. package/dist/features/company/company.controller.js +47 -12
  42. package/dist/features/company/company.service.d.ts +28 -12
  43. package/dist/features/company/company.service.d.ts.map +1 -1
  44. package/dist/features/company/company.service.js +61 -14
  45. package/dist/features/cross-tenant/cross-tenant-grant.controller.d.ts.map +1 -1
  46. package/dist/features/cross-tenant/cross-tenant-grant.controller.js +2 -0
  47. package/dist/features/identity-feature.module.d.ts.map +1 -1
  48. package/dist/features/identity-feature.module.js +123 -6
  49. package/dist/features/membership/membership.controller.d.ts +6 -5
  50. package/dist/features/membership/membership.controller.d.ts.map +1 -1
  51. package/dist/features/membership/membership.controller.js +34 -22
  52. package/dist/features/membership/membership.service.d.ts +20 -14
  53. package/dist/features/membership/membership.service.d.ts.map +1 -1
  54. package/dist/features/membership/membership.service.js +62 -29
  55. package/dist/features/membership/posting-version-bump.d.ts +5 -0
  56. package/dist/features/membership/posting-version-bump.d.ts.map +1 -1
  57. package/dist/features/membership/posting.service.d.ts +20 -24
  58. package/dist/features/membership/posting.service.d.ts.map +1 -1
  59. package/dist/features/membership/posting.service.js +101 -64
  60. package/dist/features/org-structure/org-catalog.controller.d.ts.map +1 -1
  61. package/dist/features/org-structure/org-catalog.controller.js +2 -0
  62. package/dist/features/org-structure/org-catalog.service.d.ts.map +1 -1
  63. package/dist/features/org-structure/org-catalog.service.js +4 -1
  64. package/dist/features/policy/policy.controller.d.ts.map +1 -1
  65. package/dist/features/policy/policy.controller.js +2 -0
  66. package/dist/features/role/role.controller.d.ts +3 -2
  67. package/dist/features/role/role.controller.d.ts.map +1 -1
  68. package/dist/features/role/role.controller.js +15 -10
  69. package/dist/features/role/role.service.d.ts +16 -8
  70. package/dist/features/role/role.service.d.ts.map +1 -1
  71. package/dist/features/role/role.service.js +24 -10
  72. package/dist/features/scope/scope-closure.service.d.ts +44 -4
  73. package/dist/features/scope/scope-closure.service.d.ts.map +1 -1
  74. package/dist/features/scope/scope-closure.service.js +215 -82
  75. package/dist/features/service-account/service-account.controller.d.ts +63 -0
  76. package/dist/features/service-account/service-account.controller.d.ts.map +1 -0
  77. package/dist/features/service-account/service-account.controller.js +295 -0
  78. package/dist/features/service-account/service-account.service.d.ts +209 -0
  79. package/dist/features/service-account/service-account.service.d.ts.map +1 -0
  80. package/dist/features/service-account/service-account.service.js +528 -0
  81. package/dist/features/service-account/service-account.store.d.ts +91 -0
  82. package/dist/features/service-account/service-account.store.d.ts.map +1 -0
  83. package/dist/features/service-account/service-account.store.js +158 -0
  84. package/dist/features/share/share.controller.d.ts.map +1 -1
  85. package/dist/features/share/share.controller.js +2 -0
  86. package/dist/features/share/share.service.d.ts.map +1 -1
  87. package/dist/features/shared/identity-error.interceptor.d.ts +24 -0
  88. package/dist/features/shared/identity-error.interceptor.d.ts.map +1 -0
  89. package/dist/features/shared/identity-error.interceptor.js +82 -0
  90. package/dist/features/shared/identity-errors.d.ts +23 -0
  91. package/dist/features/shared/identity-errors.d.ts.map +1 -0
  92. package/dist/features/shared/identity-errors.js +41 -0
  93. package/dist/features/user/session-revocation-check.d.ts +11 -0
  94. package/dist/features/user/session-revocation-check.d.ts.map +1 -0
  95. package/dist/features/user/session-revocation-check.js +21 -0
  96. package/dist/features/user/user.controller.d.ts +11 -6
  97. package/dist/features/user/user.controller.d.ts.map +1 -1
  98. package/dist/features/user/user.controller.js +35 -17
  99. package/dist/features/user/user.service.d.ts +55 -14
  100. package/dist/features/user/user.service.d.ts.map +1 -1
  101. package/dist/features/user/user.service.js +143 -16
  102. package/dist/index.d.ts +1 -0
  103. package/dist/index.d.ts.map +1 -1
  104. package/dist/index.js +3 -1
  105. package/dist/membership-posting.object.d.ts.map +1 -1
  106. package/dist/membership-posting.object.js +4 -2
  107. package/dist/objects.d.ts.map +1 -1
  108. package/dist/objects.js +14 -3
  109. package/dist/role.object.d.ts.map +1 -1
  110. package/dist/role.object.js +3 -0
  111. package/dist/service-account.object.d.ts +31 -0
  112. package/dist/service-account.object.d.ts.map +1 -0
  113. package/dist/service-account.object.js +75 -0
  114. package/dist/user.object.d.ts.map +1 -1
  115. package/dist/user.object.js +3 -0
  116. package/migrations/1710000000018-CreateServiceAccount.cjs +53 -0
  117. package/migrations/1710000000019-AddSessionMustReenroll.cjs +35 -0
  118. package/package.json +4 -4
@@ -1 +1 @@
1
- {"version":3,"file":"company-membership.object.d.ts","sourceRoot":"","sources":["../src/company-membership.object.ts"],"names":[],"mappings":"AAGA;;;;;;GAMG;AACH,qBAca,iBAAiB;IAE5B,IAAI,EAAG,MAAM,CAAC;IAGd,QAAQ,EAAG,MAAM,CAAC;IAGlB,WAAW,EAAG,MAAM,CAAC;IAGrB,QAAQ,EAAG,OAAO,CAAC;IAGnB,MAAM,EAAG,MAAM,CAAC;IAGhB,SAAS,EAAG,MAAM,CAAC;CACpB"}
1
+ {"version":3,"file":"company-membership.object.d.ts","sourceRoot":"","sources":["../src/company-membership.object.ts"],"names":[],"mappings":"AAGA;;;;;;GAMG;AACH,qBAca,iBAAiB;IAE5B,IAAI,EAAG,MAAM,CAAC;IAId,QAAQ,EAAG,MAAM,CAAC;IAGlB,WAAW,EAAG,MAAM,CAAC;IAGrB,QAAQ,EAAG,OAAO,CAAC;IAGnB,MAAM,EAAG,MAAM,CAAC;IAGhB,SAAS,EAAG,MAAM,CAAC;CACpB"}
@@ -21,6 +21,7 @@ const schema_1 = require("./schema");
21
21
  */
22
22
  let CompanyMembership = class CompanyMembership {
23
23
  uuid;
24
+ // immutable (ADR-0034): a membership never changes its user or company after create.
24
25
  userUuid;
25
26
  companyUuid;
26
27
  isActive;
@@ -33,11 +34,11 @@ __decorate([
33
34
  __metadata("design:type", String)
34
35
  ], CompanyMembership.prototype, "uuid", void 0);
35
36
  __decorate([
36
- (0, engine_1.Field)({ column: 'cm_user_uuid', type: 'uuid', required: true }),
37
+ (0, engine_1.Field)({ column: 'cm_user_uuid', type: 'uuid', required: true, immutable: true }),
37
38
  __metadata("design:type", String)
38
39
  ], CompanyMembership.prototype, "userUuid", void 0);
39
40
  __decorate([
40
- (0, engine_1.Field)({ column: 'cm_company_uuid', type: 'uuid', required: true }),
41
+ (0, engine_1.Field)({ column: 'cm_company_uuid', type: 'uuid', required: true, immutable: true }),
41
42
  __metadata("design:type", String)
42
43
  ], CompanyMembership.prototype, "companyUuid", void 0);
43
44
  __decorate([
@@ -1 +1 @@
1
- {"version":3,"file":"company.object.d.ts","sourceRoot":"","sources":["../src/company.object.ts"],"names":[],"mappings":"AAGA,kFAAkF;AAClF,qBAmBa,OAAO;IAElB,IAAI,EAAG,MAAM,CAAC;IAGd,IAAI,EAAG,MAAM,CAAC;IAGd,UAAU,EAAG,MAAM,CAAC;IAGpB,IAAI,EAAG,MAAM,CAAC;IAGd,MAAM,EAAG,MAAM,CAAC;IAGhB,SAAS,EAAG,MAAM,CAAC;CACpB"}
1
+ {"version":3,"file":"company.object.d.ts","sourceRoot":"","sources":["../src/company.object.ts"],"names":[],"mappings":"AAGA,kFAAkF;AAClF,qBAsBa,OAAO;IAElB,IAAI,EAAG,MAAM,CAAC;IAGd,IAAI,EAAG,MAAM,CAAC;IAKd,UAAU,EAAG,MAAM,CAAC;IAGpB,IAAI,EAAG,MAAM,CAAC;IAGd,MAAM,EAAG,MAAM,CAAC;IAGhB,SAAS,EAAG,MAAM,CAAC;CACpB"}
@@ -16,6 +16,8 @@ const schema_1 = require("./schema");
16
16
  let Company = class Company {
17
17
  uuid;
18
18
  name;
19
+ // immutable (ADR-0034): re-parenting is a hierarchy move (cycle check, closure rebuild,
20
+ // session bump), never a side effect of a generic patch.
19
21
  parentUuid;
20
22
  type;
21
23
  status;
@@ -31,11 +33,11 @@ __decorate([
31
33
  __metadata("design:type", String)
32
34
  ], Company.prototype, "name", void 0);
33
35
  __decorate([
34
- (0, engine_1.Field)({ column: 'c_parent_uuid', type: 'uuid' }),
36
+ (0, engine_1.Field)({ column: 'c_parent_uuid', type: 'uuid', immutable: true }),
35
37
  __metadata("design:type", String)
36
38
  ], Company.prototype, "parentUuid", void 0);
37
39
  __decorate([
38
- (0, engine_1.Field)({ column: 'c_type', type: 'string' }),
40
+ (0, engine_1.Field)({ column: 'c_type', type: 'string', immutable: true }),
39
41
  __metadata("design:type", String)
40
42
  ], Company.prototype, "type", void 0);
41
43
  __decorate([
@@ -63,7 +65,10 @@ exports.Company = Company = __decorate([
63
65
  // the node (`c_uuid`), so a non-root LIST/GET is bounded to the caller's reachable subtree
64
66
  // (`c_uuid` IN my subtree). DECOUPLED from writes (no `trusted` change) — this filters
65
67
  // reads only. Root spans all.
66
- readScope: { kind: 'nodeColumn', column: 'c_uuid' },
68
+ // create:'unscoped' (ADR-0034): a NEW company's c_uuid is not in anyone's subtree until the
69
+ // scope closure lists it, so the post-insert create wall would deny every create. Opted out
70
+ // explicitly; the company service owns where a non-root create may attach (parent stamping).
71
+ readScope: { kind: 'nodeColumn', column: 'c_uuid', create: 'unscoped' },
67
72
  }),
68
73
  (0, engine_1.PrimaryKey)({ kind: 'uuid', column: 'c_uuid' })
69
74
  ], Company);
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/features/access/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAIhE,OAAO,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAChE,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAC5D,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAE1D,sEAAsE;AACtE,eAAO,MAAM,qBAAqB,iCAIjC,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/features/access/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAIhE,OAAO,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAChE,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAC5D,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAE1D,sEAAsE;AACtE,eAAO,MAAM,qBAAqB,iCAAgE,CAAC"}
@@ -19,8 +19,4 @@ Object.defineProperty(exports, "CompanyAdminAccess", { enumerable: true, get: fu
19
19
  var access_admin_access_2 = require("./access-admin.access");
20
20
  Object.defineProperty(exports, "AccessAdminAccess", { enumerable: true, get: function () { return access_admin_access_2.AccessAdminAccess; } });
21
21
  /** All identity access bundles, collected into the engine at boot. */
22
- exports.identityAccessBundles = [
23
- user_management_access_1.UserManagementAccess,
24
- company_admin_access_1.CompanyAdminAccess,
25
- access_admin_access_1.AccessAdminAccess,
26
- ];
22
+ exports.identityAccessBundles = [user_management_access_1.UserManagementAccess, company_admin_access_1.CompanyAdminAccess, access_admin_access_1.AccessAdminAccess];
@@ -1 +1 @@
1
- {"version":3,"file":"audit-read.service.d.ts","sourceRoot":"","sources":["../../../src/features/audit/audit-read.service.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAE9D,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC1C;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;CAC1B;AAED,wFAAwF;AACxF,MAAM,MAAM,iBAAiB,GAAG,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;AAEzE;;;;;GAKG;AACH,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,IAAI,CAAC,EAAE,UAAU,CAAC,MAAM,CAAC,CAAC;IACnC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED;;;;GAIG;AACH,MAAM,WAAW,cAAe,SAAQ,UAAU;IAChD,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED;;;;;;GAMG;AACH,MAAM,WAAW,gBAAgB;IAC/B,IAAI,CAAC,KAAK,EAAE,cAAc,GAAG,OAAO,CAAC,SAAS,UAAU,EAAE,CAAC,CAAC;IAC5D,KAAK,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CAC3C;AAMD;;;;;;GAMG;AACH,qBAAa,gBAAgB;IAEzB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,iBAAiB;gBAFjB,MAAM,EAAE,gBAAgB,EACxB,MAAM,EAAE,aAAa,EACrB,iBAAiB,EAAE,iBAAiB;IAGvD;;;;;;OAMG;IACG,QAAQ,CACZ,SAAS,EAAE,cAAc,EACzB,OAAO,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAC;QAAC,YAAY,CAAC,EAAE,MAAM,CAAA;KAAE,GACnG,OAAO,CAAC;QAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IAyD5E;;;OAGG;IACH,OAAO,CAAC,QAAQ;CAUjB"}
1
+ {"version":3,"file":"audit-read.service.d.ts","sourceRoot":"","sources":["../../../src/features/audit/audit-read.service.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAE9D,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC1C;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;CAC1B;AAED,wFAAwF;AACxF,MAAM,MAAM,iBAAiB,GAAG,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;AAEzE;;;;;GAKG;AACH,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,IAAI,CAAC,EAAE,UAAU,CAAC,MAAM,CAAC,CAAC;IACnC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED;;;;GAIG;AACH,MAAM,WAAW,cAAe,SAAQ,UAAU;IAChD,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED;;;;;;GAMG;AACH,MAAM,WAAW,gBAAgB;IAC/B,IAAI,CAAC,KAAK,EAAE,cAAc,GAAG,OAAO,CAAC,SAAS,UAAU,EAAE,CAAC,CAAC;IAC5D,KAAK,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CAC3C;AAMD;;;;;;GAMG;AACH,qBAAa,gBAAgB;IAEzB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,iBAAiB;gBAFjB,MAAM,EAAE,gBAAgB,EACxB,MAAM,EAAE,aAAa,EACrB,iBAAiB,EAAE,iBAAiB;IAGvD;;;;;;OAMG;IACG,QAAQ,CACZ,SAAS,EAAE,cAAc,EACzB,OAAO,EAAE;QACP,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,YAAY,CAAC,EAAE,MAAM,CAAC;KACvB,GACA,OAAO,CAAC;QAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IAkE5E;;;OAGG;IACH,OAAO,CAAC,QAAQ;CAUjB"}
@@ -33,7 +33,10 @@ class AuditReadService {
33
33
  // and tenantFilter is ignored (cannot widen or cross).
34
34
  let tenantId;
35
35
  if (principal.isRoot) {
36
- tenantId = filters.tenantFilter && filters.tenantFilter.length > 0 ? filters.tenantFilter : ALL_TENANTS;
36
+ tenantId =
37
+ filters.tenantFilter && filters.tenantFilter.length > 0
38
+ ? filters.tenantFilter
39
+ : ALL_TENANTS;
37
40
  }
38
41
  else {
39
42
  tenantId = principal.tenantId;
@@ -47,9 +50,13 @@ class AuditReadService {
47
50
  // Clamp the client-supplied limit to [1, MAX_LIMIT]; a non-finite value falls back to
48
51
  // DEFAULT_LIMIT (only an in-range-but-out-of-bounds number gets floored/capped). Floor the
49
52
  // page to >= 1. offset uses the CLAMPED limit, so the window never diverges from read.limit.
50
- const requestedLimit = Number.isFinite(filters.limit) ? filters.limit : DEFAULT_LIMIT;
53
+ const requestedLimit = Number.isFinite(filters.limit)
54
+ ? filters.limit
55
+ : DEFAULT_LIMIT;
51
56
  const limit = Math.min(Math.max(1, requestedLimit), MAX_LIMIT);
52
- const page = Number.isFinite(filters.page) && filters.page >= 1 ? Math.floor(filters.page) : 1;
57
+ const page = Number.isFinite(filters.page) && filters.page >= 1
58
+ ? Math.floor(filters.page)
59
+ : 1;
53
60
  const offset = (page - 1) * limit;
54
61
  // ONE scope query — spread into BOTH the count and the page read so their tenant/type/actor
55
62
  // WHERE is identical. A non-root's total therefore counts ONLY its own tenant (invariant #4).
@@ -69,9 +76,9 @@ class AuditReadService {
69
76
  const rows = await Promise.all(events.map(async (event) => ({
70
77
  occurredAt: event.occurredAtMs,
71
78
  actorLabel: await this.labels.resolve('principal', event.actorId),
72
- action: event.action?.key
73
- ?? event.action
74
- ?? '(event)',
79
+ action: event.action?.key ??
80
+ event.action ??
81
+ '(event)',
75
82
  tenantLabel: await this.labels.resolve('tenant', event.tenantId),
76
83
  detail: this.stripIds(event.attributes ?? {}),
77
84
  })));
@@ -1 +1 @@
1
- {"version":3,"file":"audit.controller.d.ts","sourceRoot":"","sources":["../../../src/features/audit/audit.controller.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAIhD,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAW3D;;;gEAGgE;AAChE,UAAU,aAAa;IACrB,SAAS,EAAE,SAAS,GAAG;QAAE,MAAM,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;CAC7C;AAED;;;;GAIG;AACH,qBACa,eAAe;IACd,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAP,OAAO,EAAE,gBAAgB;IAIhD,IAAI,CACD,GAAG,EAAE,aAAa,EAChB,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,GAC5F,OAAO,CAAC;QAAE,OAAO,EAAE,IAAI,CAAC;QAAC,IAAI,EAAE,OAAO,EAAE,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;CA8B3F"}
1
+ {"version":3,"file":"audit.controller.d.ts","sourceRoot":"","sources":["../../../src/features/audit/audit.controller.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAIhD,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAW3D;;;gEAGgE;AAChE,UAAU,aAAa;IACrB,SAAS,EAAE,SAAS,GAAG;QAAE,MAAM,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;CAC7C;AAED;;;;GAIG;AACH,qBAEa,eAAe;IACd,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAP,OAAO,EAAE,gBAAgB;IAIhD,IAAI,CACD,GAAG,EAAE,aAAa,EAChB,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,GAC5F,OAAO,CAAC;QAAE,OAAO,EAAE,IAAI,CAAC;QAAC,IAAI,EAAE,OAAO,EAAE,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;CA8B3F"}
@@ -15,6 +15,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
15
15
  exports.AuditController = void 0;
16
16
  // audit.controller.ts
17
17
  const common_1 = require("@nestjs/common");
18
+ const identity_error_interceptor_js_1 = require("../shared/identity-error.interceptor.js");
18
19
  const guard_1 = require("@astram/guard");
19
20
  // `AuditReadService` is the DI token for the constructor injection below, so it MUST be a
20
21
  // value import — a type-only import is erased at compile time, leaving NestJS with
@@ -69,5 +70,6 @@ __decorate([
69
70
  ], AuditController.prototype, "list", null);
70
71
  exports.AuditController = AuditController = __decorate([
71
72
  (0, common_1.Controller)('identity'),
73
+ (0, common_1.UseInterceptors)(identity_error_interceptor_js_1.IdentityErrorInterceptor),
72
74
  __metadata("design:paramtypes", [audit_read_service_js_1.AuditReadService])
73
75
  ], AuditController);
@@ -1 +1 @@
1
- {"version":3,"file":"mariadb-audit-reader.d.ts","sourceRoot":"","sources":["../../../src/features/audit/mariadb-audit-reader.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE5D,MAAM,WAAW,cAAc;IAC7B,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,SAAS,OAAO,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACnE;AAgBD;;;;;GAKG;AACH,qBAAa,kBAAmB,YAAW,WAAW;IACpD,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAiB;IACpC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;gBAEpB,EAAE,EAAE,cAAc,EAAE,IAAI,GAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAA;KAAO;IAKxD,IAAI,CAAC,KAAK,EAAE;QAChB,QAAQ,EAAE,MAAM,CAAC;QACjB,IAAI,CAAC,EAAE,UAAU,CAAC,MAAM,CAAC,CAAC;QAC1B,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB,GAAG,OAAO,CAAC,SAAS,UAAU,EAAE,CAAC;IAgClC;;;;;;OAMG;IACG,KAAK,CAAC,KAAK,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,MAAM,CAAC;IAY1F;;;;;OAKG;IACH,OAAO,CAAC,gBAAgB;IAwBxB,OAAO,CAAC,OAAO;CA+BhB"}
1
+ {"version":3,"file":"mariadb-audit-reader.d.ts","sourceRoot":"","sources":["../../../src/features/audit/mariadb-audit-reader.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE5D,MAAM,WAAW,cAAc;IAC7B,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,SAAS,OAAO,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACnE;AAgBD;;;;;GAKG;AACH,qBAAa,kBAAmB,YAAW,WAAW;IACpD,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAiB;IACpC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;gBAEpB,EAAE,EAAE,cAAc,EAAE,IAAI,GAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAA;KAAO;IAKxD,IAAI,CAAC,KAAK,EAAE;QAChB,QAAQ,EAAE,MAAM,CAAC;QACjB,IAAI,CAAC,EAAE,UAAU,CAAC,MAAM,CAAC,CAAC;QAC1B,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB,GAAG,OAAO,CAAC,SAAS,UAAU,EAAE,CAAC;IAgClC;;;;;;OAMG;IACG,KAAK,CAAC,KAAK,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,MAAM,CAAC;IAY1F;;;;;OAKG;IACH,OAAO,CAAC,gBAAgB;IAuBxB,OAAO,CAAC,OAAO;CA+BhB"}
@@ -1 +1 @@
1
- {"version":3,"file":"access-bundle-reconcile.job.d.ts","sourceRoot":"","sources":["../../../src/features/authz/access-bundle-reconcile.job.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AACpC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAKvD,yCAAyC;AACzC,MAAM,WAAW,2BAA2B;IAC1C,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,CAAC;CACzC;AAED;;;;;;;;;;;;;GAaG;AACH,qBAAa,wBAAwB;IAKjC,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAL1B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAoB;IAC1C,OAAO,CAAC,OAAO,CAAS;gBAGL,IAAI,EAAE,IAAI,EACV,OAAO,EAAE,SAAS,gBAAgB,EAAE;IAKvD,6EAA6E;IACvE,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,2BAA2B,CAAC;IAuDpE,sEAAsE;YACxD,wBAAwB;CAMvC"}
1
+ {"version":3,"file":"access-bundle-reconcile.job.d.ts","sourceRoot":"","sources":["../../../src/features/authz/access-bundle-reconcile.job.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AACpC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAKvD,yCAAyC;AACzC,MAAM,WAAW,2BAA2B;IAC1C,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,CAAC;CACzC;AAED;;;;;;;;;;;;;GAaG;AACH,qBAAa,wBAAwB;IAKjC,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAL1B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAoB;IAC1C,OAAO,CAAC,OAAO,CAAS;gBAGL,IAAI,EAAE,IAAI,EACV,OAAO,EAAE,SAAS,gBAAgB,EAAE;IAKvD,6EAA6E;IACvE,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,2BAA2B,CAAC;IAqDpE,sEAAsE;YACxD,wBAAwB;CAMvC"}
@@ -1 +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"}
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;IAQ5B,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAP3B,OAAO,CAAC,QAAQ,CAAC,UAAU,CAA2B;IACtD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAsB;IAC5C,OAAO,CAAC,MAAM,CAAgC;IAC9C,OAAO,CAAC,OAAO,CAAS;gBAGtB,IAAI,EAAE,IAAI,EACO,QAAQ,EAAE,cAAc;IAK3C,6EAA6E;IACvE,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC;IAiB9D,SAAS,IAAI,eAAe,GAAG,IAAI;CAGpC"}
@@ -29,9 +29,7 @@ class CatalogReconcileJob {
29
29
  try {
30
30
  const report = await this.reconciler.reconcile(this.registry, this.store, nowIso);
31
31
  this.latest = report;
32
- const orphanNote = report.orphaned.length
33
- ? ` — ORPHANED: ${report.orphaned.join(', ')}`
34
- : '';
32
+ const orphanNote = report.orphaned.length ? ` — ORPHANED: ${report.orphaned.join(', ')}` : '';
35
33
  console.log(`[catalog] reconcile: +${report.added.length} added, ${report.orphaned.length} orphaned, ` +
36
34
  `${report.reactivated.length} reactivated${orphanNote}`);
37
35
  return report;
@@ -22,15 +22,27 @@ import type { PermissionFactsPort, CoarseGrants, ModuleRegistry } from '@astram/
22
22
  */
23
23
  export declare function expandGrantKeys(rawKeys: string[], allKeys: string[]): string[];
24
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.
25
+ * The principal's role uuids in `tenantId` — the SINGLE source of "what roles does this principal
26
+ * hold here" used by the object-type gate (gate 5), the grant-ceiling (ADR-0029), and the
27
+ * cross-tenant grant authorize. KIND-AWARE (ADR-0033): a SERVICE ACCOUNT's roles come from its
28
+ * `service_account.sa_role_ids`; a USER's roles come from its ACTIVE membership's in-effect
29
+ * postings. The service-account lookup runs first — a hit short-circuits (a service-account id
30
+ * never has membership rows anyway); a miss falls through to the timezone-safe epoch-ms membership
31
+ * window read. Making THIS function kind-aware gives all three callers correct grants uniformly
32
+ * (one definition of "a principal's roles"), preserving the single `decide()` path.
29
33
  *
30
34
  * @param now injectable clock (epoch-ms), defaults to `Date.now` — bound as a `?`
31
35
  * integer against the BIGINT `mp_effective_*_ms` columns (timezone-immune).
32
36
  */
33
37
  export declare function readRoleIdsForTenant(pool: Pool, principalId: string, tenantId: string, now?: () => number): Promise<string[]>;
38
+ /**
39
+ * The next instant (epoch-ms, strictly after `now`) at which this USER's in-effect roles in
40
+ * `tenantId` change by themselves — the earliest future `mp_effective_from_ms` (a posting starts)
41
+ * or `mp_effective_until_ms` (a posting ends) of the active membership. No version bump marks
42
+ * these moments, so cached facts must expire there (ADR-0035). `null` when there is none, and for
43
+ * a service account (its roles have no windows). A query failure propagates (fail closed).
44
+ */
45
+ export declare function readNextPostingBoundaryMs(pool: Pool, principalId: string, tenantId: string, now?: () => number): Promise<number | null>;
34
46
  export declare class DbPermissionFactsAdapter implements PermissionFactsPort {
35
47
  private readonly pool;
36
48
  private readonly registry;
@@ -48,6 +60,14 @@ export declare class DbPermissionFactsAdapter implements PermissionFactsPort {
48
60
  */
49
61
  now?: () => number);
50
62
  currentPermissionsVersion(_principalId: string, tenantId: string): Promise<number>;
63
+ /**
64
+ * The coarse grants + (ADR-0035) the LIVE role ids they were derived from + the time bound of
65
+ * those roles. The version is read FIRST, so a bump racing this load can only make the facts look
66
+ * older (one extra reload), never newer. `roleIds` is always reported (empty = genuinely none),
67
+ * so the session layer binds the in-effect roles, never the credential's `ic_roles`.
68
+ * `validUntilMs` = the next posting window boundary (absent when there is none); an outage on
69
+ * that read fails closed like every other read here.
70
+ */
51
71
  loadCoarseGrants(principalId: string, tenantId: string): Promise<CoarseGrants>;
52
72
  private readVersion;
53
73
  /**
@@ -1 +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"}
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;AA0CD;;;;;;;;;;;;GAYG;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,CAqBnB;AAED;;;;;;GAMG;AACH,wBAAsB,yBAAyB,CAC7C,IAAI,EAAE,IAAI,EACV,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,EAChB,GAAG,GAAE,MAAM,MAAiB,GAC3B,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAkBxB;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;IAaxF;;;;;;;OAOG;IACG,gBAAgB,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;YAuBtE,WAAW;IASzB;;;OAGG;YACW,WAAW;YAIX,aAAa;CAS5B"}
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.DbPermissionFactsAdapter = void 0;
4
4
  exports.expandGrantKeys = expandGrantKeys;
5
5
  exports.readRoleIdsForTenant = readRoleIdsForTenant;
6
+ exports.readNextPostingBoundaryMs = readNextPostingBoundaryMs;
6
7
  const core_1 = require("@astram/core");
7
8
  const MODULE_PREFIX = 'identity';
8
9
  /**
@@ -33,25 +34,96 @@ function expandGrantKeys(rawKeys, allKeys) {
33
34
  }
34
35
  return [...out];
35
36
  }
37
+ /** Parse a `sa_role_ids` JSON column (driver returns it already-parsed OR as a string). */
38
+ function parseRoleIdsJson(v) {
39
+ const arr = Array.isArray(v)
40
+ ? v
41
+ : typeof v === 'string'
42
+ ? (() => {
43
+ try {
44
+ return JSON.parse(v);
45
+ }
46
+ catch {
47
+ return [];
48
+ }
49
+ })()
50
+ : [];
51
+ return Array.isArray(arr) ? arr.filter((x) => typeof x === 'string') : [];
52
+ }
53
+ /**
54
+ * A SERVICE ACCOUNT (ADR-0033) holds its roles directly on `service_account.sa_role_ids` — it has
55
+ * NO `company_membership`/`membership_posting` rows (those model a human's org placement). So its
56
+ * roles are read from the account row: an ACTIVE account in this tenant → its `sa_role_ids`. Returns
57
+ * `null` when `principalId` is not a service account here (→ the caller falls through to the human
58
+ * membership path). A DISABLED account returns `[]` (in-scope but no roles → deny everything —
59
+ * defense-in-depth alongside the key's own revoke check).
60
+ */
61
+ async function readServiceAccountRoleIds(pool, principalId, tenantId) {
62
+ const rows = (await pool.query(`SELECT sa_role_ids, sa_status FROM \`shield_app\`.\`service_account\`
63
+ WHERE sa_id = ? AND sa_tenant_id = ? LIMIT 1`, [principalId, tenantId]));
64
+ const row = rows[0];
65
+ if (row === undefined)
66
+ return null; // not a service account here → let the user path run.
67
+ if (row.sa_status !== 'active')
68
+ return []; // disabled → no roles (deny-by-default).
69
+ return parseRoleIdsJson(row.sa_role_ids);
70
+ }
36
71
  /**
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.
72
+ * The principal's role uuids in `tenantId` — the SINGLE source of "what roles does this principal
73
+ * hold here" used by the object-type gate (gate 5), the grant-ceiling (ADR-0029), and the
74
+ * cross-tenant grant authorize. KIND-AWARE (ADR-0033): a SERVICE ACCOUNT's roles come from its
75
+ * `service_account.sa_role_ids`; a USER's roles come from its ACTIVE membership's in-effect
76
+ * postings. The service-account lookup runs first — a hit short-circuits (a service-account id
77
+ * never has membership rows anyway); a miss falls through to the timezone-safe epoch-ms membership
78
+ * window read. Making THIS function kind-aware gives all three callers correct grants uniformly
79
+ * (one definition of "a principal's roles"), preserving the single `decide()` path.
41
80
  *
42
81
  * @param now injectable clock (epoch-ms), defaults to `Date.now` — bound as a `?`
43
82
  * integer against the BIGINT `mp_effective_*_ms` columns (timezone-immune).
44
83
  */
45
84
  async function readRoleIdsForTenant(pool, principalId, tenantId, now = Date.now) {
85
+ // Service-account path (ADR-0033): its roles live on the account row, not memberships.
86
+ const saRoles = await readServiceAccountRoleIds(pool, principalId, tenantId);
87
+ if (saRoles !== null)
88
+ return saRoles;
89
+ // Human path: roles from the active membership's in-effect postings — of an ACTIVE account only
90
+ // (security hardening Phase 3: a disabled user holds no roles, so gate 5 and every role-keyed
91
+ // path deny at once; `company_membership.cm_user_uuid` is an FK to `users`, so a member always
92
+ // has a users row).
46
93
  const nowMs = now();
47
94
  const rows = (await pool.query(`SELECT DISTINCT mp.mp_role_uuid AS role_uuid
48
95
  FROM \`shield_app\`.\`company_membership\` cm
96
+ JOIN \`shield_app\`.\`users\` u ON u.u_uuid = cm.cm_user_uuid AND u.u_status = 'active'
49
97
  JOIN \`shield_app\`.\`membership_posting\` mp ON mp.mp_membership_uuid = cm.cm_uuid
50
98
  WHERE cm.cm_user_uuid = ? AND cm.cm_company_uuid = ? AND cm.cm_is_active = 1
51
99
  AND (mp.mp_effective_from_ms IS NULL OR mp.mp_effective_from_ms <= ?)
52
100
  AND (mp.mp_effective_until_ms IS NULL OR mp.mp_effective_until_ms > ?)`, [principalId, tenantId, nowMs, nowMs]));
53
101
  return rows.map((r) => r.role_uuid);
54
102
  }
103
+ /**
104
+ * The next instant (epoch-ms, strictly after `now`) at which this USER's in-effect roles in
105
+ * `tenantId` change by themselves — the earliest future `mp_effective_from_ms` (a posting starts)
106
+ * or `mp_effective_until_ms` (a posting ends) of the active membership. No version bump marks
107
+ * these moments, so cached facts must expire there (ADR-0035). `null` when there is none, and for
108
+ * a service account (its roles have no windows). A query failure propagates (fail closed).
109
+ */
110
+ async function readNextPostingBoundaryMs(pool, principalId, tenantId, now = Date.now) {
111
+ const nowMs = now();
112
+ const rows = (await pool.query(`SELECT MIN(CASE
113
+ WHEN mp.mp_effective_from_ms IS NOT NULL AND mp.mp_effective_from_ms > ?
114
+ THEN mp.mp_effective_from_ms
115
+ WHEN mp.mp_effective_until_ms IS NOT NULL AND mp.mp_effective_until_ms > ?
116
+ THEN mp.mp_effective_until_ms
117
+ END) AS next_boundary_ms
118
+ FROM \`shield_app\`.\`company_membership\` cm
119
+ JOIN \`shield_app\`.\`membership_posting\` mp ON mp.mp_membership_uuid = cm.cm_uuid
120
+ WHERE cm.cm_user_uuid = ? AND cm.cm_company_uuid = ? AND cm.cm_is_active = 1`, [nowMs, nowMs, principalId, tenantId]));
121
+ const raw = rows[0]?.next_boundary_ms;
122
+ if (raw === null || raw === undefined)
123
+ return null;
124
+ const ms = Number(raw);
125
+ return Number.isFinite(ms) && ms > nowMs ? ms : null;
126
+ }
55
127
  class DbPermissionFactsAdapter {
56
128
  pool;
57
129
  registry;
@@ -77,14 +149,24 @@ class DbPermissionFactsAdapter {
77
149
  throw new core_1.AuthorizationUnavailableError('permission version store unavailable', cause);
78
150
  }
79
151
  }
152
+ /**
153
+ * The coarse grants + (ADR-0035) the LIVE role ids they were derived from + the time bound of
154
+ * those roles. The version is read FIRST, so a bump racing this load can only make the facts look
155
+ * older (one extra reload), never newer. `roleIds` is always reported (empty = genuinely none),
156
+ * so the session layer binds the in-effect roles, never the credential's `ic_roles`.
157
+ * `validUntilMs` = the next posting window boundary (absent when there is none); an outage on
158
+ * that read fails closed like every other read here.
159
+ */
80
160
  async loadCoarseGrants(principalId, tenantId) {
81
161
  let version;
82
162
  let roleIds;
83
163
  let rawKeys;
164
+ let validUntilMs;
84
165
  try {
85
166
  version = await this.readVersion(tenantId);
86
167
  roleIds = await this.readRoleIds(principalId, tenantId);
87
168
  rawKeys = roleIds.length === 0 ? [] : await this.readGrantKeys(roleIds);
169
+ validUntilMs = await readNextPostingBoundaryMs(this.pool, principalId, tenantId, this.now);
88
170
  }
89
171
  catch (cause) {
90
172
  if (cause instanceof core_1.AuthorizationUnavailableError)
@@ -92,7 +174,12 @@ class DbPermissionFactsAdapter {
92
174
  throw new core_1.AuthorizationUnavailableError('permission facts store unavailable', cause);
93
175
  }
94
176
  const allKeys = this.registry.listActions().map((a) => a.permissionKey);
95
- return { permissionsVersion: version, grantedPermissionKeys: expandGrantKeys(rawKeys, allKeys) };
177
+ return {
178
+ permissionsVersion: version,
179
+ grantedPermissionKeys: expandGrantKeys(rawKeys, allKeys),
180
+ roleIds,
181
+ ...(validUntilMs !== null ? { validUntilMs } : {}),
182
+ };
96
183
  }
97
184
  async readVersion(tenantId) {
98
185
  const rows = (await this.pool.query(`SELECT apv_version FROM \`shield_authz\`.\`authz_permission_version\`
@@ -13,8 +13,11 @@ interface AuthedRequest {
13
13
  * grant; a non-root caller needs the grant. (`role_permission` itself is not a
14
14
  * registered object type — it is administered THROUGH `role`, never classified directly.)
15
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>").
16
+ * Mutating routes pass `req.principal` as the actor: it stamps who granted what (surfaced by the
17
+ * grants-summary listing as "by <name>"), and grant AND revoke run the role-binding rule (a role
18
+ * also used outside the caller's subtree is root-only) and the ADR-0029 ceiling ("only grant or
19
+ * revoke what you hold"). Errors map in one place ({@link IdentityErrorInterceptor}): ceiling and
20
+ * binding → 403, a role outside the caller's subtree → 404, unknown key/bundle → 400.
18
21
  */
19
22
  export declare class GrantAdminController {
20
23
  private readonly grants;
@@ -25,7 +28,7 @@ export declare class GrantAdminController {
25
28
  * grantor NAME (resolved server-side — no raw uuid). Gated `role:read`.
26
29
  *
27
30
  * ⚠️ 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),
31
+ * would be captured by RoleController's `@Get('roles/:uuid')` (registered first),
29
32
  * routing this to getRole('role-grants') → a 500. Keep it off the `roles/:uuid` shape.
30
33
  */
31
34
  grantsSummary(req: AuthedRequest, query: {
@@ -63,7 +66,7 @@ export declare class GrantAdminController {
63
66
  success: boolean;
64
67
  }>;
65
68
  /** Revoke one permission key from a role. `:key` is URL-encoded (contains ':'). */
66
- revoke(uuid: string, key: string): Promise<{
69
+ revoke(req: AuthedRequest, uuid: string, key: string): Promise<{
67
70
  success: boolean;
68
71
  }>;
69
72
  /**
@@ -89,7 +92,7 @@ export declare class GrantAdminController {
89
92
  * `role_permission` (see design §5.1 caveat re: shared keys). `:key` is the bundle slug.
90
93
  * Unknown bundle → 400. Gated `role:update`.
91
94
  */
92
- revokeBundle(uuid: string, key: string): Promise<{
95
+ revokeBundle(req: AuthedRequest, uuid: string, key: string): Promise<{
93
96
  success: boolean;
94
97
  }>;
95
98
  }
@@ -1 +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;AAG1E,qFAAqF;AACrF,UAAU,aAAa;IACrB,SAAS,EAAE,SAAS,CAAC;CACtB;AAaD;;;;;;;;;;;GAWG;AACH,qBACa,oBAAoB;IACnB,OAAO,CAAC,QAAQ,CAAC,MAAM;gBAAN,MAAM,EAAE,iBAAiB;IAEtD;;;;;;;;OAQG;IAGG,aAAa,CACV,GAAG,EAAE,aAAa,EAEzB,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;kGAC8F;IAGxF,IAAI,CAAQ,GAAG,EAAE,aAAa,EAAiB,IAAI,EAAE,MAAM;;;IAIjE,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"}
1
+ {"version":3,"file":"grant-admin.controller.d.ts","sourceRoot":"","sources":["../../../src/features/authz/grant-admin.controller.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,EAAE,iBAAiB,EAAe,MAAM,0BAA0B,CAAC;AAI1E,qFAAqF;AACrF,UAAU,aAAa;IACrB,SAAS,EAAE,SAAS,CAAC;CACtB;AAaD;;;;;;;;;;;;;;GAcG;AACH,qBAEa,oBAAoB;IACnB,OAAO,CAAC,QAAQ,CAAC,MAAM;gBAAN,MAAM,EAAE,iBAAiB;IAEtD;;;;;;;;OAQG;IAGG,aAAa,CACV,GAAG,EAAE,aAAa,EAEzB,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;kGAC8F;IAGxF,IAAI,CAAQ,GAAG,EAAE,aAAa,EAAiB,IAAI,EAAE,MAAM;;;IAIjE,0CAA0C;IAGpC,KAAK,CACF,GAAG,EAAE,aAAa,EACV,IAAI,EAAE,MAAM,EACnB,IAAI,EAAE;QAAE,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE;;;IAahC,mFAAmF;IAG7E,MAAM,CAAQ,GAAG,EAAE,aAAa,EAAiB,IAAI,EAAE,MAAM,EAAgB,GAAG,EAAE,MAAM;;;IAO9F;;;;OAIG;IAGG,OAAO;;;IAIb;;;;OAIG;IAGG,WAAW,CACR,GAAG,EAAE,aAAa,EACV,IAAI,EAAE,MAAM,EACnB,IAAI,EAAE;QAAE,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE;;;IAahC;;;;OAIG;IAGG,YAAY,CACT,GAAG,EAAE,aAAa,EACV,IAAI,EAAE,MAAM,EACb,GAAG,EAAE,MAAM;;;CAU5B"}
@@ -19,6 +19,7 @@ exports.GrantAdminController = void 0;
19
19
  const common_1 = require("@nestjs/common");
20
20
  const guard_1 = require("@astram/guard");
21
21
  const grant_admin_service_js_1 = require("./grant-admin.service.js");
22
+ const identity_error_interceptor_js_1 = require("../shared/identity-error.interceptor.js");
22
23
  /**
23
24
  * Derive the caller's subtree scope from the resolved principal (Task 4.2). Uses the SAME cached
24
25
  * `scopeSubtreeNodeIds` the RecordEngine's list-scope uses (one source of truth). Fail-safe to the
@@ -38,8 +39,11 @@ function subtreeScopeOf(req) {
38
39
  * grant; a non-root caller needs the grant. (`role_permission` itself is not a
39
40
  * registered object type — it is administered THROUGH `role`, never classified directly.)
40
41
  *
41
- * Mutating routes pass `req.principal.id` as the grantor so `role_permission` records
42
- * who granted what (surfaced by the grants-summary listing as "by <name>").
42
+ * Mutating routes pass `req.principal` as the actor: it stamps who granted what (surfaced by the
43
+ * grants-summary listing as "by <name>"), and grant AND revoke run the role-binding rule (a role
44
+ * also used outside the caller's subtree is root-only) and the ADR-0029 ceiling ("only grant or
45
+ * revoke what you hold"). Errors map in one place ({@link IdentityErrorInterceptor}): ceiling and
46
+ * binding → 403, a role outside the caller's subtree → 404, unknown key/bundle → 400.
43
47
  */
44
48
  let GrantAdminController = class GrantAdminController {
45
49
  grants;
@@ -52,7 +56,7 @@ let GrantAdminController = class GrantAdminController {
52
56
  * grantor NAME (resolved server-side — no raw uuid). Gated `role:read`.
53
57
  *
54
58
  * ⚠️ Path is `access/role-grants` (NOT under `roles/...`) on purpose: a `roles/<x>` path
55
- * would be captured by IdentityController's `@Get('roles/:uuid')` (registered first),
59
+ * would be captured by RoleController's `@Get('roles/:uuid')` (registered first),
56
60
  * routing this to getRole('role-grants') → a 500. Keep it off the `roles/:uuid` shape.
57
61
  */
58
62
  async grantsSummary(req, query) {
@@ -86,7 +90,7 @@ let GrantAdminController = class GrantAdminController {
86
90
  if (!key)
87
91
  throw new common_1.BadRequestException('key required');
88
92
  try {
89
- await this.grants.grant(uuid, key, req.principal?.id);
93
+ await this.grants.grant(uuid, key, req.principal);
90
94
  }
91
95
  catch (e) {
92
96
  if (e instanceof grant_admin_service_js_1.BadKeyError)
@@ -96,8 +100,8 @@ let GrantAdminController = class GrantAdminController {
96
100
  return { success: true };
97
101
  }
98
102
  /** Revoke one permission key from a role. `:key` is URL-encoded (contains ':'). */
99
- async revoke(uuid, key) {
100
- await this.grants.revoke(uuid, decodeURIComponent(key));
103
+ async revoke(req, uuid, key) {
104
+ await this.grants.revoke(uuid, decodeURIComponent(key), req.principal);
101
105
  return { success: true };
102
106
  }
103
107
  // ── Access bundles ────────────────────────────────────────────────────────
@@ -119,7 +123,7 @@ let GrantAdminController = class GrantAdminController {
119
123
  if (!key)
120
124
  throw new common_1.BadRequestException('key required');
121
125
  try {
122
- await this.grants.grantBundle(uuid, key, req.principal?.id);
126
+ await this.grants.grantBundle(uuid, key, req.principal);
123
127
  }
124
128
  catch (e) {
125
129
  if (e instanceof grant_admin_service_js_1.BadKeyError)
@@ -133,9 +137,9 @@ let GrantAdminController = class GrantAdminController {
133
137
  * `role_permission` (see design §5.1 caveat re: shared keys). `:key` is the bundle slug.
134
138
  * Unknown bundle → 400. Gated `role:update`.
135
139
  */
136
- async revokeBundle(uuid, key) {
140
+ async revokeBundle(req, uuid, key) {
137
141
  try {
138
- await this.grants.revokeBundle(uuid, decodeURIComponent(key));
142
+ await this.grants.revokeBundle(uuid, decodeURIComponent(key), req.principal);
139
143
  }
140
144
  catch (e) {
141
145
  if (e instanceof grant_admin_service_js_1.BadKeyError)
@@ -184,10 +188,11 @@ __decorate([
184
188
  __decorate([
185
189
  (0, common_1.Delete)('roles/:uuid/permissions/:key'),
186
190
  (0, guard_1.Classified)({ typeKey: 'role', action: 'update' }),
187
- __param(0, (0, common_1.Param)('uuid')),
188
- __param(1, (0, common_1.Param)('key')),
191
+ __param(0, (0, common_1.Req)()),
192
+ __param(1, (0, common_1.Param)('uuid')),
193
+ __param(2, (0, common_1.Param)('key')),
189
194
  __metadata("design:type", Function),
190
- __metadata("design:paramtypes", [String, String]),
195
+ __metadata("design:paramtypes", [Object, String, String]),
191
196
  __metadata("design:returntype", Promise)
192
197
  ], GrantAdminController.prototype, "revoke", null);
193
198
  __decorate([
@@ -210,13 +215,15 @@ __decorate([
210
215
  __decorate([
211
216
  (0, common_1.Delete)('roles/:uuid/access-bundles/:key'),
212
217
  (0, guard_1.Classified)({ typeKey: 'role', action: 'update' }),
213
- __param(0, (0, common_1.Param)('uuid')),
214
- __param(1, (0, common_1.Param)('key')),
218
+ __param(0, (0, common_1.Req)()),
219
+ __param(1, (0, common_1.Param)('uuid')),
220
+ __param(2, (0, common_1.Param)('key')),
215
221
  __metadata("design:type", Function),
216
- __metadata("design:paramtypes", [String, String]),
222
+ __metadata("design:paramtypes", [Object, String, String]),
217
223
  __metadata("design:returntype", Promise)
218
224
  ], GrantAdminController.prototype, "revokeBundle", null);
219
225
  exports.GrantAdminController = GrantAdminController = __decorate([
220
226
  (0, common_1.Controller)('identity'),
227
+ (0, common_1.UseInterceptors)(identity_error_interceptor_js_1.IdentityErrorInterceptor),
221
228
  __metadata("design:paramtypes", [grant_admin_service_js_1.GrantAdminService])
222
229
  ], GrantAdminController);