@nodii/db-rls 0.11.0 → 0.13.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.
- package/bin/migrate-gen.mjs +11 -0
- package/dist/auth-schema.d.ts +207 -1
- package/dist/auth-schema.d.ts.map +1 -1
- package/dist/auth-schema.js +331 -4
- package/dist/auth-schema.js.map +1 -1
- package/dist/context.d.ts +31 -8
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +103 -24
- package/dist/context.js.map +1 -1
- package/dist/drizzle-client.d.ts +17 -5
- package/dist/drizzle-client.d.ts.map +1 -1
- package/dist/drizzle-client.js +19 -18
- package/dist/drizzle-client.js.map +1 -1
- package/dist/fence-cli.d.ts +21 -0
- package/dist/fence-cli.d.ts.map +1 -0
- package/dist/fence-cli.js +191 -0
- package/dist/fence-cli.js.map +1 -0
- package/dist/index.d.ts +7 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +31 -7
- package/dist/index.js.map +1 -1
- package/dist/postgres-js-client.d.ts +17 -10
- package/dist/postgres-js-client.d.ts.map +1 -1
- package/dist/postgres-js-client.js +19 -21
- package/dist/postgres-js-client.js.map +1 -1
- package/dist/rls-policy-gen.d.ts +43 -27
- package/dist/rls-policy-gen.d.ts.map +1 -1
- package/dist/rls-policy-gen.js +40 -15
- package/dist/rls-policy-gen.js.map +1 -1
- package/dist/rls-policy.d.ts +11 -1
- package/dist/rls-policy.d.ts.map +1 -1
- package/dist/rls-policy.js +31 -4
- package/dist/rls-policy.js.map +1 -1
- package/dist/tenant-fence-gate.d.ts +54 -0
- package/dist/tenant-fence-gate.d.ts.map +1 -0
- package/dist/tenant-fence-gate.js +291 -0
- package/dist/tenant-fence-gate.js.map +1 -0
- package/dist/tenant-fence.d.ts +165 -0
- package/dist/tenant-fence.d.ts.map +1 -0
- package/dist/tenant-fence.js +542 -0
- package/dist/tenant-fence.js.map +1 -0
- package/dist/types.d.ts +18 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +22 -0
- package/dist/types.js.map +1 -1
- package/dist/verify-rls-cli.d.ts.map +1 -1
- package/dist/verify-rls-cli.js +86 -10
- package/dist/verify-rls-cli.js.map +1 -1
- package/dist/verify-rls.d.ts +26 -5
- package/dist/verify-rls.d.ts.map +1 -1
- package/dist/verify-rls.js +73 -5
- package/dist/verify-rls.js.map +1 -1
- package/package.json +2 -2
package/dist/context.d.ts
CHANGED
|
@@ -1,25 +1,48 @@
|
|
|
1
1
|
import { type OwnerContextOpts } from "./migrate-guard.js";
|
|
2
2
|
import { type AutoDispatchOpts, type PgTx, type PlatformOpsContextOpts, type SystemContextOpts, type UserContextOpts, type UserMembershipContextOpts } from "./types.js";
|
|
3
|
+
/** Test-only. Not exported through the public barrel. */
|
|
4
|
+
export declare function _resetFenceGateForTests(): void;
|
|
3
5
|
/**
|
|
4
6
|
* withUserContext — opens a tx on the `authenticated` pool, sets
|
|
5
7
|
* `app.user_id` + `app.tenant_id` (+ optional `app.source_ip`) via
|
|
6
8
|
* `set_config(..., true)` tx-scoped GUCs, yields the tx, commits/rolls
|
|
7
9
|
* back per the PgPool contract.
|
|
8
10
|
*
|
|
11
|
+
* On the FIRST call in a process it also runs the tenant-fence gate (see
|
|
12
|
+
* {@link assertFenceOnce}) and refuses to yield the tx if the fence is broken.
|
|
13
|
+
*
|
|
9
14
|
* Throws `DbRlsNotInitialized` if `initDbRls` hasn't run.
|
|
10
15
|
* Wraps any error thrown by `fn` in `TransactionRolledBack`.
|
|
11
16
|
*/
|
|
12
17
|
export declare function withUserContext<T>(opts: UserContextOpts, fn: (tx: PgTx) => Promise<T>): Promise<T>;
|
|
13
18
|
/**
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
19
|
+
* @deprecated REMOVED — always throws {@link CrossTenantSessionRefused}.
|
|
20
|
+
*
|
|
21
|
+
* This helper opened an `authenticated`-pool transaction binding ONLY
|
|
22
|
+
* `app.user_id`, deliberately leaving `app.tenant_id` unset so that the old
|
|
23
|
+
* union predicate `tenant_id IN (SELECT auth.user_tenant_ids(...))` would
|
|
24
|
+
* resolve across EVERY tenant the principal was an active member of. Its own
|
|
25
|
+
* docblock named task-tracking as the use case.
|
|
26
|
+
*
|
|
27
|
+
* That is not a policy bug that the predicate fix cleans up incidentally — it
|
|
28
|
+
* is the MECHANISM of the incident, sitting in the library. Reproduced
|
|
29
|
+
* end-to-end: a legitimate tenant-A session transitioned a tenant-B approval
|
|
30
|
+
* task, got HTTP 200, and released a 250,000 INR refund. Rewriting predicates
|
|
31
|
+
* while leaving this callable would have changed nothing for any caller of it.
|
|
32
|
+
*
|
|
33
|
+
* Migrate to `withUserContext({ userId, tenantId }, fn)` — the same pool, with
|
|
34
|
+
* the tenant the request actually resolved to. If a caller genuinely needs to
|
|
35
|
+
* know which tenants a user belongs to (a tenant switcher, a login
|
|
36
|
+
* tenant-picker), query `auth.user_tenant_ids` directly from a services-pool
|
|
37
|
+
* context; that function is deliberately left intact and still answers exactly
|
|
38
|
+
* that question. What is no longer available is opening a DATA session that
|
|
39
|
+
* spans them.
|
|
40
|
+
*
|
|
41
|
+
* The symbol is retained rather than deleted so a JavaScript consumer (no
|
|
42
|
+
* typechecker) gets this explanation at runtime instead of
|
|
43
|
+
* `undefined is not a function`.
|
|
21
44
|
*/
|
|
22
|
-
export declare function withUserMembershipContext<T>(
|
|
45
|
+
export declare function withUserMembershipContext<T>(_opts: UserMembershipContextOpts, _fn: (tx: PgTx) => Promise<T>): Promise<T>;
|
|
23
46
|
/**
|
|
24
47
|
* withSystemContext — opens a tx on the `services` (nodii_services) pool,
|
|
25
48
|
* sets `app.tenant_id` via tx-scoped `set_config`, yields the tx, commits
|
package/dist/context.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AASA,OAAO,EAEL,KAAK,gBAAgB,EACtB,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AASA,OAAO,EAEL,KAAK,gBAAgB,EACtB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACL,KAAK,gBAAgB,EAGrB,KAAK,IAAI,EACT,KAAK,sBAAsB,EAC3B,KAAK,iBAAiB,EAEtB,KAAK,eAAe,EACpB,KAAK,yBAAyB,EAC/B,MAAM,YAAY,CAAC;AAepB,yDAAyD;AACzD,wBAAgB,uBAAuB,IAAI,IAAI,CAE9C;AAyCD;;;;;;;;;;;GAWG;AACH,wBAAsB,eAAe,CAAC,CAAC,EACrC,IAAI,EAAE,eAAe,EACrB,EAAE,EAAE,CAAC,EAAE,EAAE,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,GAC3B,OAAO,CAAC,CAAC,CAAC,CAmCZ;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAsB,yBAAyB,CAAC,CAAC,EAC/C,KAAK,EAAE,yBAAyB,EAChC,GAAG,EAAE,CAAC,EAAE,EAAE,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,GAC5B,OAAO,CAAC,CAAC,CAAC,CAEZ;AAED;;;;GAIG;AACH,wBAAsB,iBAAiB,CAAC,CAAC,EACvC,IAAI,EAAE,iBAAiB,EACvB,EAAE,EAAE,CAAC,EAAE,EAAE,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,GAC3B,OAAO,CAAC,CAAC,CAAC,CAUZ;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,gBAAgB,CAAC,CAAC,EACtC,QAAQ,EAAE,gBAAgB,GAAG,CAAC,CAAC,EAAE,EAAE,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC,EACvD,OAAO,CAAC,EAAE,CAAC,EAAE,EAAE,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,GACjC,OAAO,CAAC,CAAC,CAAC,CAiBZ;AAiBD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAsB,sBAAsB,CAAC,CAAC,EAC5C,IAAI,EAAE,sBAAsB,EAC5B,EAAE,EAAE,CAAC,EAAE,EAAE,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,GAC3B,OAAO,CAAC,CAAC,CAAC,CAYZ;AAMD,4DAA4D;AAC5D,wBAAgB,8CAA8C,IAAI,IAAI,CAErE;AAED;;;;;;;;;GASG;AACH,wBAAsB,eAAe,CAAC,CAAC,EACrC,IAAI,EAAE,sBAAsB,EAC5B,EAAE,EAAE,CAAC,EAAE,EAAE,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,GAC3B,OAAO,CAAC,CAAC,CAAC,CAWZ;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,YAAY,CAAC,CAAC,EAClC,IAAI,EAAE,gBAAgB,EACtB,EAAE,EAAE,CAAC,EAAE,EAAE,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,GAC3B,OAAO,CAAC,CAAC,CAAC,CAeZ"}
|
package/dist/context.js
CHANGED
|
@@ -7,17 +7,82 @@
|
|
|
7
7
|
import { requirePool } from "./init.js";
|
|
8
8
|
import { recordOwnerContextInvocation } from "./metrics.js";
|
|
9
9
|
import { assertMigrateAllowed, } from "./migrate-guard.js";
|
|
10
|
-
import {
|
|
10
|
+
import { TENANT_FENCE_GATE_CALL_SQL } from "./tenant-fence-gate.js";
|
|
11
|
+
import { CrossTenantSessionRefused, MissingTenantContext, TransactionRolledBack, } from "./types.js";
|
|
12
|
+
/**
|
|
13
|
+
* Process-local latch for the first-use fence gate. `null` until the gate has
|
|
14
|
+
* run; thereafter the settled promise, so the check costs one round-trip per
|
|
15
|
+
* PROCESS rather than one per transaction.
|
|
16
|
+
*
|
|
17
|
+
* A REJECTED promise is retained deliberately. A broken fence is not a
|
|
18
|
+
* transient condition — it is a property of the schema — so re-checking on
|
|
19
|
+
* every subsequent request would turn one clear failure into a storm of
|
|
20
|
+
* identical ones while adding a catalog scan to each. The service stays down
|
|
21
|
+
* and says the same thing every time, which is the intended behaviour.
|
|
22
|
+
*/
|
|
23
|
+
let fenceGate = null;
|
|
24
|
+
/** Test-only. Not exported through the public barrel. */
|
|
25
|
+
export function _resetFenceGateForTests() {
|
|
26
|
+
fenceGate = null;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* THE GATE. Runs `auth.assert_tenant_fence()` once per process, on the first
|
|
30
|
+
* authenticated-pool checkout.
|
|
31
|
+
*
|
|
32
|
+
* WHY HERE, AND NOT IN A DEPLOY SCRIPT.
|
|
33
|
+
*
|
|
34
|
+
* The obvious home is a `db:verify` step in each service's deploy. It does not
|
|
35
|
+
* work, and this is measured rather than argued: SIX of the ten services this
|
|
36
|
+
* release has to reach — billing, kyc, notification, task-tracking, tenant and
|
|
37
|
+
* geo — have NO database step in `deploy-dev.yml` at all. Their `db:setup`
|
|
38
|
+
* runs in `ci.yml` against a throwaway Postgres. A gate wired there is green
|
|
39
|
+
* forever on the services that need it most, and reported as a pass.
|
|
40
|
+
*
|
|
41
|
+
* WHY NOT `initDbRls`. Boot order forbids it. billing runs `applySagaMigrations`
|
|
42
|
+
* and `applyDsarMigrations` at boot, AFTER its pools are constructed — a check
|
|
43
|
+
* at `initDbRls` time would fire against a schema that is about to be fixed
|
|
44
|
+
* two statements later and fail every deploy.
|
|
45
|
+
*
|
|
46
|
+
* First-authenticated-use is the earliest point that is both after all boot
|
|
47
|
+
* migrations and before any tenant row is served, and it is reached by
|
|
48
|
+
* construction: there is no way to open an authenticated transaction without
|
|
49
|
+
* passing through here. That is the "cannot forget to opt in" property. It is
|
|
50
|
+
* not a flag, a config key, or a script somebody adds to a pipeline — it is the
|
|
51
|
+
* function you must call to get the connection.
|
|
52
|
+
*
|
|
53
|
+
* WHY IT IS NOT THE `verifyRls` SHAPE. `verifyRls` takes `requiredTables` from
|
|
54
|
+
* the caller, and the author who forgets `ENABLE ROW LEVEL SECURITY` forgets
|
|
55
|
+
* the list entry in the same migration — so it sits wired and green while
|
|
56
|
+
* unprotected tables accumulate behind it. `auth.assert_tenant_fence()` asks
|
|
57
|
+
* the catalog which tables carry a tenant column. Nobody maintains a list,
|
|
58
|
+
* so nobody can omit the entry that mattered.
|
|
59
|
+
*/
|
|
60
|
+
async function assertFenceOnce(tx) {
|
|
61
|
+
if (fenceGate === null) {
|
|
62
|
+
fenceGate = tx.exec(TENANT_FENCE_GATE_CALL_SQL);
|
|
63
|
+
}
|
|
64
|
+
return fenceGate;
|
|
65
|
+
}
|
|
11
66
|
/**
|
|
12
67
|
* withUserContext — opens a tx on the `authenticated` pool, sets
|
|
13
68
|
* `app.user_id` + `app.tenant_id` (+ optional `app.source_ip`) via
|
|
14
69
|
* `set_config(..., true)` tx-scoped GUCs, yields the tx, commits/rolls
|
|
15
70
|
* back per the PgPool contract.
|
|
16
71
|
*
|
|
72
|
+
* On the FIRST call in a process it also runs the tenant-fence gate (see
|
|
73
|
+
* {@link assertFenceOnce}) and refuses to yield the tx if the fence is broken.
|
|
74
|
+
*
|
|
17
75
|
* Throws `DbRlsNotInitialized` if `initDbRls` hasn't run.
|
|
18
76
|
* Wraps any error thrown by `fn` in `TransactionRolledBack`.
|
|
19
77
|
*/
|
|
20
78
|
export async function withUserContext(opts, fn) {
|
|
79
|
+
// Reject a blank principal or tenant HERE rather than binding an empty GUC
|
|
80
|
+
// and letting `auth.session_tenant_ids()` raise 42501 on the first query.
|
|
81
|
+
// Both fail closed, but this one names the caller instead of a policy.
|
|
82
|
+
if (!opts?.userId)
|
|
83
|
+
throw new MissingTenantContext();
|
|
84
|
+
if (!opts?.tenantId)
|
|
85
|
+
throw new MissingTenantContext();
|
|
21
86
|
const pool = requirePool("authenticated");
|
|
22
87
|
return pool.withTransaction(async (tx) => {
|
|
23
88
|
// Canonical GUC namespace is `app.*` (08-rls §2/§6): the generated
|
|
@@ -31,6 +96,16 @@ export async function withUserContext(opts, fn) {
|
|
|
31
96
|
if (opts.sourceIp !== undefined) {
|
|
32
97
|
await tx.setConfig("app.source_ip", opts.sourceIp);
|
|
33
98
|
}
|
|
99
|
+
// BEFORE `fn`, deliberately. The point of the gate is that no tenant row is
|
|
100
|
+
// served through a broken fence — running it afterwards would mean the
|
|
101
|
+
// first request reads cross-tenant data and THEN reports the problem.
|
|
102
|
+
//
|
|
103
|
+
// NOT wrapped in TransactionRolledBack: that wrapper exists to mark "the
|
|
104
|
+
// caller's own code threw". This is the library refusing to hand over the
|
|
105
|
+
// transaction, and it must surface with its own message and SQLSTATE
|
|
106
|
+
// intact so the on-call engineer sees the offender list rather than a
|
|
107
|
+
// generic rollback.
|
|
108
|
+
await assertFenceOnce(tx);
|
|
34
109
|
try {
|
|
35
110
|
return await fn(tx);
|
|
36
111
|
}
|
|
@@ -40,30 +115,34 @@ export async function withUserContext(opts, fn) {
|
|
|
40
115
|
});
|
|
41
116
|
}
|
|
42
117
|
/**
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
118
|
+
* @deprecated REMOVED — always throws {@link CrossTenantSessionRefused}.
|
|
119
|
+
*
|
|
120
|
+
* This helper opened an `authenticated`-pool transaction binding ONLY
|
|
121
|
+
* `app.user_id`, deliberately leaving `app.tenant_id` unset so that the old
|
|
122
|
+
* union predicate `tenant_id IN (SELECT auth.user_tenant_ids(...))` would
|
|
123
|
+
* resolve across EVERY tenant the principal was an active member of. Its own
|
|
124
|
+
* docblock named task-tracking as the use case.
|
|
125
|
+
*
|
|
126
|
+
* That is not a policy bug that the predicate fix cleans up incidentally — it
|
|
127
|
+
* is the MECHANISM of the incident, sitting in the library. Reproduced
|
|
128
|
+
* end-to-end: a legitimate tenant-A session transitioned a tenant-B approval
|
|
129
|
+
* task, got HTTP 200, and released a 250,000 INR refund. Rewriting predicates
|
|
130
|
+
* while leaving this callable would have changed nothing for any caller of it.
|
|
131
|
+
*
|
|
132
|
+
* Migrate to `withUserContext({ userId, tenantId }, fn)` — the same pool, with
|
|
133
|
+
* the tenant the request actually resolved to. If a caller genuinely needs to
|
|
134
|
+
* know which tenants a user belongs to (a tenant switcher, a login
|
|
135
|
+
* tenant-picker), query `auth.user_tenant_ids` directly from a services-pool
|
|
136
|
+
* context; that function is deliberately left intact and still answers exactly
|
|
137
|
+
* that question. What is no longer available is opening a DATA session that
|
|
138
|
+
* spans them.
|
|
139
|
+
*
|
|
140
|
+
* The symbol is retained rather than deleted so a JavaScript consumer (no
|
|
141
|
+
* typechecker) gets this explanation at runtime instead of
|
|
142
|
+
* `undefined is not a function`.
|
|
50
143
|
*/
|
|
51
|
-
export async function withUserMembershipContext(
|
|
52
|
-
|
|
53
|
-
return pool.withTransaction(async (tx) => {
|
|
54
|
-
// ONLY app.user_id — leaving app.tenant_id unset is intentional: the §4
|
|
55
|
-
// predicate keys on user_tenant_ids(get_user_id()), not a pinned tenant.
|
|
56
|
-
await tx.setConfig("app.user_id", opts.userId);
|
|
57
|
-
if (opts.sourceIp !== undefined) {
|
|
58
|
-
await tx.setConfig("app.source_ip", opts.sourceIp);
|
|
59
|
-
}
|
|
60
|
-
try {
|
|
61
|
-
return await fn(tx);
|
|
62
|
-
}
|
|
63
|
-
catch (cause) {
|
|
64
|
-
throw new TransactionRolledBack(cause);
|
|
65
|
-
}
|
|
66
|
-
});
|
|
144
|
+
export async function withUserMembershipContext(_opts, _fn) {
|
|
145
|
+
throw new CrossTenantSessionRefused("withUserMembershipContext");
|
|
67
146
|
}
|
|
68
147
|
/**
|
|
69
148
|
* withSystemContext — opens a tx on the `services` (nodii_services) pool,
|
package/dist/context.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context.js","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAAA,gDAAgD;AAChD,EAAE;AACF,uEAAuE;AACvE,0EAA0E;AAC1E,kEAAkE;AAClE,YAAY;AAEZ,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACxC,OAAO,EAAE,4BAA4B,EAAE,MAAM,cAAc,CAAC;AAC5D,OAAO,EACL,oBAAoB,GAErB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAEL,oBAAoB,EAIpB,qBAAqB,GAGtB,MAAM,YAAY,CAAC;AAEpB
|
|
1
|
+
{"version":3,"file":"context.js","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"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"}
|
package/dist/drizzle-client.d.ts
CHANGED
|
@@ -10,12 +10,24 @@ type DrizzleClient<TSchema extends Record<string, unknown> = Record<string, unkn
|
|
|
10
10
|
*/
|
|
11
11
|
export declare function withUserContextDb<T, TSchema extends Record<string, unknown> = Record<string, unknown>>(opts: UserContextOpts, fn: (db: DrizzleClient<TSchema>) => Promise<T>): Promise<T>;
|
|
12
12
|
/**
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
13
|
+
* @deprecated REMOVED — always throws {@link CrossTenantSessionRefused}.
|
|
14
|
+
*
|
|
15
|
+
* Opened an `authenticated`-pool transaction binding ONLY `app.user_id`,
|
|
16
|
+
* leaving `app.tenant_id` deliberately unset so the old union predicate
|
|
17
|
+
* resolved across EVERY tenant the principal belonged to. That is the
|
|
18
|
+
* mechanism of the cross-tenant approval incident, not a side effect of it:
|
|
19
|
+
* a legitimate tenant-A session transitioned a tenant-B approval task, got
|
|
20
|
+
* HTTP 200, and released a 250,000 INR refund.
|
|
21
|
+
*
|
|
22
|
+
* Migrate to `withUserContextDb({ userId, tenantId }, fn)`.
|
|
23
|
+
* To LIST a user's tenants (switcher / login picker), query
|
|
24
|
+
* `auth.user_tenant_ids` from a services-pool context — that function is
|
|
25
|
+
* intact; what is gone is opening a DATA session that spans them.
|
|
26
|
+
*
|
|
27
|
+
* Retained as a throwing stub rather than deleted so a JavaScript consumer
|
|
28
|
+
* gets this explanation instead of `undefined is not a function`.
|
|
17
29
|
*/
|
|
18
|
-
export declare function withUserMembershipContextDb<T, TSchema extends Record<string, unknown> = Record<string, unknown>>(
|
|
30
|
+
export declare function withUserMembershipContextDb<T, TSchema extends Record<string, unknown> = Record<string, unknown>>(_opts: UserMembershipContextOpts, _fn: (db: DrizzleClient<TSchema>) => Promise<T>): Promise<T>;
|
|
19
31
|
/**
|
|
20
32
|
* withSystemContext (Drizzle variant) — opens a tx on the `services` pool
|
|
21
33
|
* with `app.tenant_id` GUC bound; yields a Drizzle client.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"drizzle-client.d.ts","sourceRoot":"","sources":["../src/drizzle-client.ts"],"names":[],"mappings":"AAmBA,OAAO,EAAW,KAAK,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAKzE,OAAO,EAEL,KAAK,gBAAgB,EACtB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,KAAK,EACV,iBAAiB,EACjB,eAAe,EACf,yBAAyB,EAC1B,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"drizzle-client.d.ts","sourceRoot":"","sources":["../src/drizzle-client.ts"],"names":[],"mappings":"AAmBA,OAAO,EAAW,KAAK,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAKzE,OAAO,EAEL,KAAK,gBAAgB,EACtB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,KAAK,EACV,iBAAiB,EACjB,eAAe,EACf,yBAAyB,EAC1B,MAAM,YAAY,CAAC;AAWpB,KAAK,aAAa,CAChB,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,IAC/D,cAAc,CAAC,OAAO,CAAC,CAAC;AA2D5B;;;;;GAKG;AACH,wBAAsB,iBAAiB,CACrC,CAAC,EACD,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAEjE,IAAI,EAAE,eAAe,EACrB,EAAE,EAAE,CAAC,EAAE,EAAE,aAAa,CAAC,OAAO,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,GAC7C,OAAO,CAAC,CAAC,CAAC,CAoBZ;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,2BAA2B,CAC/C,CAAC,EACD,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAEjE,KAAK,EAAE,yBAAyB,EAChC,GAAG,EAAE,CAAC,EAAE,EAAE,aAAa,CAAC,OAAO,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,GAC9C,OAAO,CAAC,CAAC,CAAC,CAEZ;AAED;;;GAGG;AACH,wBAAsB,mBAAmB,CACvC,CAAC,EACD,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAEjE,IAAI,EAAE,iBAAiB,EACvB,EAAE,EAAE,CAAC,EAAE,EAAE,aAAa,CAAC,OAAO,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,GAC7C,OAAO,CAAC,CAAC,CAAC,CAUZ;AAED;;;;;;;;;GASG;AACH,wBAAsB,kBAAkB,CACtC,CAAC,EACD,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAEjE,QAAQ,EAAE,gBAAgB,GAAG,CAAC,CAAC,EAAE,EAAE,aAAa,CAAC,OAAO,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC,EACzE,OAAO,CAAC,EAAE,CAAC,EAAE,EAAE,aAAa,CAAC,OAAO,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,GACnD,OAAO,CAAC,CAAC,CAAC,CAeZ;AAoBD,YAAY,EAAE,aAAa,EAAE,cAAc,EAAE,CAAC"}
|
package/dist/drizzle-client.js
CHANGED
|
@@ -21,7 +21,7 @@ import { requirePool } from "./init.js";
|
|
|
21
21
|
import { recordOwnerContextInvocation, withTxSpan } from "./metrics.js";
|
|
22
22
|
import { assertMigrateAllowed, } from "./migrate-guard.js";
|
|
23
23
|
import { PgClientPool } from "./pg-pool-adapter.js";
|
|
24
|
-
import { MissingTenantContext, TransactionRolledBack } from "./types.js";
|
|
24
|
+
import { CrossTenantSessionRefused, MissingTenantContext, TransactionRolledBack, } from "./types.js";
|
|
25
25
|
function clientFor(_pool) {
|
|
26
26
|
// Only PgClientPool exposes a real pg PoolClient. In-memory pools don't.
|
|
27
27
|
// The caller is responsible for using *Db helpers only against real pools.
|
|
@@ -97,24 +97,25 @@ export async function withUserContextDb(opts, fn) {
|
|
|
97
97
|
}, (db) => fn(db));
|
|
98
98
|
}
|
|
99
99
|
/**
|
|
100
|
-
*
|
|
101
|
-
*
|
|
102
|
-
*
|
|
103
|
-
*
|
|
100
|
+
* @deprecated REMOVED — always throws {@link CrossTenantSessionRefused}.
|
|
101
|
+
*
|
|
102
|
+
* Opened an `authenticated`-pool transaction binding ONLY `app.user_id`,
|
|
103
|
+
* leaving `app.tenant_id` deliberately unset so the old union predicate
|
|
104
|
+
* resolved across EVERY tenant the principal belonged to. That is the
|
|
105
|
+
* mechanism of the cross-tenant approval incident, not a side effect of it:
|
|
106
|
+
* a legitimate tenant-A session transitioned a tenant-B approval task, got
|
|
107
|
+
* HTTP 200, and released a 250,000 INR refund.
|
|
108
|
+
*
|
|
109
|
+
* Migrate to `withUserContextDb({ userId, tenantId }, fn)`.
|
|
110
|
+
* To LIST a user's tenants (switcher / login picker), query
|
|
111
|
+
* `auth.user_tenant_ids` from a services-pool context — that function is
|
|
112
|
+
* intact; what is gone is opening a DATA session that spans them.
|
|
113
|
+
*
|
|
114
|
+
* Retained as a throwing stub rather than deleted so a JavaScript consumer
|
|
115
|
+
* gets this explanation instead of `undefined is not a function`.
|
|
104
116
|
*/
|
|
105
|
-
export async function withUserMembershipContextDb(
|
|
106
|
-
|
|
107
|
-
throw new MissingTenantContext();
|
|
108
|
-
const pool = requireRealPool("authenticated");
|
|
109
|
-
return withRealTx(pool, async (client) => {
|
|
110
|
-
// ONLY app.user_id — set_user_id, not init, so no tenant is pinned.
|
|
111
|
-
await client.query("SELECT auth.set_user_id($1::uuid)", [opts.userId]);
|
|
112
|
-
if (opts.sourceIp !== undefined) {
|
|
113
|
-
await client.query("SELECT set_config('app.source_ip', $1, true)", [
|
|
114
|
-
opts.sourceIp,
|
|
115
|
-
]);
|
|
116
|
-
}
|
|
117
|
-
}, (db) => fn(db));
|
|
117
|
+
export async function withUserMembershipContextDb(_opts, _fn) {
|
|
118
|
+
throw new CrossTenantSessionRefused("withUserMembershipContextDb");
|
|
118
119
|
}
|
|
119
120
|
/**
|
|
120
121
|
* withSystemContext (Drizzle variant) — opens a tx on the `services` pool
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"drizzle-client.js","sourceRoot":"","sources":["../src/drizzle-client.ts"],"names":[],"mappings":"AAAA,sDAAsD;AACtD,EAAE;AACF,yEAAyE;AACzE,wEAAwE;AACxE,iEAAiE;AACjE,uEAAuE;AACvE,2DAA2D;AAC3D,EAAE;AACF,iBAAiB;AACjB,0EAA0E;AAC1E,sEAAsE;AACtE,0BAA0B;AAC1B,sEAAsE;AACtE,wCAAwC;AACxC,8DAA8D;AAC9D,sEAAsE;AACtE,qEAAqE;AACrE,6CAA6C;AAE7C,OAAO,EAAE,OAAO,EAAuB,MAAM,2BAA2B,CAAC;AAGzE,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACxC,OAAO,EAAE,4BAA4B,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AACxE,OAAO,EACL,oBAAoB,GAErB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAMpD,OAAO,
|
|
1
|
+
{"version":3,"file":"drizzle-client.js","sourceRoot":"","sources":["../src/drizzle-client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export interface FenceCliResult {
|
|
2
|
+
/** 0 = clean, 1 = fence defect, 2 = usage / connection error. */
|
|
3
|
+
code: number;
|
|
4
|
+
message: string;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* `install-fence` — apply the delivery unit (resolver + gate).
|
|
8
|
+
*
|
|
9
|
+
* Idempotent and re-runnable. Must be run with OWNER credentials: it creates
|
|
10
|
+
* functions in the `auth` schema. Safe to run on every deploy; safe to run
|
|
11
|
+
* twice; and re-running is how a service that has since landed a membership
|
|
12
|
+
* mirror upgrades itself from the degraded body to the strict one.
|
|
13
|
+
*/
|
|
14
|
+
export declare function runInstallFenceCli(args: readonly string[]): Promise<FenceCliResult>;
|
|
15
|
+
/**
|
|
16
|
+
* `verify-fence` — assert the fence from the CATALOG, using the TypeScript
|
|
17
|
+
* verifier (which reports EVERY offender at once rather than raising on the
|
|
18
|
+
* first, and covers the unique-index check the SQL gate leaves to deploy time).
|
|
19
|
+
*/
|
|
20
|
+
export declare function runVerifyFenceCli(args: readonly string[]): Promise<FenceCliResult>;
|
|
21
|
+
//# sourceMappingURL=fence-cli.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fence-cli.d.ts","sourceRoot":"","sources":["../src/fence-cli.ts"],"names":[],"mappings":"AA+BA,MAAM,WAAW,cAAc;IAC7B,iEAAiE;IACjE,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAkDD;;;;;;;GAOG;AACH,wBAAsB,kBAAkB,CACtC,IAAI,EAAE,SAAS,MAAM,EAAE,GACtB,OAAO,CAAC,cAAc,CAAC,CAsCzB;AAED;;;;GAIG;AACH,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,SAAS,MAAM,EAAE,GACtB,OAAO,CAAC,cAAc,CAAC,CAkCzB"}
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
// `nodii-db-rls install-fence` and `nodii-db-rls verify-fence`.
|
|
2
|
+
//
|
|
3
|
+
// The eager, pipeline-facing half of the gate. `withUserContext` runs
|
|
4
|
+
// `auth.assert_tenant_fence()` on first authenticated use, which is the
|
|
5
|
+
// unforgettable check — but "unforgettable" is not the same as "early", and a
|
|
6
|
+
// deploy that can fail at 09:00 rather than on the first customer request at
|
|
7
|
+
// 09:04 should.
|
|
8
|
+
//
|
|
9
|
+
// THIS IS NOT OFFERED AS THE PRIMARY MECHANISM, and the reason is measured
|
|
10
|
+
// rather than stylistic. Of the ten services that call neither
|
|
11
|
+
// `applyAuthSchemaMigrations` nor `authSchemaHelpers`, SIX — billing, kyc,
|
|
12
|
+
// notification, task-tracking, tenant and geo — have NO database step in
|
|
13
|
+
// `deploy-dev.yml`. Wiring the fence exclusively into a deploy script would
|
|
14
|
+
// leave those six permanently unchecked while reporting green, which is the
|
|
15
|
+
// precise failure mode this release exists to end. So the CLI is the
|
|
16
|
+
// belt-and-braces for pipelines that DO have a database step, and the runtime
|
|
17
|
+
// gate is the floor for everyone.
|
|
18
|
+
//
|
|
19
|
+
// `install-fence` exists because the delivery unit has to be applicable to a
|
|
20
|
+
// service that is on none of the library paths — one command, owner
|
|
21
|
+
// credentials, no code change:
|
|
22
|
+
//
|
|
23
|
+
// bunx @nodii/db-rls install-fence --db "$DATABASE_URL"
|
|
24
|
+
import { SESSION_FENCE_BOOTSTRAP_SQL, SESSION_TENANT_IDS_GRANTS_SQL, } from "./auth-schema.js";
|
|
25
|
+
import { TENANT_FENCE_GATE_GRANTS_SQL } from "./tenant-fence-gate.js";
|
|
26
|
+
import { TenantFenceError, verifyTenantFence } from "./tenant-fence.js";
|
|
27
|
+
function splitList(value) {
|
|
28
|
+
if (!value)
|
|
29
|
+
return [];
|
|
30
|
+
return value
|
|
31
|
+
.split(",")
|
|
32
|
+
.map((s) => s.trim())
|
|
33
|
+
.filter((s) => s.length > 0);
|
|
34
|
+
}
|
|
35
|
+
function parse(args) {
|
|
36
|
+
const out = {
|
|
37
|
+
allowDegraded: true,
|
|
38
|
+
skipGrants: false,
|
|
39
|
+
help: false,
|
|
40
|
+
};
|
|
41
|
+
for (let i = 0; i < args.length; i++) {
|
|
42
|
+
const a = args[i];
|
|
43
|
+
if (a === "--db" && i + 1 < args.length)
|
|
44
|
+
out.dbUrl = args[++i];
|
|
45
|
+
else if (a === "--schema" && i + 1 < args.length)
|
|
46
|
+
out.schema = args[++i];
|
|
47
|
+
else if (a === "--exempt-policies" && i + 1 < args.length)
|
|
48
|
+
out.exemptPolicies = args[++i];
|
|
49
|
+
else if (a === "--exempt-tables" && i + 1 < args.length)
|
|
50
|
+
out.exemptTables = args[++i];
|
|
51
|
+
// Default is to ACCEPT the degraded resolver: it is the D415-sanctioned
|
|
52
|
+
// terminal state for a service with no membership mirror, and a gate that
|
|
53
|
+
// is red on a legitimate configuration gets switched off. `--strict-only`
|
|
54
|
+
// is for a service that HAS a mirror and wants to prove it never silently
|
|
55
|
+
// regresses to fence-only.
|
|
56
|
+
else if (a === "--strict-only")
|
|
57
|
+
out.allowDegraded = false;
|
|
58
|
+
else if (a === "--skip-grants")
|
|
59
|
+
out.skipGrants = true;
|
|
60
|
+
else if (a === "--help" || a === "-h")
|
|
61
|
+
out.help = true;
|
|
62
|
+
}
|
|
63
|
+
return out;
|
|
64
|
+
}
|
|
65
|
+
function connectionString(parsed) {
|
|
66
|
+
return parsed.dbUrl ?? process.env.DATABASE_URL ?? process.env.DB_RLS_PG_URL;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* `install-fence` — apply the delivery unit (resolver + gate).
|
|
70
|
+
*
|
|
71
|
+
* Idempotent and re-runnable. Must be run with OWNER credentials: it creates
|
|
72
|
+
* functions in the `auth` schema. Safe to run on every deploy; safe to run
|
|
73
|
+
* twice; and re-running is how a service that has since landed a membership
|
|
74
|
+
* mirror upgrades itself from the degraded body to the strict one.
|
|
75
|
+
*/
|
|
76
|
+
export async function runInstallFenceCli(args) {
|
|
77
|
+
const parsed = parse(args);
|
|
78
|
+
if (parsed.help)
|
|
79
|
+
return { code: 0, message: installUsage() };
|
|
80
|
+
const conn = connectionString(parsed);
|
|
81
|
+
if (!conn) {
|
|
82
|
+
return {
|
|
83
|
+
code: 2,
|
|
84
|
+
message: "install-fence: no connection string — pass --db <url> or set DATABASE_URL / DB_RLS_PG_URL.",
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
const { Pool } = await import("pg");
|
|
88
|
+
const pool = new Pool({ connectionString: conn, max: 1 });
|
|
89
|
+
try {
|
|
90
|
+
await pool.query(SESSION_FENCE_BOOTSTRAP_SQL);
|
|
91
|
+
if (!parsed.skipGrants) {
|
|
92
|
+
await pool.query(SESSION_TENANT_IDS_GRANTS_SQL);
|
|
93
|
+
await pool.query(TENANT_FENCE_GATE_GRANTS_SQL);
|
|
94
|
+
}
|
|
95
|
+
const { rows } = await pool.query(`SELECT prosrc FROM pg_proc p JOIN pg_namespace n ON n.oid = p.pronamespace
|
|
96
|
+
WHERE n.nspname = 'auth' AND p.proname = 'session_tenant_ids'`);
|
|
97
|
+
const body = rows[0]?.prosrc ?? "";
|
|
98
|
+
const variant = body.includes("DB_RLS_DEGRADED_NO_MEMBERSHIP_MIRROR")
|
|
99
|
+
? "DEGRADED (fence-only, D415) — auth.user_tenant_ids is absent or still the bootstrap stub"
|
|
100
|
+
: "STRICT — session tenant INTERSECT server-resolved membership";
|
|
101
|
+
return {
|
|
102
|
+
code: 0,
|
|
103
|
+
message: `install-fence: OK. auth.session_tenant_ids() installed as ${variant}\ninstall-fence: auth.assert_tenant_fence() installed.`,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
catch (err) {
|
|
107
|
+
return { code: 2, message: `install-fence: ${err.message}` };
|
|
108
|
+
}
|
|
109
|
+
finally {
|
|
110
|
+
await pool.end().catch(() => { });
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* `verify-fence` — assert the fence from the CATALOG, using the TypeScript
|
|
115
|
+
* verifier (which reports EVERY offender at once rather than raising on the
|
|
116
|
+
* first, and covers the unique-index check the SQL gate leaves to deploy time).
|
|
117
|
+
*/
|
|
118
|
+
export async function runVerifyFenceCli(args) {
|
|
119
|
+
const parsed = parse(args);
|
|
120
|
+
if (parsed.help)
|
|
121
|
+
return { code: 0, message: verifyUsage() };
|
|
122
|
+
const conn = connectionString(parsed);
|
|
123
|
+
if (!conn) {
|
|
124
|
+
return {
|
|
125
|
+
code: 2,
|
|
126
|
+
message: "verify-fence: no connection string — pass --db <url> or set DATABASE_URL / DB_RLS_PG_URL.",
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
const { Pool } = await import("pg");
|
|
130
|
+
const pool = new Pool({ connectionString: conn, max: 1 });
|
|
131
|
+
try {
|
|
132
|
+
await verifyTenantFence(pool, {
|
|
133
|
+
schema: parsed.schema,
|
|
134
|
+
exemptPolicies: splitList(parsed.exemptPolicies),
|
|
135
|
+
exemptTables: splitList(parsed.exemptTables),
|
|
136
|
+
allowDegradedResolver: parsed.allowDegraded,
|
|
137
|
+
});
|
|
138
|
+
return {
|
|
139
|
+
code: 0,
|
|
140
|
+
message: "verify-fence: OK — every authenticated policy resolves through auth.session_tenant_ids(), every tenant-scoped relation is protected.",
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
catch (err) {
|
|
144
|
+
if (err instanceof TenantFenceError)
|
|
145
|
+
return { code: 1, message: err.message };
|
|
146
|
+
return { code: 2, message: `verify-fence: ${err.message}` };
|
|
147
|
+
}
|
|
148
|
+
finally {
|
|
149
|
+
await pool.end().catch(() => { });
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
function installUsage() {
|
|
153
|
+
return `usage: install-fence [--db <url>] [--skip-grants]
|
|
154
|
+
|
|
155
|
+
Applies the tenant-fence delivery unit with OWNER credentials:
|
|
156
|
+
- auth.session_tenant_ids() — the authenticated-pool resolver. The STRICT
|
|
157
|
+
body (session tenant INTERSECT membership) is installed where a real
|
|
158
|
+
auth.user_tenant_ids exists; the DEGRADED fence-only body (D415) where it
|
|
159
|
+
is absent or still the bootstrap stub. Derived from the catalog on every
|
|
160
|
+
run — there is no per-service list to get wrong, and re-running after a
|
|
161
|
+
membership mirror lands upgrades degraded to strict automatically.
|
|
162
|
+
- auth.assert_tenant_fence() — the runtime gate @nodii/db-rls calls on the
|
|
163
|
+
first authenticated-pool checkout.
|
|
164
|
+
|
|
165
|
+
Idempotent. Safe on every deploy. Run BEFORE any migration that creates a
|
|
166
|
+
policy referencing the resolver: Postgres validates the reference at CREATE
|
|
167
|
+
POLICY time, so an absent resolver fails the migration with 42883.
|
|
168
|
+
|
|
169
|
+
Connection string: --db <url>, else DATABASE_URL, else DB_RLS_PG_URL.`;
|
|
170
|
+
}
|
|
171
|
+
function verifyUsage() {
|
|
172
|
+
return `usage: verify-fence [--db <url>] [--schema <name>] [--strict-only]
|
|
173
|
+
[--exempt-policies <a.b,c>] [--exempt-tables <x,y>]
|
|
174
|
+
|
|
175
|
+
Asserts from the CATALOG that every permissive policy applying to the
|
|
176
|
+
'authenticated' role resolves through auth.session_tenant_ids(), that every
|
|
177
|
+
relation carrying a tenant column can enforce RLS, and that the resolver is
|
|
178
|
+
installed, loud, and SECURITY DEFINER. Exits 1 (loud) listing every offender.
|
|
179
|
+
|
|
180
|
+
Tables are DISCOVERED from the catalog, never supplied by the caller: the
|
|
181
|
+
author who forgets ENABLE ROW LEVEL SECURITY forgets the list entry in the
|
|
182
|
+
same migration, which is why a caller-supplied list is blind to exactly the
|
|
183
|
+
tables at risk.
|
|
184
|
+
|
|
185
|
+
--strict-only additionally rejects the DEGRADED fence-only resolver. The
|
|
186
|
+
default accepts it: it is the D415-sanctioned terminal state for a service
|
|
187
|
+
with no membership mirror.
|
|
188
|
+
|
|
189
|
+
Connection string: --db <url>, else DATABASE_URL, else DB_RLS_PG_URL.`;
|
|
190
|
+
}
|
|
191
|
+
//# sourceMappingURL=fence-cli.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fence-cli.js","sourceRoot":"","sources":["../src/fence-cli.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -10,10 +10,11 @@ export { withUserContext, withUserMembershipContext, withSystemContext, withOwne
|
|
|
10
10
|
export type { OwnerContextOpts } from "./migrate-guard.js";
|
|
11
11
|
export { assertMigrateAllowed } from "./migrate-guard.js";
|
|
12
12
|
export { RlsCoverageError, verifyRls } from "./verify-rls.js";
|
|
13
|
-
export type { ReferenceTablePredicate, RlsCoverageOffender, RlsQuerier, VerifyRlsOpts, } from "./verify-rls.js";
|
|
14
|
-
export { AUTH_SCHEMA_SQL, AUTH_SCHEMA_GRANTS_SQL, applyAuthSchemaMigrations, authSchemaFullSql, } from "./auth-schema.js";
|
|
13
|
+
export type { AnyRlsQuerier, ReferenceTablePredicate, RlsCoverageOffender, RlsQuerier, RlsQuerierPostgresJs, VerifyRlsOpts, } from "./verify-rls.js";
|
|
14
|
+
export { AUTH_SCHEMA_SQL, AUTH_SCHEMA_GRANTS_SQL, applyAuthSchemaMigrations, authSchemaFullSql, SESSION_TENANT_IDS_DEGRADED_MARKER, SESSION_TENANT_IDS_DEGRADED_SQL, SESSION_TENANT_IDS_GRANTS_SQL, SESSION_TENANT_IDS_SQL, applySessionFenceBootstrap, INSTALL_SESSION_RESOLVER_SQL, SESSION_FENCE_BOOTSTRAP_SQL, } from "./auth-schema.js";
|
|
15
15
|
export type { SqlExecutor } from "./auth-schema.js";
|
|
16
|
-
export {
|
|
16
|
+
export { TENANT_FENCE_GATE_CALL_SQL, TENANT_FENCE_GATE_GRANTS_SQL, TENANT_FENCE_GATE_SQL, sessionFenceBootstrapSql, } from "./tenant-fence-gate.js";
|
|
17
|
+
export { withRls, generateTableGrants, authenticatedPredicate, SESSION_TENANT_IDS_CALL, SESSION_TENANT_IDS_SUBQUERY, withPlatformOpsTenantSql, withPlatformOpsGlobalSql, platformOpsTenantPredicateSql, platformOpsGlobalPredicateSql, platformOpsVerbClauseSql, sqlLiteral, PLATFORM_OPS_GRANTS_REPLICA_DEFAULT, referenceTableGrantsSql, findTenantColumn, ReferenceTableTenantColumnError, REFERENCE_TABLE_SKIP_RLS_MARKER, } from "./rls-policy-gen.js";
|
|
17
18
|
export type { RlsVerb, WithRlsOpts, AuthPoolMode, PlatformOpsVerb, PlatformOpsPerVerbPermissions, WithPlatformOpsSqlOpts, ReferenceTableReadRole, ReferenceTableSqlOpts, } from "./rls-policy-gen.js";
|
|
18
19
|
export { migrateGen, runMigrateGenCli, userTenantIdsRealSql, userTenantIdsStubSql, platformOpsPoliciesSql, referenceTablesSql, } from "./migrate-gen.js";
|
|
19
20
|
export type { MigrateGenInput, MigrateGenResult, PlatformOpsTableSpec, ReferenceTableSpec, } from "./migrate-gen.js";
|
|
@@ -21,6 +22,8 @@ export { generateRlsPolicy, authSchemaHelpers } from "./rls-policy.js";
|
|
|
21
22
|
export { InMemoryPgPool } from "./in-memory-pool.js";
|
|
22
23
|
export type { TranscriptEntry } from "./in-memory-pool.js";
|
|
23
24
|
export { DB_RLS_METRIC_VOCABULARY, _resetMetricsWarnLedgerForTests, } from "./metrics.js";
|
|
24
|
-
export { DbRlsConfigError, DbRlsError, DbRlsNotInitialized, MissingTenantContext, OwnerContextOutsideMigrate, PoolUnavailable, TransactionRolledBack, } from "./types.js";
|
|
25
|
+
export { DbRlsConfigError, DbRlsError, DbRlsNotInitialized, CrossTenantSessionRefused, MissingTenantContext, OwnerContextOutsideMigrate, PoolUnavailable, TransactionRolledBack, } from "./types.js";
|
|
25
26
|
export type { AuthContext, AutoDispatchOpts, DbRlsMode, GenerateRlsPolicyOpts, PgPool, PgTx, PlatformOpsContextOpts, PoolKind, SystemContextOpts, UserContextOpts, UserMembershipContextOpts, } from "./types.js";
|
|
27
|
+
export { classifyAuthPolicy, consultsMembershipUnion, isStubMembershipHelper, resolvesThroughSession, TenantFenceError, verifyTenantFence, } from "./tenant-fence.js";
|
|
28
|
+
export type { TenantFenceOffender, TenantFenceReason, VerifyTenantFenceOpts, } from "./tenant-fence.js";
|
|
26
29
|
//# sourceMappingURL=index.d.ts.map
|