@nodii/db-rls 0.12.0 → 0.14.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.
@@ -24,8 +24,9 @@
24
24
  // internally inside `sql.begin(...)`.
25
25
  import { sql as drizzleSql } from "drizzle-orm";
26
26
  import { drizzle } from "drizzle-orm/postgres-js";
27
+ import { assertNoTenantArgument, assertSystemContextEscapeAllowed, requireVerifiedTenantClaims, } from "./claims-context.js";
27
28
  import { requirePool } from "./init.js";
28
- import { emitSlowAcquireSpan, recordAcquisition, recordAcquisitionDuration, recordGucSetFailure, recordOwnerContextInvocation, recordTxDuration, withTxSpan, } from "./metrics.js";
29
+ import { emitSlowAcquireSpan, recordAcquisition, recordAcquisitionDuration, recordCrossPoolNesting, recordGucSetFailure, recordOwnerContextInvocation, recordTxDuration, withTxSpan, } from "./metrics.js";
29
30
  import { assertMigrateAllowed, } from "./migrate-guard.js";
30
31
  import { MissingTenantContext, TransactionRolledBack, CrossTenantSessionRefused, } from "./types.js";
31
32
  /**
@@ -248,12 +249,19 @@ async function withPostgresJsTx(pool, setup, fn) {
248
249
  * postgres-js Drizzle variant of `withUserContext`. Opens a tx on the
249
250
  * `authenticated` pool, sets `app.user_id` + `app.tenant_id` via the
250
251
  * canonical `auth.init(...)` helper, yields a Drizzle client.
252
+ *
253
+ * BREAKING (unforgeable tenant binding): the tenant is NO LONGER an
254
+ * argument — it is read from the request's verified claims, bound once by
255
+ * auth middleware via `runWithVerifiedTenantClaims`. See
256
+ * {@link withUserContext} in `src/context.ts` for the full rationale.
257
+ * This is the fleet-default driver, so this is the variant most of the
258
+ * 271 call sites go through.
259
+ *
260
+ * Throws `NoVerifiedTenantClaims` if no claims are bound.
251
261
  */
252
- export async function withUserContextDbPostgresJs(opts, fn) {
253
- if (!opts.userId)
254
- throw new MissingTenantContext();
255
- if (!opts.tenantId)
256
- throw new MissingTenantContext();
262
+ export async function withUserContextDbPostgresJs(fn) {
263
+ assertNoTenantArgument("withUserContextDbPostgresJs", fn);
264
+ const opts = requireVerifiedTenantClaims("withUserContextDbPostgresJs");
257
265
  const pool = requirePostgresJsPool("authenticated");
258
266
  return withPostgresJsTx(pool, async (db) => {
259
267
  await db.execute(drizzleSql `SELECT auth.init(${opts.userId}::uuid, ${opts.tenantId}::uuid)`);
@@ -272,7 +280,9 @@ export async function withUserContextDbPostgresJs(opts, fn) {
272
280
  * a legitimate tenant-A session transitioned a tenant-B approval task, got
273
281
  * HTTP 200, and released a 250,000 INR refund.
274
282
  *
275
- * Migrate to `withUserContextDbPostgresJs({ userId, tenantId }, fn)`.
283
+ * Migrate to `withUserContextDbPostgresJs(fn)` — the same pool, with the
284
+ * tenant the request's VERIFIED claims resolved to (bound by auth middleware
285
+ * via `runWithVerifiedTenantClaims`; it is no longer an argument).
276
286
  * To LIST a user's tenants (switcher / login picker), query
277
287
  * `auth.user_tenant_ids` from a services-pool context — that function is
278
288
  * intact; what is gone is opening a DATA session that spans them.
@@ -286,10 +296,23 @@ export async function withUserMembershipContextDbPostgresJs(_opts, _fn) {
286
296
  /**
287
297
  * postgres-js Drizzle variant of `withSystemContext`. Opens a tx on the
288
298
  * `services` pool with `app.tenant_id` GUC bound; yields a Drizzle client.
299
+ *
300
+ * Carries the STEP-1 FENCE: with verified claims bound
301
+ * (`hasVerifiedTenantClaims()` true) this throws
302
+ * `SystemContextUnderClaimsRefused` before touching a pool unless the
303
+ * caller passes an explicit non-blank `{ reason }` — the services-pool
304
+ * predicate is the bare `tenant_id = auth.get_tenant_id()` with no
305
+ * membership proof, the seam where the ₹250,000 cross-tenant refund
306
+ * reproduced. Unbound contexts (workers, cron, boot) are unchanged.
307
+ * See {@link withSystemContext} in `src/context.ts`.
289
308
  */
290
309
  export async function withSystemContextDbPostgresJs(opts, fn) {
291
310
  if (!opts.tenantId)
292
311
  throw new MissingTenantContext();
312
+ const sanctioned = assertSystemContextEscapeAllowed("withSystemContextDbPostgresJs", opts);
313
+ if (sanctioned) {
314
+ recordCrossPoolNesting("authenticated", "services", sanctioned.reason);
315
+ }
293
316
  const pool = requirePostgresJsPool("services");
294
317
  return withPostgresJsTx(pool, async (db) => {
295
318
  await db.execute(drizzleSql `SELECT auth.init_tenant(${opts.tenantId}::uuid)`);
@@ -365,6 +388,11 @@ function normalizeOwnerArgs(fnOrOpts, maybeFn) {
365
388
  // ergonomics gap 01371c61.
366
389
  export { _resetForTests, getDbRlsOrNull, initDbRls, requireDbRls, requirePool, } from "./init.js";
367
390
  export { assertMigrateAllowed } from "./migrate-guard.js";
391
+ // Unforgeable tenant binding. `claims-context.ts` imports only
392
+ // `node:async_hooks` + `./types.js`, so postgres-js-only services (the
393
+ // fleet default — no `pg` installed) can bind verified claims in their auth
394
+ // middleware without reaching for the pg-coupled entry points.
395
+ export { assertNoTenantArgument, assertSystemContextEscapeAllowed, getVerifiedTenantClaimsOrNull, hasVerifiedTenantClaims, requireVerifiedTenantClaims, runWithVerifiedTenantClaims, } from "./claims-context.js";
368
396
  // DB_RLS_METRIC_VOCABULARY is a pure const (its module imports only
369
397
  // @nodii/telemetry + a type), so pg-free services can merge it into
370
398
  // initTelemetry({ vocabularyRegistry }) without pulling in `pg`. Without this
@@ -373,5 +401,5 @@ export { assertMigrateAllowed } from "./migrate-guard.js";
373
401
  // db-rls's own metrics swallowed as 'not_in_vocabulary' (request 95a860a1).
374
402
  export { DB_RLS_METRIC_VOCABULARY } from "./metrics.js";
375
403
  export { generateTableGrants, withRls } from "./rls-policy-gen.js";
376
- export { DbRlsConfigError, DbRlsError, DbRlsNotInitialized, MissingTenantContext, OwnerContextOutsideMigrate, PoolUnavailable, TransactionRolledBack, } from "./types.js";
404
+ export { DbRlsConfigError, DbRlsError, DbRlsNotInitialized, InvalidVerifiedTenantClaims, MissingTenantContext, NoVerifiedTenantClaims, OwnerContextOutsideMigrate, PoolUnavailable, SystemContextUnderClaimsRefused, TenantArgumentRefused, TransactionRolledBack, VerifiedTenantClaimsAlreadyBound, } from "./types.js";
377
405
  //# sourceMappingURL=postgres-js-client.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"postgres-js-client.js","sourceRoot":"","sources":["../src/postgres-js-client.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAC3E,EAAE;AACF,mEAAmE;AACnE,kEAAkE;AAClE,wEAAwE;AACxE,+DAA+D;AAC/D,oEAAoE;AACpE,sBAAsB;AACtB,EAAE;AACF,WAAW;AACX,0EAA0E;AAC1E,kEAAkE;AAClE,qBAAqB;AACrB,oEAAoE;AACpE,uEAAuE;AACvE,mEAAmE;AACnE,EAAE;AACF,6DAA6D;AAC7D,gCAAgC;AAChC,2DAA2D;AAC3D,sCAAsC;AACtC,6CAA6C;AAC7C,qEAAqE;AACrE,sCAAsC;AAEtC,OAAO,EAAE,GAAG,IAAI,UAAU,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EAAE,OAAO,EAA2B,MAAM,yBAAyB,CAAC;AAG3E,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACxC,OAAO,EACL,mBAAmB,EACnB,iBAAiB,EACjB,yBAAyB,EACzB,mBAAmB,EACnB,4BAA4B,EAC5B,gBAAgB,EAChB,UAAU,GACX,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,oBAAoB,GAErB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,oBAAoB,EAKpB,qBAAqB,EAGrB,yBAAyB,GAE1B,MAAM,YAAY,CAAC;AAMpB;;;;;;;;;GASG;AACH,MAAM,OAAO,gBAAgB;IAClB,IAAI,CAAW;IACP,GAAG,CAAM;IAC1B;gFAC4E;IACpE,SAAS,CAA6C;IAC7C,aAAa,CAAS;IACtB,aAAa,CAGnB;IACM,UAAU,CAIhB;IAEX,YAAY,IAUX;QACC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;QACtB,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC;QACpB,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,aAAa,IAAI,EAAE,CAAC;QAC9C,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,aAAa,CAAC;QACxC,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,UAAU,CAAC;IACpC,CAAC;IAED;qEACiE;IACjE,IAAI,MAAM;QACR,OAAO,IAAI,CAAC,GAAG,CAAC;IAClB,CAAC;IAED,KAAK,CAAC,eAAe,CAAI,EAA4B;QACnD,MAAM,YAAY,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAChC,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAC3B,IAAI,OAAO,GAA0C,OAAO,CAAC;QAC7D,IAAI,YAAqB,CAAC;QAC1B,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,UAAU,EAAE,EAAE;gBAC9D,OAAO,MAAM,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,EAAE,EAAE,EAAE,EAAE;oBACvC,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,YAAY,CAAC;oBAC5C,iBAAiB,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;oBACxC,yBAAyB,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;oBAChD,IAAI,SAAS,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;wBACpC,mBAAmB,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;wBAC1C,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;4BACvB,IAAI,CAAC;gCACH,IAAI,CAAC,aAAa,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,UAAU,EAAE,SAAS,EAAE,CAAC,CAAC;4BACjE,CAAC;4BAAC,MAAM,CAAC;gCACP,eAAe;4BACjB,CAAC;wBACH,CAAC;oBACH,CAAC;oBACD,MAAM,IAAI,GAAG,IAAI,YAAY,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;oBAC7C,IAAI,CAAC;wBACH,MAAM,GAAG,GAAG,MAAM,EAAE,CAAC,IAAI,CAAC,CAAC;wBAC3B,OAAO,GAAG,WAAW,CAAC;wBACtB,UAAU,CAAC,WAAW,CAAC,CAAC;wBACxB,OAAO,GAAG,CAAC;oBACb,CAAC;oBAAC,OAAO,GAAG,EAAE,CAAC;wBACb,OAAO,GAAG,aAAa,CAAC;wBACxB,UAAU,CAAC,aAAa,CAAC,CAAC;wBAC1B,YAAY,GAAG,GAAG,CAAC;wBACnB,MAAM,GAAG,CAAC;oBACZ,CAAC;gBACH,CAAC,CAAC,CAAC;YACL,CAAC,CAAC,CAAC;YACH,OAAO,MAAW,CAAC;QACrB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,OAAO,KAAK,OAAO,EAAE,CAAC;gBACxB,6DAA6D;gBAC7D,oDAAoD;gBACpD,iBAAiB,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;gBACxC,OAAO,GAAG,aAAa,CAAC;gBACxB,YAAY,GAAG,GAAG,CAAC;YACrB,CAAC;YACD,MAAM,YAAY,IAAI,GAAG,CAAC;QAC5B,CAAC;gBAAS,CAAC;YACT,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,OAAO,CAAC;YACxC,gBAAgB,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,EAAE,UAAU,CAAC,CAAC;YACjD,IAAI,CAAC;gBACH,IAAI,CAAC,UAAU,EAAE,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,OAAO,EAAE,UAAU,EAAE,CAAC,CAAC;YAC9D,CAAC;YAAC,MAAM,CAAC;gBACP,eAAe;YACjB,CAAC;QACH,CAAC;IACH,CAAC;IAED;;;;;;;;;;;;;;;;;OAiBG;IACH,KAAK,CAAC,sBAAsB,CAC1B,EAAmD;QAEnD,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;YACpB,uEAAuE;YACvE,mEAAmE;YACnE,IAAI,CAAC,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACrC,CAAC;QACD,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,CAAC;QAC9B,MAAM,YAAY,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAChC,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAC3B,IAAI,OAAO,GAA0C,OAAO,CAAC;QAC7D,IAAI,YAAqB,CAAC;QAC1B,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,UAAU,EAAE,EAAE;gBAC9D,OAAO,MAAM,MAAM,CAAC,WAAW,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE;oBAC7C,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,YAAY,CAAC;oBAC5C,iBAAiB,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;oBACxC,yBAAyB,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;oBAChD,IAAI,SAAS,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;wBACpC,mBAAmB,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;wBAC1C,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;4BACvB,IAAI,CAAC;gCACH,IAAI,CAAC,aAAa,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,UAAU,EAAE,SAAS,EAAE,CAAC,CAAC;4BACjE,CAAC;4BAAC,MAAM,CAAC;gCACP,eAAe;4BACjB,CAAC;wBACH,CAAC;oBACH,CAAC;oBACD,IAAI,CAAC;wBACH,MAAM,GAAG,GAAG,MAAM,EAAE,CAClB,IAA8C,CAC/C,CAAC;wBACF,OAAO,GAAG,WAAW,CAAC;wBACtB,UAAU,CAAC,WAAW,CAAC,CAAC;wBACxB,OAAO,GAAG,CAAC;oBACb,CAAC;oBAAC,OAAO,GAAG,EAAE,CAAC;wBACb,OAAO,GAAG,aAAa,CAAC;wBACxB,UAAU,CAAC,aAAa,CAAC,CAAC;wBAC1B,YAAY,GAAG,GAAG,CAAC;wBACnB,MAAM,GAAG,CAAC;oBACZ,CAAC;gBACH,CAAC,CAAC,CAAC;YACL,CAAC,CAAC,CAAC;YACH,OAAO,MAAW,CAAC;QACrB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,OAAO,KAAK,OAAO,EAAE,CAAC;gBACxB,+CAA+C;gBAC/C,iBAAiB,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;gBACxC,OAAO,GAAG,aAAa,CAAC;gBACxB,YAAY,GAAG,GAAG,CAAC;YACrB,CAAC;YACD,MAAM,YAAY,IAAI,GAAG,CAAC;QAC5B,CAAC;gBAAS,CAAC;YACT,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,OAAO,CAAC;YACxC,gBAAgB,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,EAAE,UAAU,CAAC,CAAC;YACjD,IAAI,CAAC;gBACH,IAAI,CAAC,UAAU,EAAE,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,OAAO,EAAE,UAAU,EAAE,CAAC,CAAC;YAC9D,CAAC;YAAC,MAAM,CAAC;gBACP,eAAe;YACjB,CAAC;QACH,CAAC;IACH,CAAC;CACF;AAED,MAAM,YAAY;IACP,QAAQ,CAAW;IACnB,GAAG,CAAiB;IAE7B,YAAY,IAAc,EAAE,GAAmB;QAC7C,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;QACrB,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC;IACjB,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,GAAW,EAAE,SAA6B,EAAE;QACrD,MAAM,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,EAAE,MAAmB,CAAC,CAAC;IAClD,CAAC;IAED,KAAK,CAAC,SAAS,CAAC,GAAW,EAAE,KAAa;QACxC,+DAA+D;QAC/D,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,iCAAiC,EAAE,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC;QACzE,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,mBAAmB,CAAC,GAAG,CAAC,CAAC;YACzB,MAAM,GAAG,CAAC;QACZ,CAAC;IACH,CAAC;CACF;AAED,SAAS,qBAAqB,CAC5B,IAA6D;IAE7D,MAAM,IAAI,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC;IAC/B,IAAI,CAAC,CAAC,IAAI,YAAY,gBAAgB,CAAC,EAAE,CAAC;QACxC,MAAM,IAAI,KAAK,CACb,WAAW,IAAI,wLAAwL,CACxM,CAAC;IACJ,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,KAAK,UAAU,gBAAgB,CAC7B,IAAsB,EACtB,KAA8D,EAC9D,EAAwD;IAExD,8EAA8E;IAC9E,wEAAwE;IACxE,sEAAsE;IACtE,OAAO,IAAI,CAAC,sBAAsB,CAAa,KAAK,EAAE,EAAE,EAAE,EAAE;QAC1D,IAAI,CAAC;YACH,MAAM,KAAK,CAAC,EAAE,CAAC,CAAC;YAChB,OAAO,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC;QACtB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,IAAI,qBAAqB,CAAC,GAAG,CAAC,CAAC;QACvC,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,2BAA2B,CAI/C,IAAqB,EACrB,EAAwD;IAExD,IAAI,CAAC,IAAI,CAAC,MAAM;QAAE,MAAM,IAAI,oBAAoB,EAAE,CAAC;IACnD,IAAI,CAAC,IAAI,CAAC,QAAQ;QAAE,MAAM,IAAI,oBAAoB,EAAE,CAAC;IACrD,MAAM,IAAI,GAAG,qBAAqB,CAAC,eAAe,CAAC,CAAC;IACpD,OAAO,gBAAgB,CACrB,IAAI,EACJ,KAAK,EAAE,EAAE,EAAE,EAAE;QACX,MAAM,EAAE,CAAC,OAAO,CACd,UAAU,CAAA,oBAAoB,IAAI,CAAC,MAAM,WAAW,IAAI,CAAC,QAAQ,SAAS,CAC3E,CAAC;QACF,IAAI,IAAI,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;YAChC,MAAM,EAAE,CAAC,OAAO,CACd,UAAU,CAAA,sCAAsC,IAAI,CAAC,QAAQ,SAAS,CACvE,CAAC;QACJ,CAAC;IACH,CAAC,EACD,EAAE,CACH,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,CAAC,KAAK,UAAU,qCAAqC,CAIzD,KAAgC,EAChC,GAAyD;IAEzD,MAAM,IAAI,yBAAyB,CAAC,uCAAuC,CAAC,CAAC;AAC/E,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,6BAA6B,CAIjD,IAAuB,EACvB,EAAwD;IAExD,IAAI,CAAC,IAAI,CAAC,QAAQ;QAAE,MAAM,IAAI,oBAAoB,EAAE,CAAC;IACrD,MAAM,IAAI,GAAG,qBAAqB,CAAC,UAAU,CAAC,CAAC;IAC/C,OAAO,gBAAgB,CACrB,IAAI,EACJ,KAAK,EAAE,EAAE,EAAE,EAAE;QACX,MAAM,EAAE,CAAC,OAAO,CACd,UAAU,CAAA,2BAA2B,IAAI,CAAC,QAAQ,SAAS,CAC5D,CAAC;IACJ,CAAC,EACD,EAAE,CACH,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,CAAC,KAAK,UAAU,kCAAkC,CAItD,IAA4B,EAC5B,EAAwD;IAExD,MAAM,IAAI,GAAG,qBAAqB,CAAC,cAAc,CAAC,CAAC;IACnD,OAAO,gBAAgB,CACrB,IAAI,EACJ,KAAK,EAAE,EAAE,EAAE,EAAE;QACX,MAAM,EAAE,CAAC,OAAO,CACd,UAAU,CAAA,oCAAoC,IAAI,CAAC,MAAM,SAAS,CACnE,CAAC;QACF,MAAM,EAAE,CAAC,OAAO,CACd,UAAU,CAAA,oDAAoD,IAAI,CAAC,SAAS,SAAS,CACtF,CAAC;QACF,MAAM,EAAE,CAAC,OAAO,CACd,UAAU,CAAA,oDAAoD,IAAI,CAAC,UAAU,SAAS,CACvF,CAAC;IACJ,CAAC,EACD,EAAE,CACH,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,4BAA4B,CAIhD,QAE0D,EAC1D,OAA8D;IAE9D,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,GAAG,kBAAkB,CAAa,QAAQ,EAAE,OAAO,CAAC,CAAC;IACvE,IAAI,CAAC;QACH,oBAAoB,CAAC,IAAI,CAAC,CAAC;IAC7B,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,4BAA4B,CAAC,SAAS,CAAC,CAAC;QACxC,MAAM,GAAG,CAAC;IACZ,CAAC;IACD,4BAA4B,CAAC,SAAS,CAAC,CAAC;IACxC,MAAM,IAAI,GAAG,qBAAqB,CAAC,OAAO,CAAC,CAAC;IAC5C,OAAO,gBAAgB,CAAa,IAAI,EAAE,KAAK,IAAI,EAAE,GAAE,CAAC,EAAE,EAAE,CAAC,CAAC;AAChE,CAAC;AAED,SAAS,kBAAkB,CACzB,QAE0D,EAC1D,OAA8D;IAK9D,IAAI,OAAO,QAAQ,KAAK,UAAU,EAAE,CAAC;QACnC,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,EAAE,EAAE,QAAQ,EAAE,CAAC;IACpC,CAAC;IACD,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,SAAS,CACjB,+FAA+F,CAChG,CAAC;IACJ,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,OAAO,EAAE,CAAC;AACzC,CAAC;AAID,8EAA8E;AAC9E,+EAA+E;AAC/E,EAAE;AACF,4EAA4E;AAC5E,qEAAqE;AACrE,gFAAgF;AAChF,gFAAgF;AAChF,6EAA6E;AAC7E,8EAA8E;AAC9E,iEAAiE;AACjE,8EAA8E;AAC9E,2BAA2B;AAC3B,OAAO,EACL,cAAc,EACd,cAAc,EAEd,SAAS,EACT,YAAY,EACZ,WAAW,GAEZ,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAC1D,oEAAoE;AACpE,oEAAoE;AACpE,8EAA8E;AAC9E,+EAA+E;AAC/E,+EAA+E;AAC/E,4EAA4E;AAC5E,OAAO,EAAE,wBAAwB,EAAE,MAAM,cAAc,CAAC;AACxD,OAAO,EAAE,mBAAmB,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AACnE,OAAO,EACL,gBAAgB,EAChB,UAAU,EACV,mBAAmB,EACnB,oBAAoB,EACpB,0BAA0B,EAC1B,eAAe,EACf,qBAAqB,GACtB,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"postgres-js-client.js","sourceRoot":"","sources":["../src/postgres-js-client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Installs `auth.assert_tenant_fence()`.
3
+ *
4
+ * The function RAISES `P0001` naming every offender when the fence is broken,
5
+ * and returns silently when it is intact. It is the runtime post-condition:
6
+ * cheap enough to run once per process, specific enough that the error text
7
+ * tells an on-call engineer what to fix.
8
+ *
9
+ * The rule is the POSITIVE one, identical to `classifyAuthPolicy`: every
10
+ * permissive policy applying to `authenticated` MUST resolve through
11
+ * `auth.session_tenant_ids()`. Absence of the bad shape is not presence of the
12
+ * good one — a policy mangled by a sweep into something that mentions neither
13
+ * helper is an offender, not a pass.
14
+ *
15
+ * Role resolution uses `pg_has_role(..., 'USAGE')` plus OID 0 for PUBLIC, for
16
+ * the same reason the TypeScript verifier does: a `{public}` policy applies to
17
+ * `authenticated` without containing the word, and role membership is
18
+ * transitive. A substring test over `pg_policies.roles` misses both, and each
19
+ * was proven to yield a live cross-tenant read AND write while the check
20
+ * reported zero offenders.
21
+ */
22
+ export declare const TENANT_FENCE_GATE_SQL = "CREATE SCHEMA IF NOT EXISTS auth;\nCREATE OR REPLACE FUNCTION auth._fence_reason(p_expr text) RETURNS text\nLANGUAGE sql IMMUTABLE\nSET search_path = pg_catalog, public, pg_temp\nAS $reason$\n SELECT CASE\n WHEN p_expr ~* '(auth\\s*\\.\\s*)?user_tenant_ids\\s*\\('\n THEN 'consults auth.user_tenant_ids directly instead of auth.session_tenant_ids() \u2014 resolves across EVERY tenant the principal belongs to'\n WHEN p_expr IS NULL OR btrim(p_expr) = '' OR p_expr ~* '^\\(?\\s*true\\s*\\)?$'\n THEN 'is permissive with no tenant reference at all \u2014 every tenant, every row'\n ELSE 'does not resolve through auth.session_tenant_ids() \u2014 it may be fenced some other way, but nothing here proves it, and an unrecognized shape is not a safe shape'\n END\n$reason$;\nCREATE OR REPLACE FUNCTION auth.assert_tenant_fence(\n p_schemas text[] DEFAULT ARRAY['public'],\n p_exempt_policies text[] DEFAULT ARRAY[]::text[],\n p_exempt_tables text[] DEFAULT ARRAY[]::text[],\n p_tenant_columns text[] DEFAULT ARRAY['tenant_id','org_id','organization_id'],\n p_allow_degraded boolean DEFAULT true\n) RETURNS void\nLANGUAGE plpgsql STABLE SECURITY DEFINER\nSET search_path = pg_catalog, public, pg_temp\nAS $fence_check$\nDECLARE\n v_offenders text[] := ARRAY[]::text[];\n v_row record;\n v_resolver record;\n v_membership record;\n v_degraded boolean := false;\nBEGIN\n -- ---------------------------------------------------------------------\n -- 1. The resolver itself. If it is missing, every policy referencing it is\n -- already broken, and nothing below can be interpreted.\n -- ---------------------------------------------------------------------\n SELECT p.prosrc, p.prosecdef INTO v_resolver\n FROM pg_proc p JOIN pg_namespace n ON n.oid = p.pronamespace\n WHERE n.nspname = 'auth' AND p.proname = 'session_tenant_ids' LIMIT 1;\n\n IF v_resolver IS NULL THEN\n RAISE EXCEPTION 'tenant_fence: auth.session_tenant_ids() is not installed'\n USING ERRCODE = 'P0001',\n HINT = 'Apply INSTALL_SESSION_RESOLVER_SQL (applyAuthSchemaMigrations ships it, and @nodii/saga + @nodii/dsar + @nodii/audit-chain install it before their policies).';\n END IF;\n\n v_degraded := v_resolver.prosrc LIKE '%DB_RLS_DEGRADED_NO_MEMBERSHIP_MIRROR%';\n\n IF v_resolver.prosrc !~* 'RAISE\\s+EXCEPTION' THEN\n v_offenders := v_offenders || 'auth.session_tenant_ids() [session_tenant_ids] does not RAISE on an unbound session \u2014 it was replaced with a NULL-returning getter, which restores silent failure';\n END IF;\n\n IF NOT v_resolver.prosecdef THEN\n v_offenders := v_offenders || 'auth.session_tenant_ids() [session_tenant_ids] is not SECURITY DEFINER \u2014 it cannot read the membership mirror (permission denied), or will recurse into the mirror''s own policy (54001)';\n END IF;\n\n IF v_degraded AND NOT p_allow_degraded THEN\n v_offenders := v_offenders || 'auth.session_tenant_ids() [session_tenant_ids] is the DEGRADED fence-only resolver \u2014 the session tenant is trusted WITHOUT proving membership';\n END IF;\n\n -- The stub check applies ONLY to the strict resolver. Under the degraded body\n -- auth.user_tenant_ids is never consulted, so a stub there is the expected,\n -- D415-sanctioned terminal state \u2014 not a defect. Flagging it would make every\n -- legitimately-degraded service permanently red, which is how a gate gets\n -- switched off.\n IF NOT v_degraded THEN\n SELECT p.prosrc INTO v_membership\n FROM pg_proc p JOIN pg_namespace n ON n.oid = p.pronamespace\n WHERE n.nspname = 'auth' AND p.proname = 'user_tenant_ids' LIMIT 1;\n IF v_membership IS NOT NULL\n AND v_membership.prosrc ~* 'where\\s+false'\n AND v_membership.prosrc !~* 'from\\s+\\w' THEN\n v_offenders := v_offenders || 'auth.user_tenant_ids(uuid) [user_tenant_ids] is still the bootstrap stub returning the empty set \u2014 the strict resolver intersects to nothing, so the authenticated pool is silently deny-all';\n END IF;\n END IF;\n\n -- ---------------------------------------------------------------------\n -- 2. The positive rule over every authenticated-applicable policy.\n -- ---------------------------------------------------------------------\n FOR v_row IN\n SELECT n.nspname AS schemaname,\n c.relname AS tablename,\n pol.polname AS policyname,\n CASE pol.polcmd WHEN 'r' THEN 'SELECT' WHEN 'a' THEN 'INSERT'\n WHEN 'w' THEN 'UPDATE' WHEN 'd' THEN 'DELETE'\n ELSE 'ALL' END AS cmd,\n pg_get_expr(pol.polqual, pol.polrelid) AS qual,\n pg_get_expr(pol.polwithcheck, pol.polrelid) AS with_check\n FROM pg_policy pol\n JOIN pg_class c ON c.oid = pol.polrelid\n JOIN pg_namespace n ON n.oid = c.relnamespace\n WHERE n.nspname = ANY(p_schemas)\n -- RESTRICTIVE policies are ANDed; they can only narrow access, so they\n -- cannot be the source of a cross-tenant grant.\n AND pol.polpermissive\n AND (\n EXISTS (SELECT 1 FROM unnest(pol.polroles) r WHERE r = 0)\n OR (\n EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'authenticated')\n AND EXISTS (\n SELECT 1 FROM unnest(pol.polroles) r\n WHERE pg_has_role('authenticated', r, 'USAGE')\n )\n )\n )\n AND NOT (pol.polname = ANY(p_exempt_policies))\n AND NOT ((c.relname || '.' || pol.polname) = ANY(p_exempt_policies))\n AND NOT (c.relname = ANY(p_exempt_tables))\n AND NOT ((n.nspname || '.' || c.relname) = ANY(p_exempt_tables))\n ORDER BY c.relname, pol.polname\n LOOP\n -- A policy with NEITHER clause is permissive-everything.\n IF v_row.qual IS NULL AND v_row.with_check IS NULL THEN\n v_offenders := v_offenders || format(\n '%s.%s [%s %s] is permissive with no tenant reference at all \u2014 every tenant, every row',\n v_row.schemaname, v_row.tablename, v_row.policyname, v_row.cmd);\n CONTINUE;\n END IF;\n\n -- EVERY clause present must resolve. A fenced read with an unfenced\n -- WITH CHECK is a cross-tenant WRITE, which is what the refund incident\n -- actually exercised.\n IF v_row.qual IS NOT NULL\n AND v_row.qual !~* '(auth\\s*\\.\\s*)?session_tenant_ids\\s*\\(' THEN\n v_offenders := v_offenders || format('%s.%s [%s %s USING] %s :: %s',\n v_row.schemaname, v_row.tablename, v_row.policyname, v_row.cmd,\n auth._fence_reason(v_row.qual), v_row.qual);\n END IF;\n\n IF v_row.with_check IS NOT NULL\n AND v_row.with_check !~* '(auth\\s*\\.\\s*)?session_tenant_ids\\s*\\(' THEN\n v_offenders := v_offenders || format('%s.%s [%s %s WITH CHECK] %s :: %s',\n v_row.schemaname, v_row.tablename, v_row.policyname, v_row.cmd,\n auth._fence_reason(v_row.with_check), v_row.with_check);\n END IF;\n END LOOP;\n\n -- ---------------------------------------------------------------------\n -- 3. Table coverage. THE HOLE A POLICY-LEVEL CHECK CANNOT SEE: a table with\n -- a tenant column and RLS switched off has no rows in pg_policy to\n -- inspect, and under the canonical blanket grants it is readable AND\n -- writable across every tenant \u2014 strictly more open than the union\n -- predicate this release removes.\n --\n -- Discovered FROM THE CATALOG, never from a caller-supplied list. The\n -- author who forgets ENABLE ROW LEVEL SECURITY is the same author who\n -- forgets the list entry, in the same migration.\n -- ---------------------------------------------------------------------\n FOR v_row IN\n SELECT n.nspname AS schemaname, c.relname AS tablename,\n c.relkind::text AS relkind, c.relrowsecurity,\n COALESCE((SELECT true FROM unnest(COALESCE(c.reloptions,'{}')) o\n WHERE lower(replace(o,' ','')) = 'security_invoker=true'), false) AS security_invoker\n FROM pg_class c JOIN pg_namespace n ON n.oid = c.relnamespace\n WHERE c.relkind = ANY('{r,p,v,m,f}')\n AND n.nspname = ANY(p_schemas)\n AND NOT (c.relname = ANY(p_exempt_tables))\n AND NOT ((n.nspname || '.' || c.relname) = ANY(p_exempt_tables))\n AND EXISTS (SELECT 1 FROM pg_attribute a\n WHERE a.attrelid = c.oid AND a.attnum > 0\n AND NOT a.attisdropped AND a.attname = ANY(p_tenant_columns))\n ORDER BY n.nspname, c.relname\n LOOP\n IF v_row.relkind IN ('v','m','f') THEN\n -- A view with security_invoker=true executes as the CALLER, so the\n -- underlying table's RLS applies and the view is genuinely safe.\n IF v_row.relkind = 'v' AND v_row.security_invoker THEN CONTINUE; END IF;\n v_offenders := v_offenders || format(\n '%s.%s exposes a tenant column but cannot enforce RLS (relkind=%s) \u2014 a materialized/foreign relation has no row policies at all, and a view without security_invoker=true runs as its OWNER and bypasses the underlying table''s policies',\n v_row.schemaname, v_row.tablename, v_row.relkind);\n ELSIF NOT v_row.relrowsecurity THEN\n v_offenders := v_offenders || format(\n '%s.%s has a tenant column but ROW LEVEL SECURITY is NOT ENABLED \u2014 with the canonical blanket grants this table is readable and writable across EVERY tenant, and no policy is ever consulted',\n v_row.schemaname, v_row.tablename);\n END IF;\n END LOOP;\n\n IF array_length(v_offenders, 1) > 0 THEN\n RAISE EXCEPTION 'tenant_fence: % authenticated-pool policy/helper defect(s). A user session must reach exactly ONE tenant \u2014 the one the edge resolved from the request \u2014 and nothing else, ever.%',\n array_length(v_offenders, 1),\n E'\\n - ' || array_to_string(v_offenders, E'\\n - ')\n USING ERRCODE = 'P0001',\n HINT = 'Regenerate policies with @nodii/db-rls withRls() / generateRlsPolicy(), which emit \"<col> IN (SELECT auth.session_tenant_ids())\". Nodii-staff cross-tenant access is a different path entirely: the platform_ops pool + withPlatformOpsContext.';\n END IF;\nEND\n$fence_check$;\nGRANT EXECUTE ON FUNCTION auth._fence_reason(text) TO PUBLIC;\n";
23
+ /** Grant for the gate. Split from the DDL for the same reason every other
24
+ * grant in this package is: the roles must exist before the GRANT runs, and
25
+ * on a fresh database they do not yet. */
26
+ export declare const TENANT_FENCE_GATE_GRANTS_SQL = "GRANT EXECUTE ON FUNCTION auth.assert_tenant_fence(text[], text[], text[], text[], boolean) TO nodii_services, authenticated;\n";
27
+ /** The statement `withUserContext` runs once per process on first authenticated
28
+ * checkout. Kept as a named constant so the wiring is greppable from the call
29
+ * site and the test-suite asserts the exact text. */
30
+ export declare const TENANT_FENCE_GATE_CALL_SQL = "SELECT auth.assert_tenant_fence()";
31
+ /**
32
+ * THE DELIVERY UNIT, whole: the resolver the policies reference, plus the gate
33
+ * that proves they reference it.
34
+ *
35
+ * These ship together because they fail together. A database with the resolver
36
+ * but no gate has a fence nobody checks; a database with the gate but no
37
+ * resolver cannot create the policies in the first place. Splitting them would
38
+ * create a state where a service is half-delivered and neither half complains,
39
+ * which is the condition this whole release exists to end.
40
+ *
41
+ * Every delivery path applies exactly this constant:
42
+ * - `applyAuthSchemaMigrations` (@nodii/db-rls)
43
+ * - `applySagaMigrations` (@nodii/saga)
44
+ * - `applyDsarMigrations` (@nodii/dsar)
45
+ * - `applyAuditChainMigrations` (@nodii/audit-chain)
46
+ * - the `nodii-db-rls install-fence` CLI, for a service on none of the above
47
+ *
48
+ * The three mirrors inline a byte-equal copy rather than importing it, to
49
+ * preserve their deliberate zero-dependency posture on `@nodii/db-rls`. That
50
+ * duplication is guarded by a byte-equality test in each mirror's suite, not by
51
+ * a comment asking people to remember.
52
+ */
53
+ export declare function sessionFenceBootstrapSql(installSql: string): string;
54
+ //# sourceMappingURL=tenant-fence-gate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tenant-fence-gate.d.ts","sourceRoot":"","sources":["../src/tenant-fence-gate.ts"],"names":[],"mappings":"AAkDA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,qBAAqB,0hUAyLjC,CAAC;AAEF;;2CAE2C;AAC3C,eAAO,MAAM,4BAA4B,oIACxC,CAAC;AAEF;;sDAEsD;AACtD,eAAO,MAAM,0BAA0B,sCAAsC,CAAC;AAE9E;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,wBAAwB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAEnE"}
@@ -0,0 +1,291 @@
1
+ // The RUNTIME half of the fence gate: `auth.assert_tenant_fence()`.
2
+ //
3
+ // `verifyTenantFence` (tenant-fence.ts) is the deploy-time gate. It is
4
+ // thorough, it reports every offender at once, and it is the right thing to run
5
+ // in CI and in a `db:verify` step. It also cannot be the only gate, for a
6
+ // reason that is structural rather than a matter of diligence:
7
+ //
8
+ // SIX OF THE TEN SERVICES THIS RELEASE HAS TO REACH RUN NO DATABASE STEP ON
9
+ // DEPLOY AT ALL.
10
+ //
11
+ // That is measured, not assumed — `.github/workflows/deploy-dev.yml` in
12
+ // billing, kyc, notification, task-tracking, tenant and geo contains no
13
+ // `db:setup` and no `db:migrate`. Their `db:setup` exists, and it runs in
14
+ // `ci.yml`, against a throwaway CI Postgres that is deleted minutes later. A
15
+ // gate wired into `db:setup` would be green on every one of those services
16
+ // forever while their real databases drifted, which is worse than no gate,
17
+ // because it would be REPORTED as a pass.
18
+ //
19
+ // So the runtime gate has to live somewhere every service reaches without
20
+ // anyone wiring it. There are only two such places: the library code a service
21
+ // calls to get a connection, and the database itself. This module uses both —
22
+ // a SECURITY DEFINER function in the database, invoked by `withUserContext` on
23
+ // first use (context.ts).
24
+ //
25
+ // WHY A SQL FUNCTION RATHER THAN THE TYPESCRIPT VERIFIER.
26
+ //
27
+ // `PgTx.exec` returns `Promise<void>`. The pool abstraction in this library
28
+ // deliberately yields no row-returning query surface, so a JavaScript catalog
29
+ // scan cannot run on the pool a service already has — it needs a separate
30
+ // `RlsQuerier` the caller supplies. "The caller supplies it" is precisely the
31
+ // property that left `verifyRls` unwired in every repo for a year. A check
32
+ // whose invocation depends on somebody passing something is a check that will
33
+ // not run.
34
+ //
35
+ // A SECURITY DEFINER function has none of that dependency. It is installed by
36
+ // the same delivery unit as the resolver, so a database that can evaluate the
37
+ // fence predicate can always also evaluate the fence check. It is reachable
38
+ // through `exec`. It works identically from the Go and Python ports, and from
39
+ // psql during an incident. And because it is SECURITY DEFINER it can read
40
+ // `pg_policy`, which `authenticated` cannot.
41
+ //
42
+ // TWO IMPLEMENTATIONS IS A REAL COST, AND IT IS PAID DELIBERATELY.
43
+ //
44
+ // This function and `verifyTenantFence` encode the same rule twice, which is
45
+ // the duplication failure this codebase keeps being bitten by. The control is
46
+ // not a comment asking people to keep them in step: `tests/fence-gate-agreement
47
+ // .integration.test.ts` stands up each offending shape in a real database and
48
+ // asserts BOTH implementations reach the same verdict on it. If one is fixed
49
+ // and the other is not, that test goes red.
50
+ /**
51
+ * Installs `auth.assert_tenant_fence()`.
52
+ *
53
+ * The function RAISES `P0001` naming every offender when the fence is broken,
54
+ * and returns silently when it is intact. It is the runtime post-condition:
55
+ * cheap enough to run once per process, specific enough that the error text
56
+ * tells an on-call engineer what to fix.
57
+ *
58
+ * The rule is the POSITIVE one, identical to `classifyAuthPolicy`: every
59
+ * permissive policy applying to `authenticated` MUST resolve through
60
+ * `auth.session_tenant_ids()`. Absence of the bad shape is not presence of the
61
+ * good one — a policy mangled by a sweep into something that mentions neither
62
+ * helper is an offender, not a pass.
63
+ *
64
+ * Role resolution uses `pg_has_role(..., 'USAGE')` plus OID 0 for PUBLIC, for
65
+ * the same reason the TypeScript verifier does: a `{public}` policy applies to
66
+ * `authenticated` without containing the word, and role membership is
67
+ * transitive. A substring test over `pg_policies.roles` misses both, and each
68
+ * was proven to yield a live cross-tenant read AND write while the check
69
+ * reported zero offenders.
70
+ */
71
+ export const TENANT_FENCE_GATE_SQL = `CREATE SCHEMA IF NOT EXISTS auth;
72
+ CREATE OR REPLACE FUNCTION auth._fence_reason(p_expr text) RETURNS text
73
+ LANGUAGE sql IMMUTABLE
74
+ SET search_path = pg_catalog, public, pg_temp
75
+ AS $reason$
76
+ SELECT CASE
77
+ WHEN p_expr ~* '(auth\\s*\\.\\s*)?user_tenant_ids\\s*\\('
78
+ THEN 'consults auth.user_tenant_ids directly instead of auth.session_tenant_ids() — resolves across EVERY tenant the principal belongs to'
79
+ WHEN p_expr IS NULL OR btrim(p_expr) = '' OR p_expr ~* '^\\(?\\s*true\\s*\\)?$'
80
+ THEN 'is permissive with no tenant reference at all — every tenant, every row'
81
+ ELSE 'does not resolve through auth.session_tenant_ids() — it may be fenced some other way, but nothing here proves it, and an unrecognized shape is not a safe shape'
82
+ END
83
+ $reason$;
84
+ CREATE OR REPLACE FUNCTION auth.assert_tenant_fence(
85
+ p_schemas text[] DEFAULT ARRAY['public'],
86
+ p_exempt_policies text[] DEFAULT ARRAY[]::text[],
87
+ p_exempt_tables text[] DEFAULT ARRAY[]::text[],
88
+ p_tenant_columns text[] DEFAULT ARRAY['tenant_id','org_id','organization_id'],
89
+ p_allow_degraded boolean DEFAULT true
90
+ ) RETURNS void
91
+ LANGUAGE plpgsql STABLE SECURITY DEFINER
92
+ SET search_path = pg_catalog, public, pg_temp
93
+ AS $fence_check$
94
+ DECLARE
95
+ v_offenders text[] := ARRAY[]::text[];
96
+ v_row record;
97
+ v_resolver record;
98
+ v_membership record;
99
+ v_degraded boolean := false;
100
+ BEGIN
101
+ -- ---------------------------------------------------------------------
102
+ -- 1. The resolver itself. If it is missing, every policy referencing it is
103
+ -- already broken, and nothing below can be interpreted.
104
+ -- ---------------------------------------------------------------------
105
+ SELECT p.prosrc, p.prosecdef INTO v_resolver
106
+ FROM pg_proc p JOIN pg_namespace n ON n.oid = p.pronamespace
107
+ WHERE n.nspname = 'auth' AND p.proname = 'session_tenant_ids' LIMIT 1;
108
+
109
+ IF v_resolver IS NULL THEN
110
+ RAISE EXCEPTION 'tenant_fence: auth.session_tenant_ids() is not installed'
111
+ USING ERRCODE = 'P0001',
112
+ HINT = 'Apply INSTALL_SESSION_RESOLVER_SQL (applyAuthSchemaMigrations ships it, and @nodii/saga + @nodii/dsar + @nodii/audit-chain install it before their policies).';
113
+ END IF;
114
+
115
+ v_degraded := v_resolver.prosrc LIKE '%DB_RLS_DEGRADED_NO_MEMBERSHIP_MIRROR%';
116
+
117
+ IF v_resolver.prosrc !~* 'RAISE\\s+EXCEPTION' THEN
118
+ v_offenders := v_offenders || 'auth.session_tenant_ids() [session_tenant_ids] does not RAISE on an unbound session — it was replaced with a NULL-returning getter, which restores silent failure';
119
+ END IF;
120
+
121
+ IF NOT v_resolver.prosecdef THEN
122
+ v_offenders := v_offenders || 'auth.session_tenant_ids() [session_tenant_ids] is not SECURITY DEFINER — it cannot read the membership mirror (permission denied), or will recurse into the mirror''s own policy (54001)';
123
+ END IF;
124
+
125
+ IF v_degraded AND NOT p_allow_degraded THEN
126
+ v_offenders := v_offenders || 'auth.session_tenant_ids() [session_tenant_ids] is the DEGRADED fence-only resolver — the session tenant is trusted WITHOUT proving membership';
127
+ END IF;
128
+
129
+ -- The stub check applies ONLY to the strict resolver. Under the degraded body
130
+ -- auth.user_tenant_ids is never consulted, so a stub there is the expected,
131
+ -- D415-sanctioned terminal state — not a defect. Flagging it would make every
132
+ -- legitimately-degraded service permanently red, which is how a gate gets
133
+ -- switched off.
134
+ IF NOT v_degraded THEN
135
+ SELECT p.prosrc INTO v_membership
136
+ FROM pg_proc p JOIN pg_namespace n ON n.oid = p.pronamespace
137
+ WHERE n.nspname = 'auth' AND p.proname = 'user_tenant_ids' LIMIT 1;
138
+ IF v_membership IS NOT NULL
139
+ AND v_membership.prosrc ~* 'where\\s+false'
140
+ AND v_membership.prosrc !~* 'from\\s+\\w' THEN
141
+ v_offenders := v_offenders || 'auth.user_tenant_ids(uuid) [user_tenant_ids] is still the bootstrap stub returning the empty set — the strict resolver intersects to nothing, so the authenticated pool is silently deny-all';
142
+ END IF;
143
+ END IF;
144
+
145
+ -- ---------------------------------------------------------------------
146
+ -- 2. The positive rule over every authenticated-applicable policy.
147
+ -- ---------------------------------------------------------------------
148
+ FOR v_row IN
149
+ SELECT n.nspname AS schemaname,
150
+ c.relname AS tablename,
151
+ pol.polname AS policyname,
152
+ CASE pol.polcmd WHEN 'r' THEN 'SELECT' WHEN 'a' THEN 'INSERT'
153
+ WHEN 'w' THEN 'UPDATE' WHEN 'd' THEN 'DELETE'
154
+ ELSE 'ALL' END AS cmd,
155
+ pg_get_expr(pol.polqual, pol.polrelid) AS qual,
156
+ pg_get_expr(pol.polwithcheck, pol.polrelid) AS with_check
157
+ FROM pg_policy pol
158
+ JOIN pg_class c ON c.oid = pol.polrelid
159
+ JOIN pg_namespace n ON n.oid = c.relnamespace
160
+ WHERE n.nspname = ANY(p_schemas)
161
+ -- RESTRICTIVE policies are ANDed; they can only narrow access, so they
162
+ -- cannot be the source of a cross-tenant grant.
163
+ AND pol.polpermissive
164
+ AND (
165
+ EXISTS (SELECT 1 FROM unnest(pol.polroles) r WHERE r = 0)
166
+ OR (
167
+ EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'authenticated')
168
+ AND EXISTS (
169
+ SELECT 1 FROM unnest(pol.polroles) r
170
+ WHERE pg_has_role('authenticated', r, 'USAGE')
171
+ )
172
+ )
173
+ )
174
+ AND NOT (pol.polname = ANY(p_exempt_policies))
175
+ AND NOT ((c.relname || '.' || pol.polname) = ANY(p_exempt_policies))
176
+ AND NOT (c.relname = ANY(p_exempt_tables))
177
+ AND NOT ((n.nspname || '.' || c.relname) = ANY(p_exempt_tables))
178
+ ORDER BY c.relname, pol.polname
179
+ LOOP
180
+ -- A policy with NEITHER clause is permissive-everything.
181
+ IF v_row.qual IS NULL AND v_row.with_check IS NULL THEN
182
+ v_offenders := v_offenders || format(
183
+ '%s.%s [%s %s] is permissive with no tenant reference at all — every tenant, every row',
184
+ v_row.schemaname, v_row.tablename, v_row.policyname, v_row.cmd);
185
+ CONTINUE;
186
+ END IF;
187
+
188
+ -- EVERY clause present must resolve. A fenced read with an unfenced
189
+ -- WITH CHECK is a cross-tenant WRITE, which is what the refund incident
190
+ -- actually exercised.
191
+ IF v_row.qual IS NOT NULL
192
+ AND v_row.qual !~* '(auth\\s*\\.\\s*)?session_tenant_ids\\s*\\(' THEN
193
+ v_offenders := v_offenders || format('%s.%s [%s %s USING] %s :: %s',
194
+ v_row.schemaname, v_row.tablename, v_row.policyname, v_row.cmd,
195
+ auth._fence_reason(v_row.qual), v_row.qual);
196
+ END IF;
197
+
198
+ IF v_row.with_check IS NOT NULL
199
+ AND v_row.with_check !~* '(auth\\s*\\.\\s*)?session_tenant_ids\\s*\\(' THEN
200
+ v_offenders := v_offenders || format('%s.%s [%s %s WITH CHECK] %s :: %s',
201
+ v_row.schemaname, v_row.tablename, v_row.policyname, v_row.cmd,
202
+ auth._fence_reason(v_row.with_check), v_row.with_check);
203
+ END IF;
204
+ END LOOP;
205
+
206
+ -- ---------------------------------------------------------------------
207
+ -- 3. Table coverage. THE HOLE A POLICY-LEVEL CHECK CANNOT SEE: a table with
208
+ -- a tenant column and RLS switched off has no rows in pg_policy to
209
+ -- inspect, and under the canonical blanket grants it is readable AND
210
+ -- writable across every tenant — strictly more open than the union
211
+ -- predicate this release removes.
212
+ --
213
+ -- Discovered FROM THE CATALOG, never from a caller-supplied list. The
214
+ -- author who forgets ENABLE ROW LEVEL SECURITY is the same author who
215
+ -- forgets the list entry, in the same migration.
216
+ -- ---------------------------------------------------------------------
217
+ FOR v_row IN
218
+ SELECT n.nspname AS schemaname, c.relname AS tablename,
219
+ c.relkind::text AS relkind, c.relrowsecurity,
220
+ COALESCE((SELECT true FROM unnest(COALESCE(c.reloptions,'{}')) o
221
+ WHERE lower(replace(o,' ','')) = 'security_invoker=true'), false) AS security_invoker
222
+ FROM pg_class c JOIN pg_namespace n ON n.oid = c.relnamespace
223
+ WHERE c.relkind = ANY('{r,p,v,m,f}')
224
+ AND n.nspname = ANY(p_schemas)
225
+ AND NOT (c.relname = ANY(p_exempt_tables))
226
+ AND NOT ((n.nspname || '.' || c.relname) = ANY(p_exempt_tables))
227
+ AND EXISTS (SELECT 1 FROM pg_attribute a
228
+ WHERE a.attrelid = c.oid AND a.attnum > 0
229
+ AND NOT a.attisdropped AND a.attname = ANY(p_tenant_columns))
230
+ ORDER BY n.nspname, c.relname
231
+ LOOP
232
+ IF v_row.relkind IN ('v','m','f') THEN
233
+ -- A view with security_invoker=true executes as the CALLER, so the
234
+ -- underlying table's RLS applies and the view is genuinely safe.
235
+ IF v_row.relkind = 'v' AND v_row.security_invoker THEN CONTINUE; END IF;
236
+ v_offenders := v_offenders || format(
237
+ '%s.%s exposes a tenant column but cannot enforce RLS (relkind=%s) — a materialized/foreign relation has no row policies at all, and a view without security_invoker=true runs as its OWNER and bypasses the underlying table''s policies',
238
+ v_row.schemaname, v_row.tablename, v_row.relkind);
239
+ ELSIF NOT v_row.relrowsecurity THEN
240
+ v_offenders := v_offenders || format(
241
+ '%s.%s has a tenant column but ROW LEVEL SECURITY is NOT ENABLED — with the canonical blanket grants this table is readable and writable across EVERY tenant, and no policy is ever consulted',
242
+ v_row.schemaname, v_row.tablename);
243
+ END IF;
244
+ END LOOP;
245
+
246
+ IF array_length(v_offenders, 1) > 0 THEN
247
+ RAISE EXCEPTION 'tenant_fence: % authenticated-pool policy/helper defect(s). A user session must reach exactly ONE tenant — the one the edge resolved from the request — and nothing else, ever.%',
248
+ array_length(v_offenders, 1),
249
+ E'\\n - ' || array_to_string(v_offenders, E'\\n - ')
250
+ USING ERRCODE = 'P0001',
251
+ HINT = 'Regenerate policies with @nodii/db-rls withRls() / generateRlsPolicy(), which emit "<col> IN (SELECT auth.session_tenant_ids())". Nodii-staff cross-tenant access is a different path entirely: the platform_ops pool + withPlatformOpsContext.';
252
+ END IF;
253
+ END
254
+ $fence_check$;
255
+ GRANT EXECUTE ON FUNCTION auth._fence_reason(text) TO PUBLIC;
256
+ `;
257
+ /** Grant for the gate. Split from the DDL for the same reason every other
258
+ * grant in this package is: the roles must exist before the GRANT runs, and
259
+ * on a fresh database they do not yet. */
260
+ export const TENANT_FENCE_GATE_GRANTS_SQL = `GRANT EXECUTE ON FUNCTION auth.assert_tenant_fence(text[], text[], text[], text[], boolean) TO nodii_services, authenticated;
261
+ `;
262
+ /** The statement `withUserContext` runs once per process on first authenticated
263
+ * checkout. Kept as a named constant so the wiring is greppable from the call
264
+ * site and the test-suite asserts the exact text. */
265
+ export const TENANT_FENCE_GATE_CALL_SQL = "SELECT auth.assert_tenant_fence()";
266
+ /**
267
+ * THE DELIVERY UNIT, whole: the resolver the policies reference, plus the gate
268
+ * that proves they reference it.
269
+ *
270
+ * These ship together because they fail together. A database with the resolver
271
+ * but no gate has a fence nobody checks; a database with the gate but no
272
+ * resolver cannot create the policies in the first place. Splitting them would
273
+ * create a state where a service is half-delivered and neither half complains,
274
+ * which is the condition this whole release exists to end.
275
+ *
276
+ * Every delivery path applies exactly this constant:
277
+ * - `applyAuthSchemaMigrations` (@nodii/db-rls)
278
+ * - `applySagaMigrations` (@nodii/saga)
279
+ * - `applyDsarMigrations` (@nodii/dsar)
280
+ * - `applyAuditChainMigrations` (@nodii/audit-chain)
281
+ * - the `nodii-db-rls install-fence` CLI, for a service on none of the above
282
+ *
283
+ * The three mirrors inline a byte-equal copy rather than importing it, to
284
+ * preserve their deliberate zero-dependency posture on `@nodii/db-rls`. That
285
+ * duplication is guarded by a byte-equality test in each mirror's suite, not by
286
+ * a comment asking people to remember.
287
+ */
288
+ export function sessionFenceBootstrapSql(installSql) {
289
+ return `${installSql}${TENANT_FENCE_GATE_SQL}`;
290
+ }
291
+ //# sourceMappingURL=tenant-fence-gate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tenant-fence-gate.js","sourceRoot":"","sources":["../src/tenant-fence-gate.ts"],"names":[],"mappings":"AAAA,oEAAoE;AACpE,EAAE;AACF,uEAAuE;AACvE,gFAAgF;AAChF,0EAA0E;AAC1E,+DAA+D;AAC/D,EAAE;AACF,8EAA8E;AAC9E,mBAAmB;AACnB,EAAE;AACF,wEAAwE;AACxE,wEAAwE;AACxE,0EAA0E;AAC1E,6EAA6E;AAC7E,2EAA2E;AAC3E,2EAA2E;AAC3E,0CAA0C;AAC1C,EAAE;AACF,0EAA0E;AAC1E,+EAA+E;AAC/E,8EAA8E;AAC9E,+EAA+E;AAC/E,0BAA0B;AAC1B,EAAE;AACF,0DAA0D;AAC1D,EAAE;AACF,4EAA4E;AAC5E,8EAA8E;AAC9E,0EAA0E;AAC1E,8EAA8E;AAC9E,2EAA2E;AAC3E,8EAA8E;AAC9E,WAAW;AACX,EAAE;AACF,8EAA8E;AAC9E,8EAA8E;AAC9E,4EAA4E;AAC5E,8EAA8E;AAC9E,0EAA0E;AAC1E,6CAA6C;AAC7C,EAAE;AACF,mEAAmE;AACnE,EAAE;AACF,6EAA6E;AAC7E,8EAA8E;AAC9E,gFAAgF;AAChF,8EAA8E;AAC9E,6EAA6E;AAC7E,4CAA4C;AAE5C;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAyLpC,CAAC;AAEF;;2CAE2C;AAC3C,MAAM,CAAC,MAAM,4BAA4B,GAAG;CAC3C,CAAC;AAEF;;sDAEsD;AACtD,MAAM,CAAC,MAAM,0BAA0B,GAAG,mCAAmC,CAAC;AAE9E;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,UAAU,wBAAwB,CAAC,UAAkB;IACzD,OAAO,GAAG,UAAU,GAAG,qBAAqB,EAAE,CAAC;AACjD,CAAC"}