@venturekit-pro/tenancy 0.0.13 → 0.0.15
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/dist/middleware/index.d.ts +1 -0
- package/dist/middleware/index.d.ts.map +1 -1
- package/dist/middleware/index.js +1 -0
- package/dist/middleware/index.js.map +1 -1
- package/dist/middleware/rls-middleware.d.ts +28 -0
- package/dist/middleware/rls-middleware.d.ts.map +1 -0
- package/dist/middleware/rls-middleware.js +89 -0
- package/dist/middleware/rls-middleware.js.map +1 -0
- package/package.json +11 -6
|
@@ -5,6 +5,7 @@ export { createTenantMiddleware, TenantNotFoundError, TenantSuspendedError, Tena
|
|
|
5
5
|
export { createQuotaMiddleware, QuotaExceededError, checkQuotas } from './quota-middleware.js';
|
|
6
6
|
export { createRuntimeTenantMiddleware, hostPrefixDomainResolver, } from './runtime-tenant-middleware.js';
|
|
7
7
|
export type { RuntimeTenantResolver, RuntimeTenantMiddlewareOptions, HostPrefixDomainResolverOptions, } from './runtime-tenant-middleware.js';
|
|
8
|
+
export { createTenantRlsMiddleware } from './rls-middleware.js';
|
|
8
9
|
export { createTenantUserScopesMiddleware, getTenantUser, requireTenantUser, } from './tenant-user-scopes-middleware.js';
|
|
9
10
|
export type { TenantUserWithRole, TenantUserResolver, TenantUserScopesMiddlewareOptions, RoleScopesLookup, } from './tenant-user-scopes-middleware.js';
|
|
10
11
|
export { TENANT_ROLES_CLAIM, TENANT_ROLES_MAX_LENGTH, packTenantRoles, unpackTenantRoles, } from './tenant-roles-claim.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/middleware/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EACL,sBAAsB,EACtB,mBAAmB,EACnB,oBAAoB,EACpB,mBAAmB,GACpB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,qBAAqB,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAC/F,OAAO,EACL,6BAA6B,EAC7B,wBAAwB,GACzB,MAAM,gCAAgC,CAAC;AACxC,YAAY,EACV,qBAAqB,EACrB,8BAA8B,EAC9B,+BAA+B,GAChC,MAAM,gCAAgC,CAAC;AACxC,OAAO,EACL,gCAAgC,EAChC,aAAa,EACb,iBAAiB,GAClB,MAAM,oCAAoC,CAAC;AAC5C,YAAY,EACV,kBAAkB,EAClB,kBAAkB,EAClB,iCAAiC,EACjC,gBAAgB,GACjB,MAAM,oCAAoC,CAAC;AAC5C,OAAO,EACL,kBAAkB,EAClB,uBAAuB,EACvB,eAAe,EACf,iBAAiB,GAClB,MAAM,yBAAyB,CAAC;AACjC,YAAY,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC/D,OAAO,EACL,4BAA4B,EAC5B,oBAAoB,GACrB,MAAM,wBAAwB,CAAC;AAChC,YAAY,EACV,qBAAqB,EACrB,6BAA6B,GAC9B,MAAM,wBAAwB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/middleware/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EACL,sBAAsB,EACtB,mBAAmB,EACnB,oBAAoB,EACpB,mBAAmB,GACpB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,qBAAqB,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAC/F,OAAO,EACL,6BAA6B,EAC7B,wBAAwB,GACzB,MAAM,gCAAgC,CAAC;AACxC,YAAY,EACV,qBAAqB,EACrB,8BAA8B,EAC9B,+BAA+B,GAChC,MAAM,gCAAgC,CAAC;AACxC,OAAO,EAAE,yBAAyB,EAAE,MAAM,qBAAqB,CAAC;AAChE,OAAO,EACL,gCAAgC,EAChC,aAAa,EACb,iBAAiB,GAClB,MAAM,oCAAoC,CAAC;AAC5C,YAAY,EACV,kBAAkB,EAClB,kBAAkB,EAClB,iCAAiC,EACjC,gBAAgB,GACjB,MAAM,oCAAoC,CAAC;AAC5C,OAAO,EACL,kBAAkB,EAClB,uBAAuB,EACvB,eAAe,EACf,iBAAiB,GAClB,MAAM,yBAAyB,CAAC;AACjC,YAAY,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC/D,OAAO,EACL,4BAA4B,EAC5B,oBAAoB,GACrB,MAAM,wBAAwB,CAAC;AAChC,YAAY,EACV,qBAAqB,EACrB,6BAA6B,GAC9B,MAAM,wBAAwB,CAAC"}
|
package/dist/middleware/index.js
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
export { createTenantMiddleware, TenantNotFoundError, TenantSuspendedError, TenantInactiveError, } from './tenant-middleware.js';
|
|
5
5
|
export { createQuotaMiddleware, QuotaExceededError, checkQuotas } from './quota-middleware.js';
|
|
6
6
|
export { createRuntimeTenantMiddleware, hostPrefixDomainResolver, } from './runtime-tenant-middleware.js';
|
|
7
|
+
export { createTenantRlsMiddleware } from './rls-middleware.js';
|
|
7
8
|
export { createTenantUserScopesMiddleware, getTenantUser, requireTenantUser, } from './tenant-user-scopes-middleware.js';
|
|
8
9
|
export { TENANT_ROLES_CLAIM, TENANT_ROLES_MAX_LENGTH, packTenantRoles, unpackTenantRoles, } from './tenant-roles-claim.js';
|
|
9
10
|
export { createTenantHandoffAuthorize, normalizeHandoffHost, } from './handoff-authorize.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/middleware/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EACL,sBAAsB,EACtB,mBAAmB,EACnB,oBAAoB,EACpB,mBAAmB,GACpB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,qBAAqB,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAC/F,OAAO,EACL,6BAA6B,EAC7B,wBAAwB,GACzB,MAAM,gCAAgC,CAAC;AAMxC,OAAO,EACL,gCAAgC,EAChC,aAAa,EACb,iBAAiB,GAClB,MAAM,oCAAoC,CAAC;AAO5C,OAAO,EACL,kBAAkB,EAClB,uBAAuB,EACvB,eAAe,EACf,iBAAiB,GAClB,MAAM,yBAAyB,CAAC;AAEjC,OAAO,EACL,4BAA4B,EAC5B,oBAAoB,GACrB,MAAM,wBAAwB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/middleware/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EACL,sBAAsB,EACtB,mBAAmB,EACnB,oBAAoB,EACpB,mBAAmB,GACpB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,qBAAqB,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAC/F,OAAO,EACL,6BAA6B,EAC7B,wBAAwB,GACzB,MAAM,gCAAgC,CAAC;AAMxC,OAAO,EAAE,yBAAyB,EAAE,MAAM,qBAAqB,CAAC;AAChE,OAAO,EACL,gCAAgC,EAChC,aAAa,EACb,iBAAiB,GAClB,MAAM,oCAAoC,CAAC;AAO5C,OAAO,EACL,kBAAkB,EAClB,uBAAuB,EACvB,eAAe,EACf,iBAAiB,GAClB,MAAM,yBAAyB,CAAC;AAEjC,OAAO,EACL,4BAA4B,EAC5B,oBAAoB,GACrB,MAAM,wBAAwB,CAAC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { Middleware, RequestContext } from '@venturekit/runtime';
|
|
2
|
+
/**
|
|
3
|
+
* Build the middleware that opens a `@venturekit/data` tenant scope for
|
|
4
|
+
* the resolved `ctx.tenant`, so Postgres RLS policies apply to every
|
|
5
|
+
* query in the request.
|
|
6
|
+
*
|
|
7
|
+
* Takes no options: the scope is derived entirely from `ctx.tenant`,
|
|
8
|
+
* and "no tenant" is not configurable because inheriting a stale scope
|
|
9
|
+
* is never the safe choice.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```ts
|
|
13
|
+
* import { handler } from '@venturekit/runtime';
|
|
14
|
+
* import {
|
|
15
|
+
* createRuntimeTenantMiddleware,
|
|
16
|
+
* createTenantRlsMiddleware,
|
|
17
|
+
* } from '@venturekit-pro/tenancy';
|
|
18
|
+
*
|
|
19
|
+
* const tenancy = createRuntimeTenantMiddleware({ resolver });
|
|
20
|
+
* const rls = createTenantRlsMiddleware();
|
|
21
|
+
*
|
|
22
|
+
* export const main = handler(async () => listPosts(), {
|
|
23
|
+
* middleware: [tenancy, rls],
|
|
24
|
+
* });
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
export declare function createTenantRlsMiddleware(): Middleware<RequestContext>;
|
|
28
|
+
//# sourceMappingURL=rls-middleware.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rls-middleware.d.ts","sourceRoot":"","sources":["../../src/middleware/rls-middleware.ts"],"names":[],"mappings":"AAoDA,OAAO,KAAK,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAEtE;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,yBAAyB,IAAI,UAAU,CAAC,cAAc,CAAC,CAStE"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Row-level-security scope middleware.
|
|
3
|
+
*
|
|
4
|
+
* Bridges the tenant this package RESOLVES (`ctx.tenant`, set by
|
|
5
|
+
* {@link createRuntimeTenantMiddleware}) to the tenant
|
|
6
|
+
* `@venturekit/data` PUBLISHES to Postgres, so RLS policies enforce
|
|
7
|
+
* isolation for every query the request makes.
|
|
8
|
+
*
|
|
9
|
+
* Without this, tenant isolation rests entirely on each query carrying
|
|
10
|
+
* a hand-written tenant predicate. That is unenforceable in practice:
|
|
11
|
+
* the common safe shape — check the tenant in one query, then act on
|
|
12
|
+
* the primary key in the next — is textually indistinguishable from
|
|
13
|
+
* the unsafe shape, so neither the compiler nor review catches an
|
|
14
|
+
* omission. With this middleware plus the policies described in
|
|
15
|
+
* `@venturekit/data`'s `query/tenant.ts`, a forgotten predicate returns
|
|
16
|
+
* zero rows instead of another tenant's data.
|
|
17
|
+
*
|
|
18
|
+
* # Placement (this ordering is load-bearing)
|
|
19
|
+
*
|
|
20
|
+
* ```ts
|
|
21
|
+
* middleware: [
|
|
22
|
+
* tenancy, // resolves ctx.tenant
|
|
23
|
+
* tenantRls(), // ← opens the RLS scope
|
|
24
|
+
* tenantUserScopes(), // QUERIES the membership row
|
|
25
|
+
* ]
|
|
26
|
+
* ```
|
|
27
|
+
*
|
|
28
|
+
* It must come AFTER tenant resolution (it reads `ctx.tenant`) and
|
|
29
|
+
* BEFORE any middleware that touches the database. In particular
|
|
30
|
+
* {@link createTenantUserScopesMiddleware} loads the caller's
|
|
31
|
+
* tenant-user row on the store-fallback path; if RLS is not yet scoped
|
|
32
|
+
* when that runs, the row is invisible, no scopes are granted, and
|
|
33
|
+
* every privileged route 403s. Ordering this wrong fails loudly rather
|
|
34
|
+
* than silently, which is the intended trade-off.
|
|
35
|
+
*
|
|
36
|
+
* # No tenant resolved
|
|
37
|
+
*
|
|
38
|
+
* Public routes and platform-level endpoints run through
|
|
39
|
+
* `runWithoutTenant()` rather than being left un-scoped. That matters:
|
|
40
|
+
* pooled connections are reused, so inheriting whichever tenant last
|
|
41
|
+
* touched the connection would be both wrong and non-deterministic.
|
|
42
|
+
* Clearing it means tenant-scoped tables return nothing — the
|
|
43
|
+
* fail-closed default. Genuinely cross-tenant work (operator tooling,
|
|
44
|
+
* sweeps over every tenant) belongs on a database role that bypasses
|
|
45
|
+
* RLS, not on an ambient scope left over from another request.
|
|
46
|
+
*
|
|
47
|
+
* Like `tenant-user-scopes-middleware.ts`, this module imports VALUES
|
|
48
|
+
* from an optional peer (`@venturekit/data`). That is deliberate and
|
|
49
|
+
* consistent: an RLS bridge is meaningless unless the app's data layer
|
|
50
|
+
* is the one publishing the GUC.
|
|
51
|
+
*/
|
|
52
|
+
import { runWithTenant, runWithoutTenant } from '@venturekit/data';
|
|
53
|
+
/**
|
|
54
|
+
* Build the middleware that opens a `@venturekit/data` tenant scope for
|
|
55
|
+
* the resolved `ctx.tenant`, so Postgres RLS policies apply to every
|
|
56
|
+
* query in the request.
|
|
57
|
+
*
|
|
58
|
+
* Takes no options: the scope is derived entirely from `ctx.tenant`,
|
|
59
|
+
* and "no tenant" is not configurable because inheriting a stale scope
|
|
60
|
+
* is never the safe choice.
|
|
61
|
+
*
|
|
62
|
+
* @example
|
|
63
|
+
* ```ts
|
|
64
|
+
* import { handler } from '@venturekit/runtime';
|
|
65
|
+
* import {
|
|
66
|
+
* createRuntimeTenantMiddleware,
|
|
67
|
+
* createTenantRlsMiddleware,
|
|
68
|
+
* } from '@venturekit-pro/tenancy';
|
|
69
|
+
*
|
|
70
|
+
* const tenancy = createRuntimeTenantMiddleware({ resolver });
|
|
71
|
+
* const rls = createTenantRlsMiddleware();
|
|
72
|
+
*
|
|
73
|
+
* export const main = handler(async () => listPosts(), {
|
|
74
|
+
* middleware: [tenancy, rls],
|
|
75
|
+
* });
|
|
76
|
+
* ```
|
|
77
|
+
*/
|
|
78
|
+
export function createTenantRlsMiddleware() {
|
|
79
|
+
return {
|
|
80
|
+
name: 'tenant-rls',
|
|
81
|
+
fn: async (ctx, next) => {
|
|
82
|
+
const tenantId = ctx.tenant?.id;
|
|
83
|
+
if (!tenantId)
|
|
84
|
+
return runWithoutTenant(next);
|
|
85
|
+
return runWithTenant(tenantId, next);
|
|
86
|
+
},
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=rls-middleware.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rls-middleware.js","sourceRoot":"","sources":["../../src/middleware/rls-middleware.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkDG;AACH,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAGnE;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAM,UAAU,yBAAyB;IACvC,OAAO;QACL,IAAI,EAAE,YAAY;QAClB,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;YACtB,MAAM,QAAQ,GAAG,GAAG,CAAC,MAAM,EAAE,EAAE,CAAC;YAChC,IAAI,CAAC,QAAQ;gBAAE,OAAO,gBAAgB,CAAC,IAAI,CAAC,CAAC;YAC7C,OAAO,aAAa,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;QACvC,CAAC;KACF,CAAC;AACJ,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@venturekit-pro/tenancy",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.15",
|
|
4
4
|
"description": "Multi-tenant utilities for VentureKit",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -25,24 +25,29 @@
|
|
|
25
25
|
"licenseHeader": "VentureKit Pro Commercial License — production use requires a valid VentureKit Pro license key. See https://venturekit.dev/pricing.",
|
|
26
26
|
"exports": {
|
|
27
27
|
".": {
|
|
28
|
-
"
|
|
29
|
-
"
|
|
28
|
+
"types": "./dist/index.d.ts",
|
|
29
|
+
"import": "./dist/index.js"
|
|
30
30
|
}
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"@venturekit/core": "0.0.
|
|
33
|
+
"@venturekit/core": "0.0.15"
|
|
34
34
|
},
|
|
35
35
|
"peerDependencies": {
|
|
36
|
-
"@venturekit/
|
|
36
|
+
"@venturekit/data": "0.0.15",
|
|
37
|
+
"@venturekit/runtime": "0.0.15"
|
|
37
38
|
},
|
|
38
39
|
"peerDependenciesMeta": {
|
|
40
|
+
"@venturekit/data": {
|
|
41
|
+
"optional": true
|
|
42
|
+
},
|
|
39
43
|
"@venturekit/runtime": {
|
|
40
44
|
"optional": true
|
|
41
45
|
}
|
|
42
46
|
},
|
|
43
47
|
"devDependencies": {
|
|
44
48
|
"@types/node": "^26.1.0",
|
|
45
|
-
"@venturekit/
|
|
49
|
+
"@venturekit/data": "0.0.15",
|
|
50
|
+
"@venturekit/runtime": "0.0.15",
|
|
46
51
|
"typescript": "^5.3.0"
|
|
47
52
|
},
|
|
48
53
|
"scripts": {
|