@astram/guard 0.2.0 → 0.3.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 (170) hide show
  1. package/dist/auth/account.controller.d.ts +245 -0
  2. package/dist/auth/account.controller.d.ts.map +1 -0
  3. package/dist/auth/account.controller.js +673 -0
  4. package/dist/auth/account.controller.js.map +1 -0
  5. package/dist/auth/auth.controller.d.ts +219 -5
  6. package/dist/auth/auth.controller.d.ts.map +1 -1
  7. package/dist/auth/auth.controller.js +790 -12
  8. package/dist/auth/auth.controller.js.map +1 -0
  9. package/dist/auth/auth.module.d.ts +100 -2
  10. package/dist/auth/auth.module.d.ts.map +1 -1
  11. package/dist/auth/auth.module.js +299 -7
  12. package/dist/auth/auth.module.js.map +1 -0
  13. package/dist/auth/connections.js.map +1 -0
  14. package/dist/auth/dto.d.ts +16 -6
  15. package/dist/auth/dto.d.ts.map +1 -1
  16. package/dist/auth/dto.js.map +1 -0
  17. package/dist/auth/pending-mfa-store.js.map +1 -0
  18. package/dist/auth/persistence.module.js.map +1 -0
  19. package/dist/auth/ports/audit-sink.d.ts +47 -0
  20. package/dist/auth/ports/audit-sink.d.ts.map +1 -0
  21. package/dist/auth/ports/audit-sink.js +20 -0
  22. package/dist/auth/ports/audit-sink.js.map +1 -0
  23. package/dist/auth/ports/credential-writer.d.ts +23 -0
  24. package/dist/auth/ports/credential-writer.d.ts.map +1 -0
  25. package/dist/auth/ports/credential-writer.js +2 -0
  26. package/dist/auth/ports/credential-writer.js.map +1 -0
  27. package/dist/auth/ports/email-sender.d.ts +14 -0
  28. package/dist/auth/ports/email-sender.d.ts.map +1 -0
  29. package/dist/auth/ports/email-sender.js +2 -0
  30. package/dist/auth/ports/email-sender.js.map +1 -0
  31. package/dist/auth/ports/mfa-device-store.d.ts +45 -0
  32. package/dist/auth/ports/mfa-device-store.d.ts.map +1 -0
  33. package/dist/auth/ports/mfa-device-store.js +13 -0
  34. package/dist/auth/ports/mfa-device-store.js.map +1 -0
  35. package/dist/auth/ports/profile-writer.d.ts +12 -0
  36. package/dist/auth/ports/profile-writer.d.ts.map +1 -0
  37. package/dist/auth/ports/profile-writer.js +2 -0
  38. package/dist/auth/ports/profile-writer.js.map +1 -0
  39. package/dist/auth/ports/recovery-code-store.d.ts +10 -0
  40. package/dist/auth/ports/recovery-code-store.d.ts.map +1 -0
  41. package/dist/auth/ports/recovery-code-store.js +2 -0
  42. package/dist/auth/ports/recovery-code-store.js.map +1 -0
  43. package/dist/auth/ports/reset-token-store.d.ts +12 -0
  44. package/dist/auth/ports/reset-token-store.d.ts.map +1 -0
  45. package/dist/auth/ports/reset-token-store.js +2 -0
  46. package/dist/auth/ports/reset-token-store.js.map +1 -0
  47. package/dist/auth/ports/session-facts-projector.d.ts +44 -0
  48. package/dist/auth/ports/session-facts-projector.d.ts.map +1 -0
  49. package/dist/auth/ports/session-facts-projector.js +30 -0
  50. package/dist/auth/ports/session-facts-projector.js.map +1 -0
  51. package/dist/auth/ports/session-sweeper.d.ts +12 -0
  52. package/dist/auth/ports/session-sweeper.d.ts.map +1 -0
  53. package/dist/auth/ports/session-sweeper.js +2 -0
  54. package/dist/auth/ports/session-sweeper.js.map +1 -0
  55. package/dist/auth/profile-resolver.d.ts +73 -0
  56. package/dist/auth/profile-resolver.d.ts.map +1 -0
  57. package/dist/auth/profile-resolver.js +28 -0
  58. package/dist/auth/profile-resolver.js.map +1 -0
  59. package/dist/auth/rate-limit/attempt-limiter.d.ts +91 -0
  60. package/dist/auth/rate-limit/attempt-limiter.d.ts.map +1 -0
  61. package/dist/auth/rate-limit/attempt-limiter.js +133 -0
  62. package/dist/auth/rate-limit/attempt-limiter.js.map +1 -0
  63. package/dist/auth/tokens.d.ts +174 -0
  64. package/dist/auth/tokens.d.ts.map +1 -1
  65. package/dist/auth/tokens.js +190 -1
  66. package/dist/auth/tokens.js.map +1 -0
  67. package/dist/auth/totp-enrollment.d.ts +42 -0
  68. package/dist/auth/totp-enrollment.d.ts.map +1 -0
  69. package/dist/auth/totp-enrollment.js +96 -0
  70. package/dist/auth/totp-enrollment.js.map +1 -0
  71. package/dist/authentication/argon2id-password-hasher.js.map +1 -0
  72. package/dist/authentication/mariadb-credential-resolver.d.ts +38 -24
  73. package/dist/authentication/mariadb-credential-resolver.d.ts.map +1 -1
  74. package/dist/authentication/mariadb-credential-resolver.js +42 -28
  75. package/dist/authentication/mariadb-credential-resolver.js.map +1 -0
  76. package/dist/authz/cross-tenant-grant-writer.d.ts +259 -0
  77. package/dist/authz/cross-tenant-grant-writer.d.ts.map +1 -0
  78. package/dist/authz/cross-tenant-grant-writer.js +397 -0
  79. package/dist/authz/cross-tenant-grant-writer.js.map +1 -0
  80. package/dist/authz/manifest-operation-authority-adapter.d.ts +65 -0
  81. package/dist/authz/manifest-operation-authority-adapter.d.ts.map +1 -0
  82. package/dist/authz/manifest-operation-authority-adapter.js +108 -0
  83. package/dist/authz/manifest-operation-authority-adapter.js.map +1 -0
  84. package/dist/authz/mariadb-cross-tenant-grant-adapter.d.ts +90 -0
  85. package/dist/authz/mariadb-cross-tenant-grant-adapter.d.ts.map +1 -0
  86. package/dist/authz/mariadb-cross-tenant-grant-adapter.js +211 -0
  87. package/dist/authz/mariadb-cross-tenant-grant-adapter.js.map +1 -0
  88. package/dist/authz/mariadb-record-rule-adapter.d.ts +60 -0
  89. package/dist/authz/mariadb-record-rule-adapter.d.ts.map +1 -0
  90. package/dist/authz/mariadb-record-rule-adapter.js +154 -0
  91. package/dist/authz/mariadb-record-rule-adapter.js.map +1 -0
  92. package/dist/authz/mariadb-record-share-adapter.d.ts +75 -0
  93. package/dist/authz/mariadb-record-share-adapter.d.ts.map +1 -0
  94. package/dist/authz/mariadb-record-share-adapter.js +106 -0
  95. package/dist/authz/mariadb-record-share-adapter.js.map +1 -0
  96. package/dist/authz/record-share-cascade.d.ts +52 -0
  97. package/dist/authz/record-share-cascade.d.ts.map +1 -0
  98. package/dist/authz/record-share-cascade.js +49 -0
  99. package/dist/authz/record-share-cascade.js.map +1 -0
  100. package/dist/authz/record-share-writer.d.ts +175 -0
  101. package/dist/authz/record-share-writer.d.ts.map +1 -0
  102. package/dist/authz/record-share-writer.js +225 -0
  103. package/dist/authz/record-share-writer.js.map +1 -0
  104. package/dist/authz/wire-record-authorization.d.ts +66 -0
  105. package/dist/authz/wire-record-authorization.d.ts.map +1 -0
  106. package/dist/authz/wire-record-authorization.js +34 -0
  107. package/dist/authz/wire-record-authorization.js.map +1 -0
  108. package/dist/boot/boot-sequence.js.map +1 -0
  109. package/dist/boot/boot.module.js.map +1 -0
  110. package/dist/completeness/catalog-reconciler.d.ts +21 -0
  111. package/dist/completeness/catalog-reconciler.d.ts.map +1 -0
  112. package/dist/completeness/catalog-reconciler.js +74 -0
  113. package/dist/completeness/catalog-reconciler.js.map +1 -0
  114. package/dist/completeness/operation-inventory-scanner.d.ts +17 -0
  115. package/dist/completeness/operation-inventory-scanner.d.ts.map +1 -0
  116. package/dist/completeness/operation-inventory-scanner.js +79 -0
  117. package/dist/completeness/operation-inventory-scanner.js.map +1 -0
  118. package/dist/completeness/ports/catalog-store.d.ts +33 -0
  119. package/dist/completeness/ports/catalog-store.d.ts.map +1 -0
  120. package/dist/completeness/ports/catalog-store.js +2 -0
  121. package/dist/completeness/ports/catalog-store.js.map +1 -0
  122. package/dist/config/app-config.js.map +1 -0
  123. package/dist/config/config.module.js.map +1 -0
  124. package/dist/enforcement/classification.decorator.d.ts +33 -14
  125. package/dist/enforcement/classification.decorator.d.ts.map +1 -1
  126. package/dist/enforcement/classification.decorator.js +34 -14
  127. package/dist/enforcement/classification.decorator.js.map +1 -0
  128. package/dist/enforcement/core-seam.js.map +1 -0
  129. package/dist/enforcement/enforcement.module.js.map +1 -0
  130. package/dist/enforcement/global-enforcement.guard.d.ts +29 -19
  131. package/dist/enforcement/global-enforcement.guard.d.ts.map +1 -1
  132. package/dist/enforcement/global-enforcement.guard.js +108 -25
  133. package/dist/enforcement/global-enforcement.guard.js.map +1 -0
  134. package/dist/enforcement/reenroll-allowlist.d.ts +49 -0
  135. package/dist/enforcement/reenroll-allowlist.d.ts.map +1 -0
  136. package/dist/enforcement/reenroll-allowlist.js +77 -0
  137. package/dist/enforcement/reenroll-allowlist.js.map +1 -0
  138. package/dist/index.d.ts +35 -3
  139. package/dist/index.d.ts.map +1 -1
  140. package/dist/index.js +159 -7
  141. package/dist/index.js.map +1 -0
  142. package/dist/module/shield-tokens.d.ts +15 -0
  143. package/dist/module/shield-tokens.d.ts.map +1 -0
  144. package/dist/module/shield-tokens.js +17 -0
  145. package/dist/module/shield-tokens.js.map +1 -0
  146. package/dist/module/shield.module.d.ts +153 -9
  147. package/dist/module/shield.module.d.ts.map +1 -1
  148. package/dist/module/shield.module.js +181 -24
  149. package/dist/module/shield.module.js.map +1 -0
  150. package/dist/request-auth/request-principal.d.ts +42 -0
  151. package/dist/request-auth/request-principal.d.ts.map +1 -0
  152. package/dist/request-auth/request-principal.js +87 -0
  153. package/dist/request-auth/request-principal.js.map +1 -0
  154. package/dist/request-auth/session-facts-als.d.ts +65 -0
  155. package/dist/request-auth/session-facts-als.d.ts.map +1 -0
  156. package/dist/request-auth/session-facts-als.js +57 -0
  157. package/dist/request-auth/session-facts-als.js.map +1 -0
  158. package/dist/request-auth/session-token-binder.d.ts +130 -0
  159. package/dist/request-auth/session-token-binder.d.ts.map +1 -0
  160. package/dist/request-auth/session-token-binder.js +277 -0
  161. package/dist/request-auth/session-token-binder.js.map +1 -0
  162. package/dist/request-auth/tokens.d.ts +14 -0
  163. package/dist/request-auth/tokens.d.ts.map +1 -0
  164. package/dist/request-auth/tokens.js +16 -0
  165. package/dist/request-auth/tokens.js.map +1 -0
  166. package/dist/session/crypto-adapters.js.map +1 -0
  167. package/dist/session/index.js.map +1 -0
  168. package/dist/session/mariadb-session-record-store.js.map +1 -0
  169. package/dist/session/redis-hot-session-store.js.map +1 -0
  170. package/package.json +2 -2
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cross-tenant-grant-writer.js","sourceRoot":"","sources":["../../src/authz/cross-tenant-grant-writer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,65 @@
1
+ /**
2
+ * `ManifestOperationAuthorityAdapter` — the concrete {@link OperationAuthorityPort} for
3
+ * `@astram/guard`, backed by a DECLARED operation manifest (ADR-0008). It answers
4
+ * `resolveDelegatedAuthority(operationKey, stepKey)`: what scoped, non-wildcard authority does
5
+ * a trusted operation's step carry?
6
+ *
7
+ * ── WHAT DELEGATED AUTHORITY IS (ADR-0008 / gate 7): one server operation performs an internal
8
+ * side-effect on an object type the CALLER may not touch directly (e.g. "create user" also
9
+ * writes an onboarding record in a separate object type). That internal step must run under
10
+ * the OPERATION'S OWN declared, scoped, least-privilege authority — NOT the caller's grants,
11
+ * and NEVER a wildcard "skip all checks". This adapter resolves THAT declared authority.
12
+ *
13
+ * ── PURE, NO DB (ADR-0008). This is DECLARED metadata — the operation's own manifest, known at
14
+ * build/registration time. So the lookup is SYNCHRONOUS and in-memory (no store, no I/O). The
15
+ * port is contracted synchronous; a manifest is the natural backing.
16
+ *
17
+ * ── THE MANIFEST: `${operationKey}:${stepKey}` → {@link DelegatedAuthority}. The consumer
18
+ * DECLARES it by constructing the adapter with a map (or `[key, authority]` entries). An
19
+ * UNKNOWN key ⇒ `undefined` (pass-through: NO delegation — evaluate under the caller — the
20
+ * common case).
21
+ *
22
+ * ── FAIL-CLOSED ON A MALFORMED / WILDCARD ENTRY. Core's gate-7 overlay re-checks scope with
23
+ * strict membership, but this adapter NEVER hands back a wildcard or malformed authority: a
24
+ * declared entry whose `objectTypeKeys` / `allowedActions` is not a non-empty array of
25
+ * non-empty strings, OR contains a wildcard `'*'`, is treated as UNDEFINED (no delegation).
26
+ * An operation cannot accidentally (or maliciously) declare a `'*'` and gain a root
27
+ * reach — the malformed declaration simply grants no delegated authority.
28
+ *
29
+ * ── EMPTY MANIFEST = every lookup returns `undefined` = pass-through everywhere. A consumer with
30
+ * no trusted operations yet wires this with an empty manifest — identical runtime behavior to
31
+ * the interim `emptyOperationAuthority`, but now a REAL, declared surface ready for the first
32
+ * trusted operation.
33
+ *
34
+ * ── WHY HERE, NOT CORE (ADR-0003): core defines the PORT + the gate-7 overlay that consumes it;
35
+ * the manifest-backed concrete lives in guard and is injected into the wired decision
36
+ * authority. It uses `@astram/core` TYPES only (erased at build).
37
+ */
38
+ import type { DelegatedAuthority, OperationAuthorityPort } from '@astram/core';
39
+ /** The DI token the adapter is bound under (parity with the other authz-port tokens). */
40
+ export declare const SHIELD_OPERATION_AUTHORITY_PORT: unique symbol;
41
+ /**
42
+ * A single declared operation-step authority. Mirrors core's {@link DelegatedAuthority} (a
43
+ * scoped, NON-wildcard object-type + action ceiling) — declared by the consumer per trusted
44
+ * operation step.
45
+ */
46
+ export interface OperationManifestEntry {
47
+ /** The trusted operation's stable key (its manifest identity). */
48
+ readonly operationKey: string;
49
+ /** The specific internal step within the operation. */
50
+ readonly stepKey: string;
51
+ /** The declared authority the step carries (scoped, non-wildcard). */
52
+ readonly authority: DelegatedAuthority;
53
+ }
54
+ export declare class ManifestOperationAuthorityAdapter implements OperationAuthorityPort {
55
+ private readonly manifest;
56
+ /**
57
+ * @param entries the declared operation-step authorities. Each malformed / wildcard entry is
58
+ * DROPPED at construction (fail-closed) — it will resolve to `undefined` (no delegation)
59
+ * rather than ever hand back an unsafe authority. A duplicate `${op}:${step}` key keeps the
60
+ * LAST well-formed declaration.
61
+ */
62
+ constructor(entries?: readonly OperationManifestEntry[]);
63
+ resolveDelegatedAuthority(operationKey: string, stepKey: string): DelegatedAuthority | undefined;
64
+ }
65
+ //# sourceMappingURL=manifest-operation-authority-adapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manifest-operation-authority-adapter.d.ts","sourceRoot":"","sources":["../../src/authz/manifest-operation-authority-adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AAEH,OAAO,KAAK,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC;AAE/E,yFAAyF;AACzF,eAAO,MAAM,+BAA+B,eAA4C,CAAC;AAEzF;;;;GAIG;AACH,MAAM,WAAW,sBAAsB;IACrC,kEAAkE;IAClE,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,uDAAuD;IACvD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,sEAAsE;IACtE,QAAQ,CAAC,SAAS,EAAE,kBAAkB,CAAC;CACxC;AA6BD,qBAAa,iCAAkC,YAAW,sBAAsB;IAC9E,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAkC;IAE3D;;;;;OAKG;gBACS,OAAO,GAAE,SAAS,sBAAsB,EAAO;IAuB3D,yBAAyB,CACvB,YAAY,EAAE,MAAM,EACpB,OAAO,EAAE,MAAM,GACd,kBAAkB,GAAG,SAAS;CAalC"}
@@ -0,0 +1,108 @@
1
+ "use strict";
2
+ /**
3
+ * `ManifestOperationAuthorityAdapter` — the concrete {@link OperationAuthorityPort} for
4
+ * `@astram/guard`, backed by a DECLARED operation manifest (ADR-0008). It answers
5
+ * `resolveDelegatedAuthority(operationKey, stepKey)`: what scoped, non-wildcard authority does
6
+ * a trusted operation's step carry?
7
+ *
8
+ * ── WHAT DELEGATED AUTHORITY IS (ADR-0008 / gate 7): one server operation performs an internal
9
+ * side-effect on an object type the CALLER may not touch directly (e.g. "create user" also
10
+ * writes an onboarding record in a separate object type). That internal step must run under
11
+ * the OPERATION'S OWN declared, scoped, least-privilege authority — NOT the caller's grants,
12
+ * and NEVER a wildcard "skip all checks". This adapter resolves THAT declared authority.
13
+ *
14
+ * ── PURE, NO DB (ADR-0008). This is DECLARED metadata — the operation's own manifest, known at
15
+ * build/registration time. So the lookup is SYNCHRONOUS and in-memory (no store, no I/O). The
16
+ * port is contracted synchronous; a manifest is the natural backing.
17
+ *
18
+ * ── THE MANIFEST: `${operationKey}:${stepKey}` → {@link DelegatedAuthority}. The consumer
19
+ * DECLARES it by constructing the adapter with a map (or `[key, authority]` entries). An
20
+ * UNKNOWN key ⇒ `undefined` (pass-through: NO delegation — evaluate under the caller — the
21
+ * common case).
22
+ *
23
+ * ── FAIL-CLOSED ON A MALFORMED / WILDCARD ENTRY. Core's gate-7 overlay re-checks scope with
24
+ * strict membership, but this adapter NEVER hands back a wildcard or malformed authority: a
25
+ * declared entry whose `objectTypeKeys` / `allowedActions` is not a non-empty array of
26
+ * non-empty strings, OR contains a wildcard `'*'`, is treated as UNDEFINED (no delegation).
27
+ * An operation cannot accidentally (or maliciously) declare a `'*'` and gain a root
28
+ * reach — the malformed declaration simply grants no delegated authority.
29
+ *
30
+ * ── EMPTY MANIFEST = every lookup returns `undefined` = pass-through everywhere. A consumer with
31
+ * no trusted operations yet wires this with an empty manifest — identical runtime behavior to
32
+ * the interim `emptyOperationAuthority`, but now a REAL, declared surface ready for the first
33
+ * trusted operation.
34
+ *
35
+ * ── WHY HERE, NOT CORE (ADR-0003): core defines the PORT + the gate-7 overlay that consumes it;
36
+ * the manifest-backed concrete lives in guard and is injected into the wired decision
37
+ * authority. It uses `@astram/core` TYPES only (erased at build).
38
+ */
39
+ Object.defineProperty(exports, "__esModule", { value: true });
40
+ exports.ManifestOperationAuthorityAdapter = exports.SHIELD_OPERATION_AUTHORITY_PORT = void 0;
41
+ /** The DI token the adapter is bound under (parity with the other authz-port tokens). */
42
+ exports.SHIELD_OPERATION_AUTHORITY_PORT = Symbol('SHIELD_OPERATION_AUTHORITY_PORT');
43
+ /** Compose the internal manifest map key from an operation + step. */
44
+ function manifestKey(operationKey, stepKey) {
45
+ return `${operationKey}:${stepKey}`;
46
+ }
47
+ /**
48
+ * Whether a declared authority is WELL-FORMED and non-wildcard: both `objectTypeKeys` and
49
+ * `allowedActions` must be non-empty arrays of non-empty strings, and NEITHER may contain a
50
+ * wildcard `'*'`. A malformed / wildcard authority is rejected (⇒ treated as no delegation).
51
+ */
52
+ function isWellFormedAuthority(authority) {
53
+ if (!authority || typeof authority !== 'object')
54
+ return false;
55
+ return (isNonEmptyExplicitStringArray(authority.objectTypeKeys) &&
56
+ isNonEmptyExplicitStringArray(authority.allowedActions));
57
+ }
58
+ /** A non-empty array of non-empty strings, NONE of which is a wildcard `'*'`. */
59
+ function isNonEmptyExplicitStringArray(value) {
60
+ if (!Array.isArray(value) || value.length === 0)
61
+ return false;
62
+ for (const v of value) {
63
+ if (typeof v !== 'string' || v.length === 0 || v === '*')
64
+ return false;
65
+ }
66
+ return true;
67
+ }
68
+ class ManifestOperationAuthorityAdapter {
69
+ manifest;
70
+ /**
71
+ * @param entries the declared operation-step authorities. Each malformed / wildcard entry is
72
+ * DROPPED at construction (fail-closed) — it will resolve to `undefined` (no delegation)
73
+ * rather than ever hand back an unsafe authority. A duplicate `${op}:${step}` key keeps the
74
+ * LAST well-formed declaration.
75
+ */
76
+ constructor(entries = []) {
77
+ this.manifest = new Map();
78
+ for (const entry of entries) {
79
+ if (typeof entry.operationKey !== 'string' ||
80
+ entry.operationKey.length === 0 ||
81
+ typeof entry.stepKey !== 'string' ||
82
+ entry.stepKey.length === 0) {
83
+ continue; // malformed key — skip
84
+ }
85
+ if (!isWellFormedAuthority(entry.authority)) {
86
+ continue; // malformed / wildcard authority — skip (fail-closed → undefined)
87
+ }
88
+ // Freeze a defensive copy so a later mutation of the caller's arrays cannot widen it.
89
+ const frozen = {
90
+ objectTypeKeys: Object.freeze([...entry.authority.objectTypeKeys]),
91
+ allowedActions: Object.freeze([...entry.authority.allowedActions]),
92
+ };
93
+ this.manifest.set(manifestKey(entry.operationKey, entry.stepKey), frozen);
94
+ }
95
+ }
96
+ resolveDelegatedAuthority(operationKey, stepKey) {
97
+ if (typeof operationKey !== 'string' ||
98
+ operationKey.length === 0 ||
99
+ typeof stepKey !== 'string' ||
100
+ stepKey.length === 0) {
101
+ return undefined; // malformed lookup key ⇒ pass-through
102
+ }
103
+ // A present entry is already validated + frozen at construction — return it as-is. An
104
+ // absent key ⇒ undefined (pass-through: no delegation, the common case).
105
+ return this.manifest.get(manifestKey(operationKey, stepKey));
106
+ }
107
+ }
108
+ exports.ManifestOperationAuthorityAdapter = ManifestOperationAuthorityAdapter;
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manifest-operation-authority-adapter.js","sourceRoot":"","sources":["../../src/authz/manifest-operation-authority-adapter.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;;;AAIH,yFAAyF;AAC5E,QAAA,+BAA+B,GAAG,MAAM,CAAC,iCAAiC,CAAC,CAAC;AAgBzF,sEAAsE;AACtE,SAAS,WAAW,CAAC,YAAoB,EAAE,OAAe;IACxD,OAAO,GAAG,YAAY,IAAI,OAAO,EAAE,CAAC;AACtC,CAAC;AAED;;;;GAIG;AACH,SAAS,qBAAqB,CAAC,SAAyC;IACtE,IAAI,CAAC,SAAS,IAAI,OAAO,SAAS,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IAC9D,OAAO,CACL,6BAA6B,CAAC,SAAS,CAAC,cAAc,CAAC;QACvD,6BAA6B,CAAC,SAAS,CAAC,cAAc,CAAC,CACxD,CAAC;AACJ,CAAC;AAED,iFAAiF;AACjF,SAAS,6BAA6B,CAAC,KAAwB;IAC7D,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IAC9D,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;QACtB,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,KAAK,GAAG;YAAE,OAAO,KAAK,CAAC;IACzE,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAa,iCAAiC;IAC3B,QAAQ,CAAkC;IAE3D;;;;;OAKG;IACH,YAAY,UAA6C,EAAE;QACzD,IAAI,CAAC,QAAQ,GAAG,IAAI,GAAG,EAAE,CAAC;QAC1B,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;YAC5B,IACE,OAAO,KAAK,CAAC,YAAY,KAAK,QAAQ;gBACtC,KAAK,CAAC,YAAY,CAAC,MAAM,KAAK,CAAC;gBAC/B,OAAO,KAAK,CAAC,OAAO,KAAK,QAAQ;gBACjC,KAAK,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC,EAC1B,CAAC;gBACD,SAAS,CAAC,uBAAuB;YACnC,CAAC;YACD,IAAI,CAAC,qBAAqB,CAAC,KAAK,CAAC,SAAS,CAAC,EAAE,CAAC;gBAC5C,SAAS,CAAC,kEAAkE;YAC9E,CAAC;YACD,sFAAsF;YACtF,MAAM,MAAM,GAAuB;gBACjC,cAAc,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,KAAK,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC;gBAClE,cAAc,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,KAAK,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC;aACnE,CAAC;YACF,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,WAAW,CAAC,KAAK,CAAC,YAAY,EAAE,KAAK,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC,CAAC;QAC5E,CAAC;IACH,CAAC;IAED,yBAAyB,CACvB,YAAoB,EACpB,OAAe;QAEf,IACE,OAAO,YAAY,KAAK,QAAQ;YAChC,YAAY,CAAC,MAAM,KAAK,CAAC;YACzB,OAAO,OAAO,KAAK,QAAQ;YAC3B,OAAO,CAAC,MAAM,KAAK,CAAC,EACpB,CAAC;YACD,OAAO,SAAS,CAAC,CAAC,sCAAsC;QAC1D,CAAC;QACD,sFAAsF;QACtF,yEAAyE;QACzE,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,WAAW,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC,CAAC;IAC/D,CAAC;CACF;AAhDD,8EAgDC"}
@@ -0,0 +1,90 @@
1
+ /**
2
+ * `MariaDbCrossTenantGrantAdapter` — the concrete {@link CrossTenantGrantPort} for
3
+ * `@astram/guard`, backed by the `cross_tenant_grant` table (ADR-0009). It answers
4
+ * `findActiveGrant`: is there an ACTIVE (accepted, unexpired, unrevoked), consented grant
5
+ * that lets THIS grantee reach THIS owning tenant's object type (+ record) for an action?
6
+ *
7
+ * ── WHAT A CROSS-TENANT GRANT IS (ADR-0009): the ONLY sanctioned way (short of root)
8
+ * a principal reaches a resource owned by a DIFFERENT tenant. It is an explicit,
9
+ * CONSENTED handshake — the OWNING tenant (whose data is reached into) offers it, the
10
+ * GRANTEE tenant accepts it, either side may revoke it, and it always expires. It is NEVER
11
+ * a root bypass: it carries a HARD, non-wildcard action ceiling and a scope
12
+ * (whole object type, or a single record). This adapter only REPORTS an active grant; core's
13
+ * `isCrossTenantGrantApplicable` re-checks the ceiling + scope + expiry (defense-in-depth).
14
+ *
15
+ * ── WHY HERE, NOT CORE (ADR-0003): the `mariadb` driver is a real runtime dependency; core
16
+ * defines only the PORT. This adapter is INJECTED into core's wired decision authority. It
17
+ * uses `@astram/core` TYPES only (erased at build).
18
+ *
19
+ * ── THE MATCH (ADR-0009 — an ACTIVE grant): a row matches when ALL hold —
20
+ * • `ctg_owning_tenant_uuid` = the resource's owning tenant AND `ctg_type_key` = the object
21
+ * type;
22
+ * • the grant is ACTIVE: `ctg_status = 'accepted'` (consent completed) AND
23
+ * `ctg_revoked_at IS NULL` (not revoked by either side) AND `ctg_expires_at_ms > ?` (not
24
+ * expired — bound epoch-ms, timezone-immune) — a revoked/offered/expired grant is NEVER
25
+ * returned, so it can never open access even for a heartbeat;
26
+ * • the grantee is EITHER this user (`ctg_grantee_kind='user'` AND
27
+ * `ctg_grantee_uuid = grantee`) OR one of the grantee's roles (`ctg_grantee_kind='role'`
28
+ * AND `ctg_grantee_uuid IN (roleIds)`);
29
+ * • the action is in the ceiling — the action-ceiling membership check is left to core's
30
+ * grant-evaluator (which re-checks `allowedActions`), but we DO gate scope: an
31
+ * `object_type` grant matches any record of the type; a `record` grant matches ONLY when
32
+ * the query names the exact `ctg_record_uuid` (a record-scoped row is EXCLUDED for an
33
+ * object-type / record-less query so a NULL-compare never spuriously matches).
34
+ * ≥1 matching row ⇒ mapped to a {@link CrossTenantGrant} (first match). No matching row ⇒
35
+ * `null` (a NORMAL data-absence — no active consent — NOT an error, NOT a lockout).
36
+ *
37
+ * ── SCOPE + RECORD-ID HANDLING: `object_type` rows are always eligible; a `record` row is
38
+ * eligible ONLY when `query.recordId` is present AND equals `ctg_record_uuid`. When the
39
+ * query has NO recordId (an object-type-level question), the SQL restricts to
40
+ * `ctg_scope = 'object_type'` so a record row's `ctg_record_uuid` is never compared to a
41
+ * bound NULL (which would never match — but we make the intent explicit and avoid the
42
+ * accidental all-records reach a `record_uuid = NULL` OR-branch could imply).
43
+ *
44
+ * ── ACTION CEILING PARSING: `ctg_allowed_actions` is a JSON column (a JSON array of action-key
45
+ * strings). ⚠️ The mariadb driver AUTO-PARSES a JSON column into a JS ARRAY on read, so the
46
+ * cell arrives already-parsed — `parseAllowedActions` accepts BOTH an already-parsed array
47
+ * (production) and a JSON string (test mocks). A malformed/empty value ⇒ `[]` (an empty
48
+ * ceiling permits nothing — fail-closed; core's ceiling check then denies).
49
+ *
50
+ * ── EXPIRY (MUST-FIX #1 — timezone immunity): `ctg_expires_at_ms` is a BIGINT epoch-ms column
51
+ * (NOT a TIMESTAMP). The active-grant filter compares it `> ?` against a BOUND integer (the
52
+ * adapter's `now()` epoch-ms), so it is immune to host-timezone / `NOW()` wall-clock skew (a
53
+ * TIMESTAMP + `NOW()` comparison silently kept expired grants alive on a non-UTC host). Mirrors
54
+ * the reset-token store's `prt_expires_at_ms` BIGINT precedent.
55
+ *
56
+ * ── EMPTY roleIds: guarded — an empty role list must NOT emit `IN ()` (a MariaDB syntax
57
+ * error). We omit the role branch entirely when there are no roles, so a role-less grantee
58
+ * is matched by its user-grant rows alone.
59
+ *
60
+ * ── ADR-0016 (transient-vs-deny): a genuine store outage THROWS
61
+ * {@link AuthorizationUnavailableError} (→ 503 retryable) — NEVER a silent `null` that
62
+ * would mask an outage as "no grant".
63
+ *
64
+ * ── SAFETY: every value (owningTenantId, typeKey, grantee, each roleId, recordId) is BOUND
65
+ * as a `?` param — NEVER interpolated. A SQL payload in typeKey/recordId is data, not SQL.
66
+ * Mirrors {@link MariaDbRecordShareAdapter}.
67
+ */
68
+ import type { CrossTenantGrant, CrossTenantGrantPort, CrossTenantGrantQuery } from '@astram/core';
69
+ /** The DI token the adapter is bound under (parity with the share/rule-port tokens). */
70
+ export declare const SHIELD_CROSS_TENANT_GRANT_PORT: unique symbol;
71
+ /** The minimal query surface this adapter needs — a `mariadb` Pool satisfies it. */
72
+ export interface GrantQueryable {
73
+ query(sql: string, params?: readonly unknown[]): Promise<unknown>;
74
+ }
75
+ /**
76
+ * The concrete cross-tenant grant adapter. Answers `findActiveGrant` from `cross_tenant_grant`,
77
+ * matching user- and role-granted, ACCEPTED, unexpired, unrevoked grants for the owning tenant
78
+ * + object type (+ record for record-scoped).
79
+ */
80
+ export declare class MariaDbCrossTenantGrantAdapter implements CrossTenantGrantPort {
81
+ private readonly db;
82
+ private readonly schema;
83
+ private readonly now;
84
+ constructor(db: GrantQueryable, opts?: {
85
+ schema?: string;
86
+ nowMs?: () => number;
87
+ });
88
+ findActiveGrant(query: CrossTenantGrantQuery): Promise<CrossTenantGrant | null>;
89
+ }
90
+ //# sourceMappingURL=mariadb-cross-tenant-grant-adapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mariadb-cross-tenant-grant-adapter.d.ts","sourceRoot":"","sources":["../../src/authz/mariadb-cross-tenant-grant-adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkEG;AAEH,OAAO,KAAK,EACV,gBAAgB,EAChB,oBAAoB,EACpB,qBAAqB,EAEtB,MAAM,cAAc,CAAC;AAGtB,wFAAwF;AACxF,eAAO,MAAM,8BAA8B,eAA2C,CAAC;AAKvF,oFAAoF;AACpF,MAAM,WAAW,cAAc;IAC7B,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,SAAS,OAAO,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACnE;AAsDD;;;;GAIG;AACH,qBAAa,8BAA+B,YAAW,oBAAoB;IACzE,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAiB;IACpC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAe;gBAEvB,EAAE,EAAE,cAAc,EAAE,IAAI,GAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,MAAM,CAAA;KAAO;IAQ9E,eAAe,CAAC,KAAK,EAAE,qBAAqB,GAAG,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC;CAuFtF"}
@@ -0,0 +1,211 @@
1
+ "use strict";
2
+ /**
3
+ * `MariaDbCrossTenantGrantAdapter` — the concrete {@link CrossTenantGrantPort} for
4
+ * `@astram/guard`, backed by the `cross_tenant_grant` table (ADR-0009). It answers
5
+ * `findActiveGrant`: is there an ACTIVE (accepted, unexpired, unrevoked), consented grant
6
+ * that lets THIS grantee reach THIS owning tenant's object type (+ record) for an action?
7
+ *
8
+ * ── WHAT A CROSS-TENANT GRANT IS (ADR-0009): the ONLY sanctioned way (short of root)
9
+ * a principal reaches a resource owned by a DIFFERENT tenant. It is an explicit,
10
+ * CONSENTED handshake — the OWNING tenant (whose data is reached into) offers it, the
11
+ * GRANTEE tenant accepts it, either side may revoke it, and it always expires. It is NEVER
12
+ * a root bypass: it carries a HARD, non-wildcard action ceiling and a scope
13
+ * (whole object type, or a single record). This adapter only REPORTS an active grant; core's
14
+ * `isCrossTenantGrantApplicable` re-checks the ceiling + scope + expiry (defense-in-depth).
15
+ *
16
+ * ── WHY HERE, NOT CORE (ADR-0003): the `mariadb` driver is a real runtime dependency; core
17
+ * defines only the PORT. This adapter is INJECTED into core's wired decision authority. It
18
+ * uses `@astram/core` TYPES only (erased at build).
19
+ *
20
+ * ── THE MATCH (ADR-0009 — an ACTIVE grant): a row matches when ALL hold —
21
+ * • `ctg_owning_tenant_uuid` = the resource's owning tenant AND `ctg_type_key` = the object
22
+ * type;
23
+ * • the grant is ACTIVE: `ctg_status = 'accepted'` (consent completed) AND
24
+ * `ctg_revoked_at IS NULL` (not revoked by either side) AND `ctg_expires_at_ms > ?` (not
25
+ * expired — bound epoch-ms, timezone-immune) — a revoked/offered/expired grant is NEVER
26
+ * returned, so it can never open access even for a heartbeat;
27
+ * • the grantee is EITHER this user (`ctg_grantee_kind='user'` AND
28
+ * `ctg_grantee_uuid = grantee`) OR one of the grantee's roles (`ctg_grantee_kind='role'`
29
+ * AND `ctg_grantee_uuid IN (roleIds)`);
30
+ * • the action is in the ceiling — the action-ceiling membership check is left to core's
31
+ * grant-evaluator (which re-checks `allowedActions`), but we DO gate scope: an
32
+ * `object_type` grant matches any record of the type; a `record` grant matches ONLY when
33
+ * the query names the exact `ctg_record_uuid` (a record-scoped row is EXCLUDED for an
34
+ * object-type / record-less query so a NULL-compare never spuriously matches).
35
+ * ≥1 matching row ⇒ mapped to a {@link CrossTenantGrant} (first match). No matching row ⇒
36
+ * `null` (a NORMAL data-absence — no active consent — NOT an error, NOT a lockout).
37
+ *
38
+ * ── SCOPE + RECORD-ID HANDLING: `object_type` rows are always eligible; a `record` row is
39
+ * eligible ONLY when `query.recordId` is present AND equals `ctg_record_uuid`. When the
40
+ * query has NO recordId (an object-type-level question), the SQL restricts to
41
+ * `ctg_scope = 'object_type'` so a record row's `ctg_record_uuid` is never compared to a
42
+ * bound NULL (which would never match — but we make the intent explicit and avoid the
43
+ * accidental all-records reach a `record_uuid = NULL` OR-branch could imply).
44
+ *
45
+ * ── ACTION CEILING PARSING: `ctg_allowed_actions` is a JSON column (a JSON array of action-key
46
+ * strings). ⚠️ The mariadb driver AUTO-PARSES a JSON column into a JS ARRAY on read, so the
47
+ * cell arrives already-parsed — `parseAllowedActions` accepts BOTH an already-parsed array
48
+ * (production) and a JSON string (test mocks). A malformed/empty value ⇒ `[]` (an empty
49
+ * ceiling permits nothing — fail-closed; core's ceiling check then denies).
50
+ *
51
+ * ── EXPIRY (MUST-FIX #1 — timezone immunity): `ctg_expires_at_ms` is a BIGINT epoch-ms column
52
+ * (NOT a TIMESTAMP). The active-grant filter compares it `> ?` against a BOUND integer (the
53
+ * adapter's `now()` epoch-ms), so it is immune to host-timezone / `NOW()` wall-clock skew (a
54
+ * TIMESTAMP + `NOW()` comparison silently kept expired grants alive on a non-UTC host). Mirrors
55
+ * the reset-token store's `prt_expires_at_ms` BIGINT precedent.
56
+ *
57
+ * ── EMPTY roleIds: guarded — an empty role list must NOT emit `IN ()` (a MariaDB syntax
58
+ * error). We omit the role branch entirely when there are no roles, so a role-less grantee
59
+ * is matched by its user-grant rows alone.
60
+ *
61
+ * ── ADR-0016 (transient-vs-deny): a genuine store outage THROWS
62
+ * {@link AuthorizationUnavailableError} (→ 503 retryable) — NEVER a silent `null` that
63
+ * would mask an outage as "no grant".
64
+ *
65
+ * ── SAFETY: every value (owningTenantId, typeKey, grantee, each roleId, recordId) is BOUND
66
+ * as a `?` param — NEVER interpolated. A SQL payload in typeKey/recordId is data, not SQL.
67
+ * Mirrors {@link MariaDbRecordShareAdapter}.
68
+ */
69
+ Object.defineProperty(exports, "__esModule", { value: true });
70
+ exports.MariaDbCrossTenantGrantAdapter = exports.SHIELD_CROSS_TENANT_GRANT_PORT = void 0;
71
+ const core_1 = require("@astram/core");
72
+ /** The DI token the adapter is bound under (parity with the share/rule-port tokens). */
73
+ exports.SHIELD_CROSS_TENANT_GRANT_PORT = Symbol('SHIELD_CROSS_TENANT_GRANT_PORT');
74
+ /** Schema the `cross_tenant_grant` table lives in (astram3 ships the migration). */
75
+ const DEFAULT_SCHEMA = 'shield_authz';
76
+ /**
77
+ * Parse `ctg_allowed_actions` to a `string[]`. ⚠️ The `ctg_allowed_actions` column is a JSON
78
+ * column, and the mariadb driver AUTO-PARSES it into a JS ARRAY on read — so `raw` is ALREADY an
79
+ * array in production. A test mock, however, may hand back a JSON STRING. This helper accepts
80
+ * BOTH:
81
+ * • already-parsed array ⇒ filter to strings, return directly (NO `JSON.parse` — parsing an
82
+ * array throws, which is exactly the bug that collapsed every ceiling to `[]`);
83
+ * • non-empty string ⇒ `JSON.parse`, then filter to strings;
84
+ * • anything else (null / empty / non-array-non-string / malformed) ⇒ `[]`.
85
+ * Non-string members are dropped (fail-closed — a hostile array can never widen the ceiling); an
86
+ * empty ceiling permits nothing.
87
+ */
88
+ function parseAllowedActions(raw) {
89
+ // Production path: the driver already parsed the JSON column into an array.
90
+ if (Array.isArray(raw)) {
91
+ return raw.filter((a) => typeof a === 'string');
92
+ }
93
+ // Mock / legacy path: a JSON string. Empty/null ⇒ empty ceiling.
94
+ if (typeof raw === 'string' && raw.length > 0) {
95
+ let parsed;
96
+ try {
97
+ parsed = JSON.parse(raw);
98
+ }
99
+ catch {
100
+ return [];
101
+ }
102
+ if (!Array.isArray(parsed))
103
+ return [];
104
+ return parsed.filter((a) => typeof a === 'string');
105
+ }
106
+ return [];
107
+ }
108
+ /** Coerce a BIGINT epoch-ms cell (number | bigint | numeric string | null) to a JS number. */
109
+ function toMs(value) {
110
+ if (value === null)
111
+ return 0;
112
+ const ms = typeof value === 'number' ? value : Number(value);
113
+ return Number.isFinite(ms) ? ms : 0;
114
+ }
115
+ /**
116
+ * The concrete cross-tenant grant adapter. Answers `findActiveGrant` from `cross_tenant_grant`,
117
+ * matching user- and role-granted, ACCEPTED, unexpired, unrevoked grants for the owning tenant
118
+ * + object type (+ record for record-scoped).
119
+ */
120
+ class MariaDbCrossTenantGrantAdapter {
121
+ db;
122
+ schema;
123
+ now;
124
+ constructor(db, opts = {}) {
125
+ this.db = db;
126
+ this.schema = opts.schema ?? DEFAULT_SCHEMA;
127
+ // The expiry filter binds THIS clock's epoch-ms against the BIGINT `ctg_expires_at_ms`
128
+ // column — timezone-immune (no Date/`NOW()` skew). Injectable for deterministic tests.
129
+ this.now = opts.nowMs ?? (() => Date.now());
130
+ }
131
+ async findActiveGrant(query) {
132
+ // Grantee predicate: the user grant OR (only when roles exist) a role grant. The role
133
+ // branch is OMITTED for an empty role list so we NEVER emit `IN ()` (a syntax error).
134
+ let granteeSql = `(ctg_grantee_kind = 'user' AND ctg_grantee_uuid = ?)`;
135
+ const granteeParams = [query.grantee];
136
+ if (query.roleIds.length > 0) {
137
+ const placeholders = query.roleIds.map(() => '?').join(', ');
138
+ granteeSql += ` OR (ctg_grantee_kind = 'role' AND ctg_grantee_uuid IN (${placeholders}))`;
139
+ granteeParams.push(...query.roleIds);
140
+ }
141
+ // Scope predicate. An `object_type` grant always matches this type; a `record` grant
142
+ // matches ONLY when the query names the exact record. When the query has NO recordId, we
143
+ // restrict to object_type grants (a record row's record_uuid must never be compared to a
144
+ // bound NULL — it would not match, but we make the intent explicit + never risk a NULL OR).
145
+ let scopeSql;
146
+ const scopeParams = [];
147
+ if (typeof query.recordId === 'string' && query.recordId.length > 0) {
148
+ scopeSql = `(ctg_scope = 'object_type' OR (ctg_scope = 'record' AND ctg_record_uuid = ?))`;
149
+ scopeParams.push(query.recordId);
150
+ }
151
+ else {
152
+ scopeSql = `ctg_scope = 'object_type'`;
153
+ }
154
+ // The expiry bound is a BIGINT epoch-ms comparison against a BOUND integer (this clock's
155
+ // now) — timezone-immune (no `NOW()`/Date wall-clock skew, MUST-FIX #1). Param order matches
156
+ // the SQL below exactly: owning_tenant, type_key, expiry_now, <grantee params>, <scope params>.
157
+ const nowMs = this.now();
158
+ const params = [
159
+ query.owningTenantId,
160
+ query.typeKey,
161
+ nowMs,
162
+ ...granteeParams,
163
+ ...scopeParams,
164
+ ];
165
+ let rows;
166
+ try {
167
+ rows = (await this.db.query(`SELECT ctg_scope, ctg_type_key, ctg_record_uuid, ctg_allowed_actions, ctg_expires_at_ms` +
168
+ ` FROM \`${this.schema}\`.\`cross_tenant_grant\`` +
169
+ ` WHERE ctg_owning_tenant_uuid = ?` +
170
+ ` AND ctg_type_key = ?` +
171
+ ` AND ctg_status = 'accepted'` +
172
+ ` AND ctg_revoked_at IS NULL` +
173
+ ` AND ctg_expires_at_ms > ?` +
174
+ ` AND (${granteeSql})` +
175
+ ` AND (${scopeSql})` +
176
+ // Prefer a record-scoped row over an object_type row when both match, and the
177
+ // latest-expiring first — deterministic single-match selection.
178
+ ` ORDER BY (ctg_scope = 'record') DESC, ctg_expires_at_ms DESC` +
179
+ ` LIMIT 1`, params));
180
+ }
181
+ catch (err) {
182
+ // Genuine store outage — fail closed as retryable (never mask as "no grant").
183
+ throw new core_1.AuthorizationUnavailableError(`cross_tenant_grant store unavailable for ` +
184
+ `${query.owningTenantId}:${query.typeKey}:${query.action}: ${err.message}`);
185
+ }
186
+ const row = rows[0];
187
+ if (!row) {
188
+ // No active consented grant — a NORMAL data-absence (NOT an error). Core reads this as
189
+ // "no cross-tenant permit" — deny-by-default for the reach.
190
+ return null;
191
+ }
192
+ // Map the row → CrossTenantGrant. Core's grant-evaluator re-checks allowedActions + scope
193
+ // + expiry; we still filtered status/expiry/revoke/scope in SQL so a revoked/expired/
194
+ // offered grant is never even returned.
195
+ const scope = row.ctg_scope === 'record' ? 'record' : 'object_type';
196
+ const grant = {
197
+ scope,
198
+ typeKey: row.ctg_type_key,
199
+ allowedActions: parseAllowedActions(row.ctg_allowed_actions),
200
+ // Already epoch-ms (BIGINT column) — just coerce the driver's number/string to a JS number.
201
+ expiresAtMs: toMs(row.ctg_expires_at_ms),
202
+ // `recordId` is only present for a record-scoped grant (exactOptionalPropertyTypes:
203
+ // never write an explicit `undefined` onto the optional field).
204
+ ...(scope === 'record' && row.ctg_record_uuid !== null
205
+ ? { recordId: row.ctg_record_uuid }
206
+ : {}),
207
+ };
208
+ return grant;
209
+ }
210
+ }
211
+ exports.MariaDbCrossTenantGrantAdapter = MariaDbCrossTenantGrantAdapter;
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mariadb-cross-tenant-grant-adapter.js","sourceRoot":"","sources":["../../src/authz/mariadb-cross-tenant-grant-adapter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,60 @@
1
+ /**
2
+ * `MariaDbRecordPermissionRuleAdapter` — the concrete {@link RecordPermissionRulePort}
3
+ * for `@astram/guard`, backed by the `record_permission_rule` table (design §5.1). It
4
+ * reads ADMIN rules ONLY and evaluates them for a single record+action, returning the
5
+ * `{ allow, explicitDeny }` verdict core's record-permit tier composes with the
6
+ * framework-owned owner BASELINE (design §5.2).
7
+ *
8
+ * ── ADMIN-ONLY BY CONSTRUCTION (not by a WHERE filter): the owner/system baselines are
9
+ * COMPUTED IN CORE (no rows), so this table is only ever WRITTEN admin-authored rules —
10
+ * there is no `provenance` column and every row here is an admin rule. ⚠️ If a future
11
+ * change EVER stores non-admin (e.g. `system`) rows in this table, this SELECT MUST add
12
+ * a `WHERE rpr_provenance = 'admin'` filter — otherwise a system row would be read as an
13
+ * admin rule and could conflict with core's immutable baseline.
14
+ *
15
+ * ── WHY HERE, NOT CORE (ADR-0003): the `mariadb` driver is a real runtime dependency;
16
+ * core defines only the PORT + the immutable owner baseline. This adapter is INJECTED
17
+ * into core's wired decision authority. It uses `@astram/core` TYPES only (erased).
18
+ *
19
+ * ── THE MODEL (design §5):
20
+ * • The OWNER baseline is CORE's job — it is NOT a row here. This table holds ONLY
21
+ * admin-authored rules that WIDEN the baseline (e.g. "Company Admins read all
22
+ * inspection_reports") or add explicit DENIES (deny-wins). There is no
23
+ * `owner`/`system` subject_kind row.
24
+ * • NO applicable admin rule ⇒ `{ allow: false, explicitDeny: false }` — a NORMAL
25
+ * data-absence (not a lockout): core's owner baseline still permits the owner. An
26
+ * empty table ⇒ owner-only access, exactly the safe default.
27
+ * • A matching `allow` rule ⇒ `allow: true`. A matching `deny` rule ⇒
28
+ * `explicitDeny: true` (deny-wins; core short-circuits the share on it).
29
+ *
30
+ * ── ADR-0016 (transient-vs-deny): a genuine store outage THROWS
31
+ * {@link AuthorizationUnavailableError} (→ 503 retryable) — NEVER a silent
32
+ * `{allow:false,explicitDeny:false}` that would mask an outage as a deny.
33
+ *
34
+ * ── SAFETY: every rule row is matched on `(tenant, type_key, action)` with all values
35
+ * BOUND as `?` params (no interpolation). The action match is exact OR the rule's
36
+ * wildcard `*`. Field conditions are evaluated in JS against the record attributes
37
+ * core supplies (design §11.1 builder shape) — the operator set is a fixed
38
+ * allow-list; an unknown operator makes the rule NOT match (fail-closed, never a
39
+ * spurious allow).
40
+ */
41
+ import type { RecordPermissionRulePort, RulePermit, RulePermitInput } from '@astram/core';
42
+ /** The DI token the adapter is bound under (parity with the session store token). */
43
+ export declare const SHIELD_RECORD_RULE_PORT: unique symbol;
44
+ /** The minimal query surface this adapter needs — a `mariadb` Pool satisfies it. */
45
+ export interface RuleQueryable {
46
+ query(sql: string, params?: readonly unknown[]): Promise<unknown>;
47
+ }
48
+ /**
49
+ * The concrete admin-rule adapter. Reads ADMIN rules for `(tenant, type_key, action|*)`
50
+ * and folds them into a `{ allow, explicitDeny }` verdict with deny-wins.
51
+ */
52
+ export declare class MariaDbRecordPermissionRuleAdapter implements RecordPermissionRulePort {
53
+ private readonly db;
54
+ private readonly schema;
55
+ constructor(db: RuleQueryable, opts?: {
56
+ schema?: string;
57
+ });
58
+ evaluate(input: RulePermitInput): Promise<RulePermit>;
59
+ }
60
+ //# sourceMappingURL=mariadb-record-rule-adapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mariadb-record-rule-adapter.d.ts","sourceRoot":"","sources":["../../src/authz/mariadb-record-rule-adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AAEH,OAAO,KAAK,EACV,wBAAwB,EACxB,UAAU,EACV,eAAe,EAChB,MAAM,cAAc,CAAC;AAGtB,qFAAqF;AACrF,eAAO,MAAM,uBAAuB,eAAoC,CAAC;AAKzE,oFAAoF;AACpF,MAAM,WAAW,aAAa;IAC5B,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,SAAS,OAAO,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACnE;AAsED;;;GAGG;AACH,qBAAa,kCAAmC,YAAW,wBAAwB;IACjF,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAgB;IACnC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;gBAEpB,EAAE,EAAE,aAAa,EAAE,IAAI,GAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAA;KAAO;IAKvD,QAAQ,CAAC,KAAK,EAAE,eAAe,GAAG,OAAO,CAAC,UAAU,CAAC;CAwC5D"}