@stratum-hq/db-adapters 1.3.0 → 1.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 (41) hide show
  1. package/README.md +59 -3
  2. package/dist/adapters/drizzle.d.ts +3 -2
  3. package/dist/adapters/drizzle.d.ts.map +1 -1
  4. package/dist/adapters/drizzle.js +3 -2
  5. package/dist/adapters/drizzle.js.map +1 -1
  6. package/dist/adapters/sequelize.d.ts +3 -2
  7. package/dist/adapters/sequelize.d.ts.map +1 -1
  8. package/dist/adapters/sequelize.js +3 -2
  9. package/dist/adapters/sequelize.js.map +1 -1
  10. package/dist/database/manager.d.ts +1 -1
  11. package/dist/database/manager.js +2 -2
  12. package/dist/database/manager.js.map +1 -1
  13. package/dist/database/pool-manager.d.ts +1 -1
  14. package/dist/database/pool-manager.d.ts.map +1 -1
  15. package/dist/index.d.ts +4 -0
  16. package/dist/index.d.ts.map +1 -1
  17. package/dist/index.js +7 -1
  18. package/dist/index.js.map +1 -1
  19. package/dist/migration-helpers.d.ts +2 -1
  20. package/dist/migration-helpers.d.ts.map +1 -1
  21. package/dist/migration-helpers.js +8 -1
  22. package/dist/migration-helpers.js.map +1 -1
  23. package/dist/pglite/index.d.ts +60 -0
  24. package/dist/pglite/index.d.ts.map +1 -0
  25. package/dist/pglite/index.js +196 -0
  26. package/dist/pglite/index.js.map +1 -0
  27. package/dist/rls/manager.d.ts +27 -1
  28. package/dist/rls/manager.d.ts.map +1 -1
  29. package/dist/rls/manager.js +43 -6
  30. package/dist/rls/manager.js.map +1 -1
  31. package/dist/rls/policy-check.d.ts +49 -1
  32. package/dist/rls/policy-check.d.ts.map +1 -1
  33. package/dist/rls/policy-check.js +136 -11
  34. package/dist/rls/policy-check.js.map +1 -1
  35. package/dist/rls/session.d.ts +34 -2
  36. package/dist/rls/session.d.ts.map +1 -1
  37. package/dist/rls/session.js +39 -5
  38. package/dist/rls/session.js.map +1 -1
  39. package/dist/schema/manager.js +1 -1
  40. package/dist/schema/manager.js.map +1 -1
  41. package/package.json +24 -6
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/pglite/index.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,31 @@
1
1
  import pg from "pg";
2
- export declare function createPolicy(client: pg.PoolClient, tableName: string): Promise<void>;
2
+ /**
3
+ * The read-only subtree predicate of migration 031. It admits the rows of the
4
+ * current tenant's descendants only when the session set app.tenant_scope to
5
+ * 'subtree'. It goes in a FOR SELECT policy only, so writes stay exact.
6
+ */
7
+ export declare const SUBTREE_READ_FILTER: string;
8
+ export interface CreatePolicyOptions {
9
+ /**
10
+ * Also create the tenant_subtree_read policy, so that a session in the
11
+ * "subtree" scope reads the rows of the tenant's descendants. Needs
12
+ * migration 031 of @stratum-hq/lib. Default false.
13
+ */
14
+ subtreeRead?: boolean;
15
+ /**
16
+ * The control role of migration 032 (default `stratum_control`). An
17
+ * existing stratum_control_plane policy is accepted only when it applies to
18
+ * exactly this role.
19
+ */
20
+ controlRole?: string;
21
+ }
22
+ /**
23
+ * Throws when the database has no stratum_subtree_tenant_ids() function. The
24
+ * subtree read policy calls it, and without it CREATE POLICY fails with a
25
+ * less clear error.
26
+ */
27
+ export declare function assertSubtreeFunction(client: pg.PoolClient): Promise<void>;
28
+ export declare function createPolicy(client: pg.PoolClient, tableName: string, options?: CreatePolicyOptions): Promise<void>;
3
29
  export declare function dropPolicy(client: pg.PoolClient, tableName: string): Promise<void>;
4
30
  export declare function enableRLS(client: pg.PoolClient, tableName: string): Promise<void>;
5
31
  export declare function disableRLS(client: pg.PoolClient, tableName: string): Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../../src/rls/manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC;AAapB,wBAAsB,YAAY,CAChC,MAAM,EAAE,EAAE,CAAC,UAAU,EACrB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,IAAI,CAAC,CA0Bf;AAED,wBAAsB,UAAU,CAC9B,MAAM,EAAE,EAAE,CAAC,UAAU,EACrB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,IAAI,CAAC,CAGf;AAED,wBAAsB,SAAS,CAC7B,MAAM,EAAE,EAAE,CAAC,UAAU,EACrB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,IAAI,CAAC,CAIf;AAED,wBAAsB,UAAU,CAC9B,MAAM,EAAE,EAAE,CAAC,UAAU,EACrB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,IAAI,CAAC,CAIf;AAED,wBAAsB,YAAY,CAChC,MAAM,EAAE,EAAE,CAAC,UAAU,EACrB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,OAAO,CAAC,CAWlB"}
1
+ {"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../../src/rls/manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC;AAKpB;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,QAEuC,CAAC;AAExE,MAAM,WAAW,mBAAmB;IAClC;;;;OAIG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;;GAIG;AACH,wBAAsB,qBAAqB,CAAC,MAAM,EAAE,EAAE,CAAC,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAUhF;AAUD,wBAAsB,YAAY,CAChC,MAAM,EAAE,EAAE,CAAC,UAAU,EACrB,SAAS,EAAE,MAAM,EACjB,OAAO,GAAE,mBAAwB,GAChC,OAAO,CAAC,IAAI,CAAC,CA+Cf;AAED,wBAAsB,UAAU,CAC9B,MAAM,EAAE,EAAE,CAAC,UAAU,EACrB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,IAAI,CAAC,CAIf;AAED,wBAAsB,SAAS,CAC7B,MAAM,EAAE,EAAE,CAAC,UAAU,EACrB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,IAAI,CAAC,CAIf;AAED,wBAAsB,UAAU,CAC9B,MAAM,EAAE,EAAE,CAAC,UAAU,EACrB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,IAAI,CAAC,CAIf;AAED,wBAAsB,YAAY,CAChC,MAAM,EAAE,EAAE,CAAC,UAAU,EACrB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,OAAO,CAAC,CAWlB"}
@@ -1,5 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SUBTREE_READ_FILTER = void 0;
4
+ exports.assertSubtreeFunction = assertSubtreeFunction;
3
5
  exports.createPolicy = createPolicy;
4
6
  exports.dropPolicy = dropPolicy;
5
7
  exports.enableRLS = enableRLS;
@@ -7,6 +9,25 @@ exports.disableRLS = disableRLS;
7
9
  exports.isRLSEnabled = isRLSEnabled;
8
10
  const policy_check_js_1 = require("./policy-check.js");
9
11
  const TENANT_FILTER = "tenant_id = NULLIF(current_setting('app.current_tenant_id', true), '')::uuid";
12
+ /**
13
+ * The read-only subtree predicate of migration 031. It admits the rows of the
14
+ * current tenant's descendants only when the session set app.tenant_scope to
15
+ * 'subtree'. It goes in a FOR SELECT policy only, so writes stay exact.
16
+ */
17
+ exports.SUBTREE_READ_FILTER = "current_setting('app.tenant_scope', true) = 'subtree' " +
18
+ "AND tenant_id = ANY ((SELECT stratum_subtree_tenant_ids())::uuid[])";
19
+ /**
20
+ * Throws when the database has no stratum_subtree_tenant_ids() function. The
21
+ * subtree read policy calls it, and without it CREATE POLICY fails with a
22
+ * less clear error.
23
+ */
24
+ async function assertSubtreeFunction(client) {
25
+ const res = await client.query("SELECT to_regprocedure('stratum_subtree_tenant_ids()') IS NOT NULL AS present");
26
+ if (!res.rows[0]?.present) {
27
+ throw new Error("[stratum] The subtree read policy needs the function stratum_subtree_tenant_ids(), " +
28
+ "which migration 031 of @stratum-hq/lib creates. Run the Stratum migrations first.");
29
+ }
30
+ }
10
31
  // Validate table name to prevent SQL injection (only allows alphanumeric + underscores)
11
32
  function validateTableName(tableName) {
12
33
  if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(tableName)) {
@@ -14,31 +35,47 @@ function validateTableName(tableName) {
14
35
  }
15
36
  return tableName;
16
37
  }
17
- async function createPolicy(client, tableName) {
38
+ async function createPolicy(client, tableName, options = {}) {
18
39
  const safe = validateTableName(tableName);
40
+ if (options.subtreeRead) {
41
+ await assertSubtreeFunction(client);
42
+ }
19
43
  // Read every policy on the table that the name resolves to, in whichever
20
- // schema that is. PostgreSQL ORs permissive policies together, so each one
44
+ // schema that is. The caller's search path stays as it is (this may run in
45
+ // the caller's transaction), so every comparison here has an exact
46
+ // pg_catalog operator: the regclass lookups are cast to oid. PostgreSQL ORs permissive policies together, so each one
21
47
  // must filter by tenant, and a policy's name proves nothing.
22
- const existing = await client.query(`SELECT p.policyname, p.permissive, p.cmd, p.qual, p.with_check, p.roles::text[] AS roles
48
+ // Each row also carries the schema of the tenants table, the only schema
49
+ // that may qualify the subtree function (migration 031 creates it there).
50
+ const existing = await client.query(`SELECT p.policyname, p.permissive, p.cmd, p.qual, p.with_check, p.roles::text[] AS roles,
51
+ (SELECT tn.nspname FROM pg_class tc JOIN pg_namespace tn ON tn.oid = tc.relnamespace
52
+ WHERE tc.oid = pg_catalog.to_regclass('tenants')::pg_catalog.oid) AS tenants_schema
23
53
  FROM pg_policies p
24
54
  JOIN pg_class c ON c.relname = p.tablename
25
55
  JOIN pg_namespace n ON n.oid = c.relnamespace AND n.nspname = p.schemaname
26
- WHERE c.oid = to_regclass($1)`, [safe]);
27
- const issues = (0, policy_check_js_1.tablePolicyIssues)(existing.rows);
56
+ WHERE c.oid = pg_catalog.to_regclass($1::pg_catalog.text)::pg_catalog.oid`, [safe]);
57
+ const issues = (0, policy_check_js_1.tablePolicyIssues)(existing.rows, existing.rows[0]?.tenants_schema ?? undefined, options.controlRole);
28
58
  if (issues.length > 0) {
29
59
  throw new Error(`[stratum] Table ${safe} has row-level security policies that do not isolate it by tenant: ` +
30
60
  `${issues.join("; ")} (expected ${TENANT_FILTER}). ` +
31
61
  `Drop or correct those policies, then call createPolicy again.`);
32
62
  }
63
+ for (const warning of (0, policy_check_js_1.tablePolicyWarnings)(existing.rows)) {
64
+ process.emitWarning(`[stratum] Table ${safe}: ${warning}`, { code: "STRATUM_GUC_BYPASS_POLICY" });
65
+ }
33
66
  if (!existing.rows.some((p) => p.policyname === "tenant_isolation")) {
34
67
  // Cannot use parameterized queries inside DO blocks or for DDL identifiers.
35
68
  // Table name is validated via allowlist regex above.
36
69
  await client.query(`CREATE POLICY tenant_isolation ON ${safe} USING (${TENANT_FILTER})`);
37
70
  }
71
+ if (options.subtreeRead && !existing.rows.some((p) => p.policyname === "tenant_subtree_read")) {
72
+ await client.query(`CREATE POLICY tenant_subtree_read ON ${safe} FOR SELECT USING (${exports.SUBTREE_READ_FILTER})`);
73
+ }
38
74
  }
39
75
  async function dropPolicy(client, tableName) {
40
76
  const safe = validateTableName(tableName);
41
77
  await client.query(`DROP POLICY IF EXISTS tenant_isolation ON ${safe}`);
78
+ await client.query(`DROP POLICY IF EXISTS tenant_subtree_read ON ${safe}`);
42
79
  }
43
80
  async function enableRLS(client, tableName) {
44
81
  const safe = validateTableName(tableName);
@@ -53,7 +90,7 @@ async function disableRLS(client, tableName) {
53
90
  async function isRLSEnabled(client, tableName) {
54
91
  const safe = validateTableName(tableName);
55
92
  // The table the name resolves to, in whichever schema that is.
56
- const res = await client.query(`SELECT relrowsecurity FROM pg_class WHERE oid = to_regclass($1)`, [safe]);
93
+ const res = await client.query(`SELECT relrowsecurity FROM pg_catalog.pg_class WHERE oid = pg_catalog.to_regclass($1::pg_catalog.text)::pg_catalog.oid`, [safe]);
57
94
  if (res.rows.length === 0) {
58
95
  return false;
59
96
  }
@@ -1 +1 @@
1
- {"version":3,"file":"manager.js","sourceRoot":"","sources":["../../src/rls/manager.ts"],"names":[],"mappings":";;AAaA,oCA6BC;AAED,gCAMC;AAED,8BAOC;AAED,gCAOC;AAED,oCAcC;AAnFD,uDAAsE;AAEtE,MAAM,aAAa,GAAG,8EAA8E,CAAC;AAErG,wFAAwF;AACxF,SAAS,iBAAiB,CAAC,SAAiB;IAC1C,IAAI,CAAC,0BAA0B,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC;QAChD,MAAM,IAAI,KAAK,CAAC,uBAAuB,SAAS,EAAE,CAAC,CAAC;IACtD,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAEM,KAAK,UAAU,YAAY,CAChC,MAAqB,EACrB,SAAiB;IAEjB,MAAM,IAAI,GAAG,iBAAiB,CAAC,SAAS,CAAC,CAAC;IAC1C,yEAAyE;IACzE,2EAA2E;IAC3E,6DAA6D;IAC7D,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,KAAK,CACjC;;;;oCAIgC,EAChC,CAAC,IAAI,CAAC,CACP,CAAC;IACF,MAAM,MAAM,GAAG,IAAA,mCAAiB,EAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;IAChD,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACtB,MAAM,IAAI,KAAK,CACb,mBAAmB,IAAI,qEAAqE;YAC1F,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,cAAc,aAAa,KAAK;YACpD,+DAA+D,CAClE,CAAC;IACJ,CAAC;IACD,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,KAAK,kBAAkB,CAAC,EAAE,CAAC;QACpE,4EAA4E;QAC5E,qDAAqD;QACrD,MAAM,MAAM,CAAC,KAAK,CAAC,qCAAqC,IAAI,WAAW,aAAa,GAAG,CAAC,CAAC;IAC3F,CAAC;AACH,CAAC;AAEM,KAAK,UAAU,UAAU,CAC9B,MAAqB,EACrB,SAAiB;IAEjB,MAAM,IAAI,GAAG,iBAAiB,CAAC,SAAS,CAAC,CAAC;IAC1C,MAAM,MAAM,CAAC,KAAK,CAAC,6CAA6C,IAAI,EAAE,CAAC,CAAC;AAC1E,CAAC;AAEM,KAAK,UAAU,SAAS,CAC7B,MAAqB,EACrB,SAAiB;IAEjB,MAAM,IAAI,GAAG,iBAAiB,CAAC,SAAS,CAAC,CAAC;IAC1C,MAAM,MAAM,CAAC,KAAK,CAAC,eAAe,IAAI,4BAA4B,CAAC,CAAC;IACpE,MAAM,MAAM,CAAC,KAAK,CAAC,eAAe,IAAI,2BAA2B,CAAC,CAAC;AACrE,CAAC;AAEM,KAAK,UAAU,UAAU,CAC9B,MAAqB,EACrB,SAAiB;IAEjB,MAAM,IAAI,GAAG,iBAAiB,CAAC,SAAS,CAAC,CAAC;IAC1C,MAAM,MAAM,CAAC,KAAK,CAAC,eAAe,IAAI,8BAA8B,CAAC,CAAC;IACtE,MAAM,MAAM,CAAC,KAAK,CAAC,eAAe,IAAI,6BAA6B,CAAC,CAAC;AACvE,CAAC;AAEM,KAAK,UAAU,YAAY,CAChC,MAAqB,EACrB,SAAiB;IAEjB,MAAM,IAAI,GAAG,iBAAiB,CAAC,SAAS,CAAC,CAAC;IAC1C,+DAA+D;IAC/D,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,KAAK,CAC5B,iEAAiE,EACjE,CAAC,IAAI,CAAC,CACP,CAAC;IACF,IAAI,GAAG,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1B,OAAO,KAAK,CAAC;IACf,CAAC;IACD,OAAO,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC;AACpC,CAAC"}
1
+ {"version":3,"file":"manager.js","sourceRoot":"","sources":["../../src/rls/manager.ts"],"names":[],"mappings":"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"}
@@ -22,6 +22,21 @@
22
22
  * either order, with casts, inside an AND with other conditions, and ORed with
23
23
  * Stratum's administrative bypass. Anything else is reported as not filtering
24
24
  * by tenant, so an unusual but correct policy is refused, never accepted.
25
+ *
26
+ * It recognises the legacy bypass of migration 032, `(SELECT
27
+ * stratum_legacy_bypass()) OR <tenant match>`, like the older direct check of
28
+ * app.bypass_rls. A policy named stratum_control_plane counts as isolating
29
+ * only when it applies to exactly the control role (default stratum_control):
30
+ * that role is the library's own, and a session cannot join it by setting
31
+ * anything. tablePolicyWarnings flags every policy that still checks
32
+ * app.bypass_rls directly; 2.0 will refuse those.
33
+ *
34
+ * It also recognises the subtree read of migration 031: the
35
+ * stratum_subtree_tenant_ids() match ANDed with the app.tenant_scope =
36
+ * 'subtree' check. That form counts only in the USING clause of a SELECT
37
+ * policy. In any other policy, the same clause would let UPDATE or DELETE
38
+ * reach a descendant's rows, and in WITH CHECK it would let a write name a
39
+ * descendant.
25
40
  */
26
41
  /** One row of pg_policies. */
27
42
  export interface PolicyRow {
@@ -35,9 +50,42 @@ export interface PolicyRow {
35
50
  /** The roles the policy applies to; ["public"] for PUBLIC. */
36
51
  roles: string[];
37
52
  }
53
+ /** The default control role of migration 032. */
54
+ export declare const DEFAULT_CONTROL_ROLE = "stratum_control";
55
+ /**
56
+ * Why one permissive policy lets rows of other tenants through, or null when
57
+ * it filters by tenant for every command it covers. Shared with the policy
58
+ * checks of `@stratum-hq/cli`, so both apply the same expression rules.
59
+ *
60
+ * @param functionSchema - As for {@link tablePolicyIssues}.
61
+ */
62
+ export declare function permissivePolicyIssue(p: Pick<PolicyRow, "policyname" | "cmd" | "qual" | "with_check">, functionSchema?: string): string | null;
63
+ /**
64
+ * True for the stratum_control_plane policy of migration 032 when it is
65
+ * permissive and applies to exactly `controlRole`. Only that role's members
66
+ * can use it, whatever it admits.
67
+ */
68
+ export declare function isControlPlanePolicy(p: Pick<PolicyRow, "policyname" | "permissive"> & {
69
+ roles?: string[];
70
+ }, controlRole?: string): boolean;
38
71
  /**
39
72
  * Every reason the table's policies do not isolate it by tenant. Empty when
40
73
  * they do, or when the table has no policies.
74
+ *
75
+ * @param functionSchema - The schema of Stratum's tenants table, the only
76
+ * schema that may qualify stratum_subtree_tenant_ids() and
77
+ * stratum_legacy_bypass(). When it is undefined, only the unqualified
78
+ * functions count.
79
+ * @param controlRole - The control role of migration 032. A policy named
80
+ * stratum_control_plane that applies to exactly this role is accepted.
81
+ */
82
+ export declare function tablePolicyIssues(policies: PolicyRow[], functionSchema?: string, controlRole?: string): string[];
83
+ /**
84
+ * The policies that admit the app.bypass_rls setting directly. A session that
85
+ * can run SQL can set it, so such a policy holds only against a client that
86
+ * never sets it. The legacy form of migration 032 is not flagged: it admits
87
+ * the setting only while the stratum_security switch is on, which only the
88
+ * control role can change.
41
89
  */
42
- export declare function tablePolicyIssues(policies: PolicyRow[]): string[];
90
+ export declare function tablePolicyWarnings(policies: PolicyRow[]): string[];
43
91
  //# sourceMappingURL=policy-check.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"policy-check.d.ts","sourceRoot":"","sources":["../../src/rls/policy-check.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,8BAA8B;AAC9B,MAAM,WAAW,SAAS;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,qCAAqC;IACrC,UAAU,EAAE,MAAM,CAAC;IACnB,uDAAuD;IACvD,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,8DAA8D;IAC9D,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AA0JD;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,SAAS,EAAE,GAAG,MAAM,EAAE,CAgBjE"}
1
+ {"version":3,"file":"policy-check.d.ts","sourceRoot":"","sources":["../../src/rls/policy-check.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AAEH,8BAA8B;AAC9B,MAAM,WAAW,SAAS;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,qCAAqC;IACrC,UAAU,EAAE,MAAM,CAAC;IACnB,uDAAuD;IACvD,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,8DAA8D;IAC9D,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AAcD,iDAAiD;AACjD,eAAO,MAAM,oBAAoB,oBAAoB,CAAC;AA4LtD;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CACnC,CAAC,EAAE,IAAI,CAAC,SAAS,EAAE,YAAY,GAAG,KAAK,GAAG,MAAM,GAAG,YAAY,CAAC,EAChE,cAAc,CAAC,EAAE,MAAM,GACtB,MAAM,GAAG,IAAI,CAEf;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAClC,CAAC,EAAE,IAAI,CAAC,SAAS,EAAE,YAAY,GAAG,YAAY,CAAC,GAAG;IAAE,KAAK,CAAC,EAAE,MAAM,EAAE,CAAA;CAAE,EACtE,WAAW,GAAE,MAA6B,GACzC,OAAO,CAOT;AAiBD;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,SAAS,EAAE,EACrB,cAAc,CAAC,EAAE,MAAM,EACvB,WAAW,GAAE,MAA6B,GACzC,MAAM,EAAE,CAsBV;AAID;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,SAAS,EAAE,GAAG,MAAM,EAAE,CAanE"}
@@ -23,13 +23,42 @@
23
23
  * either order, with casts, inside an AND with other conditions, and ORed with
24
24
  * Stratum's administrative bypass. Anything else is reported as not filtering
25
25
  * by tenant, so an unusual but correct policy is refused, never accepted.
26
+ *
27
+ * It recognises the legacy bypass of migration 032, `(SELECT
28
+ * stratum_legacy_bypass()) OR <tenant match>`, like the older direct check of
29
+ * app.bypass_rls. A policy named stratum_control_plane counts as isolating
30
+ * only when it applies to exactly the control role (default stratum_control):
31
+ * that role is the library's own, and a session cannot join it by setting
32
+ * anything. tablePolicyWarnings flags every policy that still checks
33
+ * app.bypass_rls directly; 2.0 will refuse those.
34
+ *
35
+ * It also recognises the subtree read of migration 031: the
36
+ * stratum_subtree_tenant_ids() match ANDed with the app.tenant_scope =
37
+ * 'subtree' check. That form counts only in the USING clause of a SELECT
38
+ * policy. In any other policy, the same clause would let UPDATE or DELETE
39
+ * reach a descendant's rows, and in WITH CHECK it would let a write name a
40
+ * descendant.
26
41
  */
27
42
  Object.defineProperty(exports, "__esModule", { value: true });
43
+ exports.DEFAULT_CONTROL_ROLE = void 0;
44
+ exports.permissivePolicyIssue = permissivePolicyIssue;
45
+ exports.isControlPlanePolicy = isControlPlanePolicy;
28
46
  exports.tablePolicyIssues = tablePolicyIssues;
47
+ exports.tablePolicyWarnings = tablePolicyWarnings;
29
48
  /** The session setting that holds the current tenant (see rls/session.ts). */
30
49
  const TENANT_SETTING = "app.current_tenant_id";
31
50
  /** The setting Stratum's own policies admit for administrative access. */
32
51
  const BYPASS_SETTING = "app.bypass_rls";
52
+ /** The setting that opts a session in to the subtree read scope (migration 031). */
53
+ const SCOPE_SETTING = "app.tenant_scope";
54
+ /** The function that returns the current tenant and its descendants (migration 031). */
55
+ const SUBTREE_FUNCTION = "stratum_subtree_tenant_ids";
56
+ /** The function of migration 032 that gates the legacy bypass behind stratum_security. */
57
+ const LEGACY_FUNCTION = "stratum_legacy_bypass";
58
+ /** The policy of migration 032 that admits the control role. */
59
+ const CONTROL_POLICY = "stratum_control_plane";
60
+ /** The default control role of migration 032. */
61
+ exports.DEFAULT_CONTROL_ROLE = "stratum_control";
33
62
  /** Drops whitespace and casts, lower-cases, and keeps string literals intact. */
34
63
  function normalize(expr) {
35
64
  let out = "";
@@ -118,18 +147,58 @@ const SETTING_READ = `current_setting'${TENANT_SETTING.replace(".", "\\.")}'(?:,
118
147
  const CURRENT_TENANT = `(?:nullif${SETTING_READ},''|${SETTING_READ})`;
119
148
  const TENANT_MATCH = new RegExp(`^(?:tenant_id=${CURRENT_TENANT}|${CURRENT_TENANT}=tenant_id)$`);
120
149
  const BYPASS_MATCH = new RegExp(`^current_setting'${BYPASS_SETTING.replace(".", "\\.")}'(?:,(?:true|false))?='on'$`);
121
- /** True when the normalized expression only admits rows of the current tenant. */
122
- function filtersByTenant(expr) {
150
+ const SCOPE_MATCH = new RegExp(`^current_setting'${SCOPE_SETTING.replace(".", "\\.")}'(?:,(?:true|false))?='subtree'$`);
151
+ /**
152
+ * tenant_id = ANY ((SELECT stratum_subtree_tenant_ids())::uuid[]) after
153
+ * normalize and flatten. PostgreSQL qualifies the function with its schema
154
+ * when that schema is not on the search path of the reading session. Only the
155
+ * schema that holds Stratum's tenants table, where migration 031 creates the
156
+ * function, may qualify it: a function of the same name in another schema
157
+ * could return any ids.
158
+ */
159
+ function subtreeMatch(functionSchema) {
160
+ const qualifier = functionSchema === undefined ? "" : `(?:${escapeRegExp(quoteIdent(functionSchema).toLowerCase())}\\.)?`;
161
+ return new RegExp(`^tenant_id=anyselect${qualifier}${SUBTREE_FUNCTION}(?:as${SUBTREE_FUNCTION})?$`);
162
+ }
163
+ /**
164
+ * (SELECT stratum_legacy_bypass()) after normalize and flatten, with the same
165
+ * rule for a schema qualifier as the subtree function.
166
+ */
167
+ function legacyMatch(functionSchema) {
168
+ const qualifier = functionSchema === undefined ? "" : `(?:${escapeRegExp(quoteIdent(functionSchema).toLowerCase())}\\.)?`;
169
+ return new RegExp(`^select${qualifier}${LEGACY_FUNCTION}(?:as${LEGACY_FUNCTION})?$`);
170
+ }
171
+ function escapeRegExp(text) {
172
+ return text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
173
+ }
174
+ /** The identifier as PostgreSQL prints it: quoted unless it is a plain lowercase name. */
175
+ function quoteIdent(name) {
176
+ return /^[a-z_][a-z0-9_$]*$/.test(name) ? name : `"${name.replace(/"/g, '""')}"`;
177
+ }
178
+ /**
179
+ * True when the normalized expression only admits rows of the current tenant.
180
+ * `subtree` matches the subtree function call that may appear in the
181
+ * expression, or is null where the subtree read is not allowed.
182
+ */
183
+ function filtersByTenant(expr, subtree, legacy) {
123
184
  const e = stripOuterParens(expr);
124
185
  const disjuncts = splitTopLevel(e, "or");
125
186
  if (disjuncts.length > 1) {
126
- // Every branch must be safe; Stratum's administrative bypass is one of them.
127
- return (disjuncts.every((d) => BYPASS_MATCH.test(flatten(d)) || filtersByTenant(d)) &&
128
- disjuncts.some((d) => !BYPASS_MATCH.test(flatten(d))));
187
+ // Every branch must be safe; Stratum's administrative bypass, direct or
188
+ // through the legacy function of migration 032, is one of them.
189
+ const isBypass = (d) => BYPASS_MATCH.test(flatten(d)) || legacy.test(flatten(d));
190
+ return (disjuncts.every((d) => isBypass(d) || filtersByTenant(d, subtree, legacy)) &&
191
+ disjuncts.some((d) => !isBypass(d)));
129
192
  }
130
193
  const conjuncts = splitTopLevel(e, "and");
131
194
  if (conjuncts.length > 1) {
132
- return conjuncts.some((c) => filtersByTenant(c));
195
+ if (conjuncts.some((c) => filtersByTenant(c, subtree, legacy)))
196
+ return true;
197
+ // The subtree read of migration 031: the descendants of the current
198
+ // tenant, only when the session opted in with app.tenant_scope.
199
+ return (subtree !== null &&
200
+ conjuncts.some((c) => SCOPE_MATCH.test(flatten(c))) &&
201
+ conjuncts.some((c) => subtree.test(flatten(c))));
133
202
  }
134
203
  return TENANT_MATCH.test(flatten(e));
135
204
  }
@@ -138,22 +207,45 @@ function oneLine(expr) {
138
207
  return expr === null ? "none" : expr.replace(/\s+/g, " ");
139
208
  }
140
209
  /** Why one permissive policy lets rows of other tenants through, or null. */
141
- function permissiveIssue(p) {
210
+ function permissiveIssue(p, subtree, legacy) {
142
211
  const name = `policy "${oneLine(p.policyname)}" (${p.cmd})`;
143
212
  // INSERT policies have only WITH CHECK; SELECT and DELETE only USING. For
144
213
  // ALL and UPDATE a missing WITH CHECK means PostgreSQL reuses USING.
145
214
  if (p.cmd !== "INSERT") {
146
- if (p.qual === null || !filtersByTenant(normalize(p.qual))) {
215
+ // Only a SELECT policy may widen to the subtree: in any other policy the
216
+ // same USING clause would let UPDATE or DELETE reach a descendant's rows.
217
+ if (p.qual === null || !filtersByTenant(normalize(p.qual), p.cmd === "SELECT" ? subtree : null, legacy)) {
147
218
  return `${name} USING (${oneLine(p.qual)}) does not filter by tenant`;
148
219
  }
149
220
  }
150
221
  if (p.cmd === "INSERT" || p.with_check !== null) {
151
- if (p.with_check === null || !filtersByTenant(normalize(p.with_check))) {
222
+ if (p.with_check === null || !filtersByTenant(normalize(p.with_check), null, legacy)) {
152
223
  return `${name} WITH CHECK (${oneLine(p.with_check)}) does not filter by tenant`;
153
224
  }
154
225
  }
155
226
  return null;
156
227
  }
228
+ /**
229
+ * Why one permissive policy lets rows of other tenants through, or null when
230
+ * it filters by tenant for every command it covers. Shared with the policy
231
+ * checks of `@stratum-hq/cli`, so both apply the same expression rules.
232
+ *
233
+ * @param functionSchema - As for {@link tablePolicyIssues}.
234
+ */
235
+ function permissivePolicyIssue(p, functionSchema) {
236
+ return permissiveIssue(p, subtreeMatch(functionSchema), legacyMatch(functionSchema));
237
+ }
238
+ /**
239
+ * True for the stratum_control_plane policy of migration 032 when it is
240
+ * permissive and applies to exactly `controlRole`. Only that role's members
241
+ * can use it, whatever it admits.
242
+ */
243
+ function isControlPlanePolicy(p, controlRole = exports.DEFAULT_CONTROL_ROLE) {
244
+ return (p.policyname === CONTROL_POLICY &&
245
+ p.permissive === "PERMISSIVE" &&
246
+ p.roles?.length === 1 &&
247
+ p.roles[0] === controlRole);
248
+ }
157
249
  /** Why the tenant_isolation policy is not the shape createPolicy generates, or null. */
158
250
  function tenantIsolationShapeIssue(p) {
159
251
  const name = `policy "${oneLine(p.policyname)}"`;
@@ -171,10 +263,23 @@ function tenantIsolationShapeIssue(p) {
171
263
  /**
172
264
  * Every reason the table's policies do not isolate it by tenant. Empty when
173
265
  * they do, or when the table has no policies.
266
+ *
267
+ * @param functionSchema - The schema of Stratum's tenants table, the only
268
+ * schema that may qualify stratum_subtree_tenant_ids() and
269
+ * stratum_legacy_bypass(). When it is undefined, only the unqualified
270
+ * functions count.
271
+ * @param controlRole - The control role of migration 032. A policy named
272
+ * stratum_control_plane that applies to exactly this role is accepted.
174
273
  */
175
- function tablePolicyIssues(policies) {
274
+ function tablePolicyIssues(policies, functionSchema, controlRole = exports.DEFAULT_CONTROL_ROLE) {
275
+ const subtree = subtreeMatch(functionSchema);
276
+ const legacy = legacyMatch(functionSchema);
176
277
  const issues = [];
177
278
  for (const p of policies) {
279
+ if (isControlPlanePolicy(p, controlRole)) {
280
+ // Only the library's own role can use it, whatever it admits.
281
+ continue;
282
+ }
178
283
  if (p.policyname === "tenant_isolation") {
179
284
  const shape = tenantIsolationShapeIssue(p);
180
285
  if (shape !== null) {
@@ -183,11 +288,31 @@ function tablePolicyIssues(policies) {
183
288
  }
184
289
  }
185
290
  if (p.permissive === "PERMISSIVE") {
186
- const issue = permissiveIssue(p);
291
+ const issue = permissiveIssue(p, subtree, legacy);
187
292
  if (issue !== null)
188
293
  issues.push(issue);
189
294
  }
190
295
  }
191
296
  return issues;
192
297
  }
298
+ const DIRECT_BYPASS = `current_setting'${BYPASS_SETTING.replace(".", "\\.")}'`;
299
+ /**
300
+ * The policies that admit the app.bypass_rls setting directly. A session that
301
+ * can run SQL can set it, so such a policy holds only against a client that
302
+ * never sets it. The legacy form of migration 032 is not flagged: it admits
303
+ * the setting only while the stratum_security switch is on, which only the
304
+ * control role can change.
305
+ */
306
+ function tablePolicyWarnings(policies) {
307
+ const direct = new RegExp(DIRECT_BYPASS);
308
+ const warnings = [];
309
+ for (const p of policies) {
310
+ const exprs = [p.qual, p.with_check].filter((e) => e !== null);
311
+ if (exprs.some((e) => direct.test(flatten(normalize(e))))) {
312
+ warnings.push(`policy "${oneLine(p.policyname)}" admits app.bypass_rls directly. ` +
313
+ `Use the control role of migration 032, which PostgreSQL role membership decides, instead.`);
314
+ }
315
+ }
316
+ return warnings;
317
+ }
193
318
  //# sourceMappingURL=policy-check.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"policy-check.js","sourceRoot":"","sources":["../../src/rls/policy-check.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"policy-check.js","sourceRoot":"","sources":["../../src/rls/policy-check.ts"],"names":[],"mappings":"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"}
@@ -1,8 +1,40 @@
1
1
  import pg from "pg";
2
- export declare function setTenantContext(client: pg.PoolClient, tenantId: string): Promise<void>;
2
+ /**
3
+ * Which rows a tenant context can read.
4
+ *
5
+ * - "exact": the rows of the tenant only. This is the default.
6
+ * - "subtree": the rows of the tenant and of every descendant, through the
7
+ * tenant_subtree_read policies of migration 031. Writes stay limited to the
8
+ * exact tenant in both scopes, and so do reads of credential-bearing rows
9
+ * (api_keys, webhooks and sensitive config_entries).
10
+ */
11
+ export type TenantScope = "exact" | "subtree";
12
+ export interface TenantContextOptions {
13
+ /** The read scope. Default "exact". */
14
+ scope?: TenantScope;
15
+ }
16
+ /**
17
+ * Sets the tenant and the read scope for the current transaction.
18
+ *
19
+ * It always writes both settings, so a second call in the same transaction
20
+ * replaces an earlier "subtree" scope instead of keeping it.
21
+ */
22
+ export declare function setTenantContext(client: pg.PoolClient, tenantId: string, options?: TenantContextOptions): Promise<void>;
3
23
  export declare function resetTenantContext(client: pg.PoolClient): Promise<void>;
4
- export declare function withTenantContext<T>(pool: pg.Pool, tenantId: string, fn: (client: pg.PoolClient) => Promise<T>): Promise<T>;
5
24
  /**
25
+ * Runs `fn` in a transaction that is scoped to one tenant.
26
+ *
27
+ * @param options `{ scope: "subtree" }` also lets `fn` read the rows of every
28
+ * descendant of the tenant. Writes stay limited to the tenant.
29
+ * @throws Error when `options.scope` is not "exact" or "subtree".
30
+ */
31
+ export declare function withTenantContext<T>(pool: pg.Pool, tenantId: string, fn: (client: pg.PoolClient) => Promise<T>, options?: TenantContextOptions): Promise<T>;
32
+ /**
33
+ * @deprecated Since 1.8 the library reaches across tenants through the control
34
+ * role of migration 032 (Stratum's `adminPool`), not through this setting.
35
+ * Removed in 2.0. Once the stratum_security legacy switch is off, the setting
36
+ * opens nothing, so this helper sees only what the pool's role sees.
37
+ *
6
38
  * Runs `fn` inside a transaction with the RLS bypass flag set, so control-plane
7
39
  * / system operations that legitimately span tenant boundaries (provisioning,
8
40
  * cascade ops, ancestry reads) can see and write all rows.
@@ -1 +1 @@
1
- {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/rls/session.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC;AAEpB,wBAAsB,gBAAgB,CACpC,MAAM,EAAE,EAAE,CAAC,UAAU,EACrB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,IAAI,CAAC,CAEf;AAED,wBAAsB,kBAAkB,CAAC,MAAM,EAAE,EAAE,CAAC,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAE7E;AAED,wBAAsB,iBAAiB,CAAC,CAAC,EACvC,IAAI,EAAE,EAAE,CAAC,IAAI,EACb,QAAQ,EAAE,MAAM,EAChB,EAAE,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,UAAU,KAAK,OAAO,CAAC,CAAC,CAAC,GACxC,OAAO,CAAC,CAAC,CAAC,CAcZ;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,aAAa,CAAC,CAAC,EACnC,IAAI,EAAE,EAAE,CAAC,IAAI,EACb,EAAE,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,UAAU,KAAK,OAAO,CAAC,CAAC,CAAC,GACxC,OAAO,CAAC,CAAC,CAAC,CAcZ;AAED,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,EAAE,CAAC,UAAU,GACpB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAMxB"}
1
+ {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/rls/session.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC;AAEpB;;;;;;;;GAQG;AACH,MAAM,MAAM,WAAW,GAAG,OAAO,GAAG,SAAS,CAAC;AAE9C,MAAM,WAAW,oBAAoB;IACnC,uCAAuC;IACvC,KAAK,CAAC,EAAE,WAAW,CAAC;CACrB;AAaD;;;;;GAKG;AACH,wBAAsB,gBAAgB,CACpC,MAAM,EAAE,EAAE,CAAC,UAAU,EACrB,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,oBAAyB,GACjC,OAAO,CAAC,IAAI,CAAC,CAKf;AAED,wBAAsB,kBAAkB,CAAC,MAAM,EAAE,EAAE,CAAC,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAI7E;AAED;;;;;;GAMG;AACH,wBAAsB,iBAAiB,CAAC,CAAC,EACvC,IAAI,EAAE,EAAE,CAAC,IAAI,EACb,QAAQ,EAAE,MAAM,EAChB,EAAE,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,UAAU,KAAK,OAAO,CAAC,CAAC,CAAC,EACzC,OAAO,GAAE,oBAAyB,GACjC,OAAO,CAAC,CAAC,CAAC,CAgBZ;AAID;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,aAAa,CAAC,CAAC,EACnC,IAAI,EAAE,EAAE,CAAC,IAAI,EACb,EAAE,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,UAAU,KAAK,OAAO,CAAC,CAAC,CAAC,GACxC,OAAO,CAAC,CAAC,CAAC,CAsBZ;AAED,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,EAAE,CAAC,UAAU,GACpB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAMxB"}
@@ -5,17 +5,40 @@ exports.resetTenantContext = resetTenantContext;
5
5
  exports.withTenantContext = withTenantContext;
6
6
  exports.withRlsBypass = withRlsBypass;
7
7
  exports.getCurrentTenantId = getCurrentTenantId;
8
- async function setTenantContext(client, tenantId) {
9
- await client.query("SELECT set_config('app.current_tenant_id', $1, true)", [tenantId]);
8
+ /** The value of app.tenant_scope for a scope. Only 'subtree' widens reads. */
9
+ function scopeSetting(options) {
10
+ const scope = options.scope ?? "exact";
11
+ if (scope !== "exact" && scope !== "subtree") {
12
+ throw new Error(`[stratum] Unknown tenant scope: ${String(scope)} (expected "exact" or "subtree")`);
13
+ }
14
+ return scope === "subtree" ? "subtree" : "";
15
+ }
16
+ /**
17
+ * Sets the tenant and the read scope for the current transaction.
18
+ *
19
+ * It always writes both settings, so a second call in the same transaction
20
+ * replaces an earlier "subtree" scope instead of keeping it.
21
+ */
22
+ async function setTenantContext(client, tenantId, options = {}) {
23
+ await client.query("SELECT set_config('app.current_tenant_id', $1, true), set_config('app.tenant_scope', $2, true)", [tenantId, scopeSetting(options)]);
10
24
  }
11
25
  async function resetTenantContext(client) {
12
- await client.query("SELECT set_config('app.current_tenant_id', '', true)");
26
+ await client.query("SELECT set_config('app.current_tenant_id', '', true), set_config('app.tenant_scope', '', true)");
13
27
  }
14
- async function withTenantContext(pool, tenantId, fn) {
28
+ /**
29
+ * Runs `fn` in a transaction that is scoped to one tenant.
30
+ *
31
+ * @param options `{ scope: "subtree" }` also lets `fn` read the rows of every
32
+ * descendant of the tenant. Writes stay limited to the tenant.
33
+ * @throws Error when `options.scope` is not "exact" or "subtree".
34
+ */
35
+ async function withTenantContext(pool, tenantId, fn, options = {}) {
36
+ // Fail on an unknown scope before a connection is taken.
37
+ scopeSetting(options);
15
38
  const client = await pool.connect();
16
39
  try {
17
40
  await client.query("BEGIN");
18
- await setTenantContext(client, tenantId);
41
+ await setTenantContext(client, tenantId, options);
19
42
  const result = await fn(client);
20
43
  await client.query("COMMIT");
21
44
  return result;
@@ -28,7 +51,13 @@ async function withTenantContext(pool, tenantId, fn) {
28
51
  client.release();
29
52
  }
30
53
  }
54
+ let warnedRlsBypass = false;
31
55
  /**
56
+ * @deprecated Since 1.8 the library reaches across tenants through the control
57
+ * role of migration 032 (Stratum's `adminPool`), not through this setting.
58
+ * Removed in 2.0. Once the stratum_security legacy switch is off, the setting
59
+ * opens nothing, so this helper sees only what the pool's role sees.
60
+ *
32
61
  * Runs `fn` inside a transaction with the RLS bypass flag set, so control-plane
33
62
  * / system operations that legitimately span tenant boundaries (provisioning,
34
63
  * cascade ops, ancestry reads) can see and write all rows.
@@ -42,6 +71,11 @@ async function withTenantContext(pool, tenantId, fn) {
42
71
  * access. Tenant-scoped work should use `withTenantContext` instead.
43
72
  */
44
73
  async function withRlsBypass(pool, fn) {
74
+ if (!warnedRlsBypass) {
75
+ warnedRlsBypass = true;
76
+ process.emitWarning("withRlsBypass is deprecated and will be removed in 2.0. Run cross-tenant work on a pool whose " +
77
+ "login is a member of the Stratum control role (migration 032) instead.", { type: "DeprecationWarning", code: "STRATUM_WITH_RLS_BYPASS" });
78
+ }
45
79
  const client = await pool.connect();
46
80
  try {
47
81
  await client.query("BEGIN");
@@ -1 +1 @@
1
- {"version":3,"file":"session.js","sourceRoot":"","sources":["../../src/rls/session.ts"],"names":[],"mappings":";;AAEA,4CAKC;AAED,gDAEC;AAED,8CAkBC;AAeD,sCAiBC;AAED,gDAQC;AAvEM,KAAK,UAAU,gBAAgB,CACpC,MAAqB,EACrB,QAAgB;IAEhB,MAAM,MAAM,CAAC,KAAK,CAAC,sDAAsD,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC;AACzF,CAAC;AAEM,KAAK,UAAU,kBAAkB,CAAC,MAAqB;IAC5D,MAAM,MAAM,CAAC,KAAK,CAAC,sDAAsD,CAAC,CAAC;AAC7E,CAAC;AAEM,KAAK,UAAU,iBAAiB,CACrC,IAAa,EACb,QAAgB,EAChB,EAAyC;IAEzC,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC;IACpC,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QAC5B,MAAM,gBAAgB,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;QACzC,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,MAAM,CAAC,CAAC;QAChC,MAAM,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;QAC7B,OAAO,MAAM,CAAC;IAChB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;QAC/B,MAAM,GAAG,CAAC;IACZ,CAAC;YAAS,CAAC;QACT,MAAM,CAAC,OAAO,EAAE,CAAC;IACnB,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;GAYG;AACI,KAAK,UAAU,aAAa,CACjC,IAAa,EACb,EAAyC;IAEzC,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC;IACpC,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QAC5B,MAAM,MAAM,CAAC,KAAK,CAAC,iDAAiD,CAAC,CAAC;QACtE,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,MAAM,CAAC,CAAC;QAChC,MAAM,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;QAC7B,OAAO,MAAM,CAAC;IAChB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;QAC/B,MAAM,GAAG,CAAC;IACZ,CAAC;YAAS,CAAC;QACT,MAAM,CAAC,OAAO,EAAE,CAAC;IACnB,CAAC;AACH,CAAC;AAEM,KAAK,UAAU,kBAAkB,CACtC,MAAqB;IAErB,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,KAAK,CAC5B,0EAA0E,CAC3E,CAAC;IACF,MAAM,KAAK,GAAG,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,eAAe,CAAC;IAC3C,OAAO,KAAK,IAAI,IAAI,CAAC;AACvB,CAAC"}
1
+ {"version":3,"file":"session.js","sourceRoot":"","sources":["../../src/rls/session.ts"],"names":[],"mappings":"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"}