@xemahq/biome-database-nest 0.5.0 → 0.5.2
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/README.md +67 -0
- package/dist/index.d.ts +6 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +16 -1
- package/dist/index.js.map +1 -1
- package/dist/lib/bootstrap.d.ts.map +1 -1
- package/dist/lib/bootstrap.js +20 -1
- package/dist/lib/bootstrap.js.map +1 -1
- package/dist/lib/config.d.ts +3 -0
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +11 -0
- package/dist/lib/config.js.map +1 -1
- package/dist/lib/nest/prisma-service-factory.d.ts +16 -1
- package/dist/lib/nest/prisma-service-factory.d.ts.map +1 -1
- package/dist/lib/nest/prisma-service-factory.js +105 -1
- package/dist/lib/nest/prisma-service-factory.js.map +1 -1
- package/dist/lib/tenant-isolation/org-scope.d.ts +33 -0
- package/dist/lib/tenant-isolation/org-scope.d.ts.map +1 -0
- package/dist/lib/tenant-isolation/org-scope.js +172 -0
- package/dist/lib/tenant-isolation/org-scope.js.map +1 -0
- package/dist/lib/tenant-isolation/tenant-isolation-error.d.ts +20 -0
- package/dist/lib/tenant-isolation/tenant-isolation-error.d.ts.map +1 -0
- package/dist/lib/tenant-isolation/tenant-isolation-error.js +39 -0
- package/dist/lib/tenant-isolation/tenant-isolation-error.js.map +1 -0
- package/dist/lib/tenant-isolation/tenant-isolation-mode.d.ts +8 -0
- package/dist/lib/tenant-isolation/tenant-isolation-mode.d.ts.map +1 -0
- package/dist/lib/tenant-isolation/tenant-isolation-mode.js +30 -0
- package/dist/lib/tenant-isolation/tenant-isolation-mode.js.map +1 -0
- package/dist/lib/tenant-isolation/tenant-isolation.extension.d.ts +30 -0
- package/dist/lib/tenant-isolation/tenant-isolation.extension.d.ts.map +1 -0
- package/dist/lib/tenant-isolation/tenant-isolation.extension.js +94 -0
- package/dist/lib/tenant-isolation/tenant-isolation.extension.js.map +1 -0
- package/package.json +14 -5
package/README.md
CHANGED
|
@@ -50,6 +50,73 @@ no DB boilerplate. It provides:
|
|
|
50
50
|
- **Boot-time bootstrap.** `bootstrapBiomeDatabases` resolves config, writes an
|
|
51
51
|
inline CA cert if supplied, creates the schema under an advisory lock, runs
|
|
52
52
|
`prisma migrate deploy`, and exports the resolved connection into the env.
|
|
53
|
+
- **Systematic org-tenant isolation.** `createBiomePrismaService` detects
|
|
54
|
+
org-scoped models (any model with an `orgId` column) from the client's
|
|
55
|
+
runtime data model and scopes every query on them to the request's org via a
|
|
56
|
+
Prisma client extension — no hand-written `where: { orgId }` per service.
|
|
57
|
+
|
|
58
|
+
## Tenant isolation
|
|
59
|
+
|
|
60
|
+
Org-scoped access is pushed down into the client so it is systematic instead of
|
|
61
|
+
hand-written. Any model declaring the org column (default `orgId`; configurable
|
|
62
|
+
via `tenantIsolation.orgField`, opt-outs via `tenantIsolation.exemptModels`) is
|
|
63
|
+
detected at client construction and covered by the isolation extension.
|
|
64
|
+
|
|
65
|
+
### Modes
|
|
66
|
+
|
|
67
|
+
Resolution: `tenantIsolation.mode` option > `XEMA_TENANT_ISOLATION_MODE` env
|
|
68
|
+
var > default `observe`. An invalid env value fails the boot — it never
|
|
69
|
+
silently weakens the mode.
|
|
70
|
+
|
|
71
|
+
| Mode | Behavior of the default injected client |
|
|
72
|
+
| --- | --- |
|
|
73
|
+
| `observe` (default) | NO query is rewritten. Every violation `enforce` would reject (org mismatch in a write payload, org-model access with no org context) is logged as a structured warning. |
|
|
74
|
+
| `enforce` | `where` clauses are AND-ed with `{ orgId }` (unique wheres included — a cross-org `findUnique` returns `null`, a cross-org `update`/`delete` raises P2025); create payloads are stamped with the org; violations throw a typed `TenantIsolationError`. |
|
|
75
|
+
| `off` | Passthrough kill switch. `forOrg()` still enforces — explicit scoping is never silently degraded. |
|
|
76
|
+
|
|
77
|
+
The **rollout story** is deliberate: everything ships in `observe` (zero
|
|
78
|
+
behavior change across ~60 services), each service burns down its warnings,
|
|
79
|
+
then flips itself to `enforce` via `XEMA_TENANT_ISOLATION_MODE=enforce` (or the
|
|
80
|
+
module option). `enforce` is the target state; `off` exists only as an
|
|
81
|
+
emergency kill switch.
|
|
82
|
+
|
|
83
|
+
### Where the org comes from
|
|
84
|
+
|
|
85
|
+
Inside a request, the org is the `RequestContext.orgId` that
|
|
86
|
+
`RequestContextMiddleware` resolved. `AmbientOrgContextMiddleware`
|
|
87
|
+
(`@xemahq/platform-common`) runs right after it and lifts the org into the
|
|
88
|
+
ambient async scope the extension reads. Services wired through
|
|
89
|
+
`configureAuthMiddleware` / `applyXemaServiceMiddleware` get BOTH middlewares
|
|
90
|
+
automatically — no per-service wiring is needed.
|
|
91
|
+
|
|
92
|
+
### The boot-path rule
|
|
93
|
+
|
|
94
|
+
Outside a request (boot, migrations, seeding, contribution sync, Temporal
|
|
95
|
+
activities, background workers) there is NO ambient org. In `enforce` mode,
|
|
96
|
+
touching an org-scoped model there throws. Scope explicitly:
|
|
97
|
+
|
|
98
|
+
```ts
|
|
99
|
+
// Jobs/workers acting for one org — hard-bound, always enforced:
|
|
100
|
+
const scoped = prisma.forOrg(orgId);
|
|
101
|
+
await scoped.artifact.findMany(); // implicitly WHERE orgId = <orgId>
|
|
102
|
+
|
|
103
|
+
// Legitimate cross-org system paths (boot backfills, platform admin):
|
|
104
|
+
const system = prisma.asSystem(); // unscoped; debug-logged per call site
|
|
105
|
+
|
|
106
|
+
// Non-HTTP entrypoints that DO have an org (queue consumers, activities):
|
|
107
|
+
// runWithAmbientOrg comes from @xemahq/platform-common.
|
|
108
|
+
await runWithAmbientOrg(orgId, () => handler());
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
`forOrg(orgId)` always enforces, in every mode. `asSystem()` returns the raw
|
|
112
|
+
client and logs each distinct call site once at debug level.
|
|
113
|
+
|
|
114
|
+
### Conformance testing
|
|
115
|
+
|
|
116
|
+
`@xemahq/biome-test-utils` exports `assertTenantIsolation(client, { orgA,
|
|
117
|
+
orgB, models })` — given the wrapped client and a list of org-scoped model
|
|
118
|
+
probes, it verifies cross-org reads return empty/null, cross-org writes throw,
|
|
119
|
+
and `asSystem()` sees both orgs, cleaning up after itself.
|
|
53
120
|
|
|
54
121
|
## When to use it
|
|
55
122
|
|
package/dist/index.d.ts
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
|
-
export { DEFAULT_BIOME_DB_KEY, deriveBiomeSchemaName
|
|
1
|
+
export { DEFAULT_BIOME_DB_KEY, deriveBiomeSchemaName } from './lib/schema-name';
|
|
2
2
|
export { resolveBiomeDatabaseConfigFromEnv, type BiomeDatabaseConfig, } from './lib/config';
|
|
3
3
|
export { createBiomePrismaAdapter, createBiomePrismaAdapterFromEnv, } from './lib/adapter';
|
|
4
4
|
export { fetchSystemDatabaseConfig, type SystemDatabaseConfigRequest, } from './lib/control-plane-client';
|
|
5
5
|
export { resolveControlPlaneBaseUrl, ORG_DATABASE_POOL_SERVICE_NAME, type ControlPlaneDiscoveryOptions, } from './lib/control-plane-discovery';
|
|
6
6
|
export { bootstrapBiomeDatabases, type BiomeDatabaseDeclaration, type BootstrapBiomeDatabasesOptions, } from './lib/bootstrap';
|
|
7
7
|
export { BiomeDatabaseModule, BIOME_DATABASE_READY, type BiomeDatabaseModuleOptions, } from './lib/nest/biome-database.module';
|
|
8
|
-
export { createBiomePrismaService, type BiomePrismaClient, type BiomePrismaClientCtor, } from './lib/nest/prisma-service-factory';
|
|
8
|
+
export { createBiomePrismaService, type BiomePrismaClient, type BiomePrismaClientCtor, type BiomePrismaServiceInstance, type CreateBiomePrismaServiceOptions, type TenantIsolationServiceOptions, type TenantScopedApi, } from './lib/nest/prisma-service-factory';
|
|
9
|
+
export { TenantIsolationMode, TENANT_ISOLATION_MODE_ENV_VAR, resolveTenantIsolationMode, } from './lib/tenant-isolation/tenant-isolation-mode';
|
|
10
|
+
export { TenantIsolationError, TenantIsolationViolationKind, formatTenantIsolationViolation, type TenantIsolationViolation, } from './lib/tenant-isolation/tenant-isolation-error';
|
|
11
|
+
export { PrismaModelOperation, scopeArgsToOrg, type OrgScopeOutcome, type OrgScopeRequest, } from './lib/tenant-isolation/org-scope';
|
|
12
|
+
export { DEFAULT_ORG_FIELD, collectOrgScopedModels, createTenantIsolationExtension, type CreateTenantIsolationExtensionOptions, type TenantIsolationExtensionDefinition, type TenantIsolationLogger, type TenantIsolationQueryContext, } from './lib/tenant-isolation/tenant-isolation.extension';
|
|
9
13
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AAChF,OAAO,EACL,iCAAiC,EACjC,KAAK,mBAAmB,GACzB,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,wBAAwB,EACxB,+BAA+B,GAChC,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,yBAAyB,EACzB,KAAK,2BAA2B,GACjC,MAAM,4BAA4B,CAAC;AACpC,OAAO,EACL,0BAA0B,EAC1B,8BAA8B,EAC9B,KAAK,4BAA4B,GAClC,MAAM,+BAA+B,CAAC;AACvC,OAAO,EACL,uBAAuB,EACvB,KAAK,wBAAwB,EAC7B,KAAK,8BAA8B,GACpC,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,mBAAmB,EACnB,oBAAoB,EACpB,KAAK,0BAA0B,GAChC,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EACL,wBAAwB,EACxB,KAAK,iBAAiB,EACtB,KAAK,qBAAqB,EAC1B,KAAK,0BAA0B,EAC/B,KAAK,+BAA+B,EACpC,KAAK,6BAA6B,EAClC,KAAK,eAAe,GACrB,MAAM,mCAAmC,CAAC;AAC3C,OAAO,EACL,mBAAmB,EACnB,6BAA6B,EAC7B,0BAA0B,GAC3B,MAAM,8CAA8C,CAAC;AACtD,OAAO,EACL,oBAAoB,EACpB,4BAA4B,EAC5B,8BAA8B,EAC9B,KAAK,wBAAwB,GAC9B,MAAM,+CAA+C,CAAC;AACvD,OAAO,EACL,oBAAoB,EACpB,cAAc,EACd,KAAK,eAAe,EACpB,KAAK,eAAe,GACrB,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EACL,iBAAiB,EACjB,sBAAsB,EACtB,8BAA8B,EAC9B,KAAK,qCAAqC,EAC1C,KAAK,kCAAkC,EACvC,KAAK,qBAAqB,EAC1B,KAAK,2BAA2B,GACjC,MAAM,mDAAmD,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.createBiomePrismaService = exports.BIOME_DATABASE_READY = exports.BiomeDatabaseModule = exports.bootstrapBiomeDatabases = exports.ORG_DATABASE_POOL_SERVICE_NAME = exports.resolveControlPlaneBaseUrl = exports.fetchSystemDatabaseConfig = exports.createBiomePrismaAdapterFromEnv = exports.createBiomePrismaAdapter = exports.resolveBiomeDatabaseConfigFromEnv = exports.deriveBiomeSchemaName = exports.DEFAULT_BIOME_DB_KEY = void 0;
|
|
3
|
+
exports.createTenantIsolationExtension = exports.collectOrgScopedModels = exports.DEFAULT_ORG_FIELD = exports.scopeArgsToOrg = exports.PrismaModelOperation = exports.formatTenantIsolationViolation = exports.TenantIsolationViolationKind = exports.TenantIsolationError = exports.resolveTenantIsolationMode = exports.TENANT_ISOLATION_MODE_ENV_VAR = exports.TenantIsolationMode = exports.createBiomePrismaService = exports.BIOME_DATABASE_READY = exports.BiomeDatabaseModule = exports.bootstrapBiomeDatabases = exports.ORG_DATABASE_POOL_SERVICE_NAME = exports.resolveControlPlaneBaseUrl = exports.fetchSystemDatabaseConfig = exports.createBiomePrismaAdapterFromEnv = exports.createBiomePrismaAdapter = exports.resolveBiomeDatabaseConfigFromEnv = exports.deriveBiomeSchemaName = exports.DEFAULT_BIOME_DB_KEY = void 0;
|
|
4
4
|
var schema_name_1 = require("./lib/schema-name");
|
|
5
5
|
Object.defineProperty(exports, "DEFAULT_BIOME_DB_KEY", { enumerable: true, get: function () { return schema_name_1.DEFAULT_BIOME_DB_KEY; } });
|
|
6
6
|
Object.defineProperty(exports, "deriveBiomeSchemaName", { enumerable: true, get: function () { return schema_name_1.deriveBiomeSchemaName; } });
|
|
@@ -21,4 +21,19 @@ Object.defineProperty(exports, "BiomeDatabaseModule", { enumerable: true, get: f
|
|
|
21
21
|
Object.defineProperty(exports, "BIOME_DATABASE_READY", { enumerable: true, get: function () { return biome_database_module_1.BIOME_DATABASE_READY; } });
|
|
22
22
|
var prisma_service_factory_1 = require("./lib/nest/prisma-service-factory");
|
|
23
23
|
Object.defineProperty(exports, "createBiomePrismaService", { enumerable: true, get: function () { return prisma_service_factory_1.createBiomePrismaService; } });
|
|
24
|
+
var tenant_isolation_mode_1 = require("./lib/tenant-isolation/tenant-isolation-mode");
|
|
25
|
+
Object.defineProperty(exports, "TenantIsolationMode", { enumerable: true, get: function () { return tenant_isolation_mode_1.TenantIsolationMode; } });
|
|
26
|
+
Object.defineProperty(exports, "TENANT_ISOLATION_MODE_ENV_VAR", { enumerable: true, get: function () { return tenant_isolation_mode_1.TENANT_ISOLATION_MODE_ENV_VAR; } });
|
|
27
|
+
Object.defineProperty(exports, "resolveTenantIsolationMode", { enumerable: true, get: function () { return tenant_isolation_mode_1.resolveTenantIsolationMode; } });
|
|
28
|
+
var tenant_isolation_error_1 = require("./lib/tenant-isolation/tenant-isolation-error");
|
|
29
|
+
Object.defineProperty(exports, "TenantIsolationError", { enumerable: true, get: function () { return tenant_isolation_error_1.TenantIsolationError; } });
|
|
30
|
+
Object.defineProperty(exports, "TenantIsolationViolationKind", { enumerable: true, get: function () { return tenant_isolation_error_1.TenantIsolationViolationKind; } });
|
|
31
|
+
Object.defineProperty(exports, "formatTenantIsolationViolation", { enumerable: true, get: function () { return tenant_isolation_error_1.formatTenantIsolationViolation; } });
|
|
32
|
+
var org_scope_1 = require("./lib/tenant-isolation/org-scope");
|
|
33
|
+
Object.defineProperty(exports, "PrismaModelOperation", { enumerable: true, get: function () { return org_scope_1.PrismaModelOperation; } });
|
|
34
|
+
Object.defineProperty(exports, "scopeArgsToOrg", { enumerable: true, get: function () { return org_scope_1.scopeArgsToOrg; } });
|
|
35
|
+
var tenant_isolation_extension_1 = require("./lib/tenant-isolation/tenant-isolation.extension");
|
|
36
|
+
Object.defineProperty(exports, "DEFAULT_ORG_FIELD", { enumerable: true, get: function () { return tenant_isolation_extension_1.DEFAULT_ORG_FIELD; } });
|
|
37
|
+
Object.defineProperty(exports, "collectOrgScopedModels", { enumerable: true, get: function () { return tenant_isolation_extension_1.collectOrgScopedModels; } });
|
|
38
|
+
Object.defineProperty(exports, "createTenantIsolationExtension", { enumerable: true, get: function () { return tenant_isolation_extension_1.createTenantIsolationExtension; } });
|
|
24
39
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,iDAAgF;AAAvE,mHAAA,oBAAoB,OAAA;AAAE,oHAAA,qBAAqB,OAAA;AACpD,uCAGsB;AAFpB,2HAAA,iCAAiC,OAAA;AAGnC,yCAGuB;AAFrB,mHAAA,wBAAwB,OAAA;AACxB,0HAAA,+BAA+B,OAAA;AAEjC,mEAGoC;AAFlC,iIAAA,yBAAyB,OAAA;AAG3B,yEAIuC;AAHrC,qIAAA,0BAA0B,OAAA;AAC1B,yIAAA,8BAA8B,OAAA;AAGhC,6CAIyB;AAHvB,oHAAA,uBAAuB,OAAA;AAIzB,0EAI0C;AAHxC,4HAAA,mBAAmB,OAAA;AACnB,6HAAA,oBAAoB,OAAA;AAGtB,4EAQ2C;AAPzC,kIAAA,wBAAwB,OAAA;AAQ1B,sFAIsD;AAHpD,4HAAA,mBAAmB,OAAA;AACnB,sIAAA,6BAA6B,OAAA;AAC7B,mIAAA,0BAA0B,OAAA;AAE5B,wFAKuD;AAJrD,8HAAA,oBAAoB,OAAA;AACpB,sIAAA,4BAA4B,OAAA;AAC5B,wIAAA,8BAA8B,OAAA;AAGhC,8DAK0C;AAJxC,iHAAA,oBAAoB,OAAA;AACpB,2GAAA,cAAc,OAAA;AAIhB,gGAQ2D;AAPzD,+HAAA,iBAAiB,OAAA;AACjB,oIAAA,sBAAsB,OAAA;AACtB,4IAAA,8BAA8B,OAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bootstrap.d.ts","sourceRoot":"","sources":["../../src/lib/bootstrap.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uCAAuC,CAAC;AAWzE,OAAO,EAEL,KAAK,4BAA4B,EAClC,MAAM,2BAA2B,CAAC;AAInC,MAAM,WAAW,wBAAwB;IAEvC,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IAEtB,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;CAC/B;AAED,MAAM,WAAW,8BAA8B;IAC7C,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,SAAS,EAAE,SAAS,wBAAwB,EAAE,CAAC;IAQxD,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,GAAG,CAAC,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;IAQzD,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAC;IAElC,QAAQ,CAAC,qBAAqB,CAAC,EAAE,4BAA4B,CAAC;IAW9D,QAAQ,CAAC,kBAAkB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACjD;
|
|
1
|
+
{"version":3,"file":"bootstrap.d.ts","sourceRoot":"","sources":["../../src/lib/bootstrap.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uCAAuC,CAAC;AAWzE,OAAO,EAEL,KAAK,4BAA4B,EAClC,MAAM,2BAA2B,CAAC;AAInC,MAAM,WAAW,wBAAwB;IAEvC,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IAEtB,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;CAC/B;AAED,MAAM,WAAW,8BAA8B;IAC7C,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,SAAS,EAAE,SAAS,wBAAwB,EAAE,CAAC;IAQxD,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,GAAG,CAAC,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;IAQzD,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAC;IAElC,QAAQ,CAAC,qBAAqB,CAAC,EAAE,4BAA4B,CAAC;IAW9D,QAAQ,CAAC,kBAAkB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACjD;AAgSD,wBAAsB,uBAAuB,CAC3C,IAAI,EAAE,8BAA8B,GACnC,OAAO,CAAC,IAAI,CAAC,CAmDf"}
|
package/dist/lib/bootstrap.js
CHANGED
|
@@ -136,6 +136,25 @@ async function runMigrations(biomeId, key, workspaceDir, config) {
|
|
|
136
136
|
}
|
|
137
137
|
return migrateUrl;
|
|
138
138
|
}
|
|
139
|
+
function buildRuntimeUrl(biomeId, key, config, directMigrateUrl) {
|
|
140
|
+
if (config.poolerHost === undefined || config.poolerHost === '') {
|
|
141
|
+
return (0, platform_common_1.ensureBiomeDbSearchPath)(directMigrateUrl);
|
|
142
|
+
}
|
|
143
|
+
const pooledUrl = (0, platform_common_1.composeBiomeDatabaseUrl)({
|
|
144
|
+
user: config.username,
|
|
145
|
+
password: config.password,
|
|
146
|
+
host: config.poolerHost,
|
|
147
|
+
port: String(config.poolerPort ?? config.port),
|
|
148
|
+
database: config.database,
|
|
149
|
+
...(config.schema !== undefined ? { schema: config.schema } : {}),
|
|
150
|
+
sslParams: config.poolerSslParams ?? 'sslmode=disable',
|
|
151
|
+
});
|
|
152
|
+
if (pooledUrl === null) {
|
|
153
|
+
throw new Error(`${LOG_PREFIX} biome=${biomeId} key=${key}: incomplete config — cannot form a pooled runtime URL.`);
|
|
154
|
+
}
|
|
155
|
+
console.log(`${LOG_PREFIX} biome=${biomeId} key=${key}: runtime routed through pooler ${config.poolerHost}:${config.poolerPort ?? config.port} (migrations stay direct)`);
|
|
156
|
+
return (0, platform_common_1.ensureBiomeDbSearchPath)(pooledUrl);
|
|
157
|
+
}
|
|
139
158
|
function exportResolvedEnv(key, config, runtimeUrl) {
|
|
140
159
|
const suffix = upperKeySuffix(key);
|
|
141
160
|
process.env[`DATABASE_URL${suffix}`] = runtimeUrl;
|
|
@@ -158,7 +177,7 @@ async function bootstrapBiomeDatabases(opts) {
|
|
|
158
177
|
const config = await materializeInlineCaCert(opts.biomeId, key, rawConfig);
|
|
159
178
|
await ensureSchemaExists(opts.biomeId, key, config, opts.requiredExtensions ?? []);
|
|
160
179
|
const migrateUrl = await runMigrations(opts.biomeId, key, decl.workspaceDir, config);
|
|
161
|
-
const runtimeUrl = (
|
|
180
|
+
const runtimeUrl = buildRuntimeUrl(opts.biomeId, key, config, migrateUrl);
|
|
162
181
|
exportResolvedEnv(key, config, runtimeUrl);
|
|
163
182
|
console.log(`${LOG_PREFIX} biome=${opts.biomeId} key=${key}: bootstrap complete`);
|
|
164
183
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bootstrap.js","sourceRoot":"","sources":["../../src/lib/bootstrap.ts"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"bootstrap.js","sourceRoot":"","sources":["../../src/lib/bootstrap.ts"],"names":[],"mappings":"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"}
|
package/dist/lib/config.d.ts
CHANGED
|
@@ -8,6 +8,9 @@ export interface BiomeDatabaseConfig {
|
|
|
8
8
|
readonly sslParams?: string;
|
|
9
9
|
readonly caCertPem?: string;
|
|
10
10
|
readonly poolMax?: number;
|
|
11
|
+
readonly poolerHost?: string;
|
|
12
|
+
readonly poolerPort?: number;
|
|
13
|
+
readonly poolerSslParams?: string;
|
|
11
14
|
}
|
|
12
15
|
export declare function resolveBiomeDatabaseConfigFromEnv(env?: NodeJS.ProcessEnv, dbKey?: string): BiomeDatabaseConfig | null;
|
|
13
16
|
//# sourceMappingURL=config.d.ts.map
|
package/dist/lib/config.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/lib/config.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAE1B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAE1B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAE5B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/lib/config.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAE1B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAE1B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAE5B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAe1B,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAE7B,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAM7B,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;CACnC;AA8CD,wBAAgB,iCAAiC,CAC/C,GAAG,GAAE,MAAM,CAAC,UAAwB,EACpC,KAAK,CAAC,EAAE,MAAM,GACb,mBAAmB,GAAG,IAAI,CA6E5B"}
|
package/dist/lib/config.js
CHANGED
|
@@ -49,6 +49,14 @@ function resolveBiomeDatabaseConfigFromEnv(env = process.env, dbKey) {
|
|
|
49
49
|
throw new Error(`Invalid DB_PORT${suffix} value "${portRaw}" — expected a positive integer.`);
|
|
50
50
|
}
|
|
51
51
|
const sslParams = readDbVar(env, 'DB_SSL_PARAMS', suffix, true);
|
|
52
|
+
const poolerHost = readDbVar(env, 'DB_POOLER_HOST', suffix, true);
|
|
53
|
+
const poolerPortRaw = readDbVar(env, 'DB_POOLER_PORT', suffix, true);
|
|
54
|
+
const poolerPort = poolerPortRaw !== undefined ? Number.parseInt(poolerPortRaw, 10) : undefined;
|
|
55
|
+
if (poolerPortRaw !== undefined &&
|
|
56
|
+
(!Number.isInteger(poolerPort) || poolerPort <= 0)) {
|
|
57
|
+
throw new Error(`Invalid DB_POOLER_PORT${suffix} value "${poolerPortRaw}" — expected a positive integer.`);
|
|
58
|
+
}
|
|
59
|
+
const poolerSslParams = readDbVar(env, 'DB_POOLER_SSL_PARAMS', suffix, true);
|
|
52
60
|
const poolMaxRaw = readDbVar(env, 'DB_POOL_MAX', suffix, true);
|
|
53
61
|
const poolMax = poolMaxRaw !== undefined ? Number.parseInt(poolMaxRaw, 10) : undefined;
|
|
54
62
|
if (poolMaxRaw !== undefined && (!Number.isInteger(poolMax) || poolMax <= 0)) {
|
|
@@ -63,6 +71,9 @@ function resolveBiomeDatabaseConfigFromEnv(env = process.env, dbKey) {
|
|
|
63
71
|
...(schema !== undefined ? { schema } : {}),
|
|
64
72
|
...(sslParams !== undefined ? { sslParams } : {}),
|
|
65
73
|
...(poolMax !== undefined ? { poolMax } : {}),
|
|
74
|
+
...(poolerHost !== undefined ? { poolerHost } : {}),
|
|
75
|
+
...(poolerPort !== undefined ? { poolerPort } : {}),
|
|
76
|
+
...(poolerSslParams !== undefined ? { poolerSslParams } : {}),
|
|
66
77
|
};
|
|
67
78
|
}
|
|
68
79
|
//# sourceMappingURL=config.js.map
|
package/dist/lib/config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/lib/config.ts"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/lib/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,8 +1,23 @@
|
|
|
1
1
|
import type { OnModuleDestroy, OnModuleInit, Type } from '@nestjs/common';
|
|
2
|
+
import { TenantIsolationMode } from '../tenant-isolation/tenant-isolation-mode';
|
|
2
3
|
export type BiomePrismaClientCtor<TClient extends BiomePrismaClient> = new (...args: any[]) => TClient;
|
|
3
4
|
export interface BiomePrismaClient {
|
|
4
5
|
$connect(): Promise<void>;
|
|
5
6
|
$disconnect(): Promise<void>;
|
|
6
7
|
}
|
|
7
|
-
export
|
|
8
|
+
export interface TenantIsolationServiceOptions {
|
|
9
|
+
readonly mode?: TenantIsolationMode;
|
|
10
|
+
readonly orgField?: string;
|
|
11
|
+
readonly exemptModels?: readonly string[];
|
|
12
|
+
}
|
|
13
|
+
export interface CreateBiomePrismaServiceOptions {
|
|
14
|
+
readonly loggerContext?: string;
|
|
15
|
+
readonly tenantIsolation?: TenantIsolationServiceOptions;
|
|
16
|
+
}
|
|
17
|
+
export interface TenantScopedApi<TClient> {
|
|
18
|
+
forOrg(orgId: string): TClient;
|
|
19
|
+
asSystem(): TClient;
|
|
20
|
+
}
|
|
21
|
+
export type BiomePrismaServiceInstance<TClient> = TClient & TenantScopedApi<TClient> & OnModuleInit & OnModuleDestroy;
|
|
22
|
+
export declare function createBiomePrismaService<TClient extends BiomePrismaClient>(PrismaClientCtor: BiomePrismaClientCtor<TClient>, optionsOrLoggerContext?: string | CreateBiomePrismaServiceOptions): Type<BiomePrismaServiceInstance<TClient>>;
|
|
8
23
|
//# sourceMappingURL=prisma-service-factory.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prisma-service-factory.d.ts","sourceRoot":"","sources":["../../../src/lib/nest/prisma-service-factory.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"prisma-service-factory.d.ts","sourceRoot":"","sources":["../../../src/lib/nest/prisma-service-factory.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAC;AAK1E,OAAO,EACL,mBAAmB,EAEpB,MAAM,2CAA2C,CAAC;AAkBnD,MAAM,MAAM,qBAAqB,CAAC,OAAO,SAAS,iBAAiB,IAAI,KAIrE,GAAG,IAAI,EAAE,GAAG,EAAE,KACX,OAAO,CAAC;AAGb,MAAM,WAAW,iBAAiB;IAChC,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1B,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9B;AAGD,MAAM,WAAW,6BAA6B;IAK5C,QAAQ,CAAC,IAAI,CAAC,EAAE,mBAAmB,CAAC;IAEpC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAK3B,QAAQ,CAAC,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAC3C;AAGD,MAAM,WAAW,+BAA+B;IAE9C,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,eAAe,CAAC,EAAE,6BAA6B,CAAC;CAC1D;AAMD,MAAM,WAAW,eAAe,CAAC,OAAO;IAOtC,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;IAM/B,QAAQ,IAAI,OAAO,CAAC;CACrB;AAGD,MAAM,MAAM,0BAA0B,CAAC,OAAO,IAAI,OAAO,GACvD,eAAe,CAAC,OAAO,CAAC,GACxB,YAAY,GACZ,eAAe,CAAC;AA2ElB,wBAAgB,wBAAwB,CAAC,OAAO,SAAS,iBAAiB,EACxE,gBAAgB,EAAE,qBAAqB,CAAC,OAAO,CAAC,EAChD,sBAAsB,CAAC,EAAE,MAAM,GAAG,+BAA+B,GAChE,IAAI,CAAC,0BAA0B,CAAC,OAAO,CAAC,CAAC,CAmK3C"}
|
|
@@ -11,12 +11,116 @@ var __metadata = (this && this.__metadata) || function (k, v) {
|
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
exports.createBiomePrismaService = createBiomePrismaService;
|
|
13
13
|
const common_1 = require("@nestjs/common");
|
|
14
|
+
const platform_common_1 = require("@xemahq/platform-common");
|
|
14
15
|
const adapter_1 = require("../adapter");
|
|
15
|
-
|
|
16
|
+
const tenant_isolation_mode_1 = require("../tenant-isolation/tenant-isolation-mode");
|
|
17
|
+
const tenant_isolation_extension_1 = require("../tenant-isolation/tenant-isolation.extension");
|
|
18
|
+
function lowerFirst(name) {
|
|
19
|
+
return name.length === 0
|
|
20
|
+
? name
|
|
21
|
+
: name.charAt(0).toLowerCase() + name.slice(1);
|
|
22
|
+
}
|
|
23
|
+
function normalizeOptions(optionsOrLoggerContext) {
|
|
24
|
+
if (optionsOrLoggerContext === undefined) {
|
|
25
|
+
return {};
|
|
26
|
+
}
|
|
27
|
+
if (typeof optionsOrLoggerContext === 'string') {
|
|
28
|
+
return { loggerContext: optionsOrLoggerContext };
|
|
29
|
+
}
|
|
30
|
+
return optionsOrLoggerContext;
|
|
31
|
+
}
|
|
32
|
+
function createBiomePrismaService(PrismaClientCtor, optionsOrLoggerContext) {
|
|
33
|
+
const options = normalizeOptions(optionsOrLoggerContext);
|
|
34
|
+
const loggerContext = options.loggerContext ?? 'PrismaService';
|
|
35
|
+
const tenantOptions = options.tenantIsolation ?? {};
|
|
36
|
+
const orgField = tenantOptions.orgField ?? tenant_isolation_extension_1.DEFAULT_ORG_FIELD;
|
|
37
|
+
const exemptModels = tenantOptions.exemptModels ?? [];
|
|
16
38
|
let BiomePrismaService = class BiomePrismaService extends PrismaClientCtor {
|
|
17
39
|
biomePrismaLogger = new common_1.Logger(loggerContext);
|
|
18
40
|
constructor() {
|
|
19
41
|
super({ adapter: (0, adapter_1.createBiomePrismaAdapterFromEnv)() });
|
|
42
|
+
const mode = (0, tenant_isolation_mode_1.resolveTenantIsolationMode)(tenantOptions.mode);
|
|
43
|
+
const raw = this;
|
|
44
|
+
const tenantLogger = new common_1.Logger(`${loggerContext}:tenant-isolation`);
|
|
45
|
+
const requireExtends = () => {
|
|
46
|
+
const extendsFn = raw.$extends;
|
|
47
|
+
if (typeof extendsFn !== 'function') {
|
|
48
|
+
throw new Error('[tenant-isolation] the Prisma client has no $extends — tenant ' +
|
|
49
|
+
'isolation requires a generated @prisma/client >= 5 (this SDK peers on ^7).');
|
|
50
|
+
}
|
|
51
|
+
return extendsFn.bind(raw);
|
|
52
|
+
};
|
|
53
|
+
let scopedModelsCache;
|
|
54
|
+
const getScopedModels = () => {
|
|
55
|
+
scopedModelsCache ??= (0, tenant_isolation_extension_1.collectOrgScopedModels)(raw, orgField, exemptModels);
|
|
56
|
+
return scopedModelsCache;
|
|
57
|
+
};
|
|
58
|
+
const forOrg = (orgId) => {
|
|
59
|
+
if (typeof orgId !== 'string' || orgId === '') {
|
|
60
|
+
throw new Error('[tenant-isolation] forOrg requires a non-empty orgId. For a ' +
|
|
61
|
+
'legitimate cross-org system path use asSystem().');
|
|
62
|
+
}
|
|
63
|
+
const orgScopedModels = getScopedModels();
|
|
64
|
+
return requireExtends()((0, tenant_isolation_extension_1.createTenantIsolationExtension)({
|
|
65
|
+
orgScopedModels,
|
|
66
|
+
orgField,
|
|
67
|
+
mode: tenant_isolation_mode_1.TenantIsolationMode.Enforce,
|
|
68
|
+
orgIdSource: () => orgId,
|
|
69
|
+
logger: tenantLogger,
|
|
70
|
+
}));
|
|
71
|
+
};
|
|
72
|
+
const seenSystemCallSites = new Set();
|
|
73
|
+
const asSystem = () => {
|
|
74
|
+
const callSite = new Error().stack?.split('\n')[2]?.trim();
|
|
75
|
+
if (callSite !== undefined && !seenSystemCallSites.has(callSite)) {
|
|
76
|
+
seenSystemCallSites.add(callSite);
|
|
77
|
+
tenantLogger.debug(`[tenant-isolation] asSystem() unscoped access from ${callSite}`);
|
|
78
|
+
}
|
|
79
|
+
return raw;
|
|
80
|
+
};
|
|
81
|
+
if (mode === tenant_isolation_mode_1.TenantIsolationMode.Off) {
|
|
82
|
+
return new Proxy(raw, {
|
|
83
|
+
get(target, prop, _receiver) {
|
|
84
|
+
if (prop === 'forOrg') {
|
|
85
|
+
return forOrg;
|
|
86
|
+
}
|
|
87
|
+
if (prop === 'asSystem') {
|
|
88
|
+
return asSystem;
|
|
89
|
+
}
|
|
90
|
+
const value = Reflect.get(target, prop, target);
|
|
91
|
+
return typeof value === 'function' ? value.bind(target) : value;
|
|
92
|
+
},
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
const ambientClient = requireExtends()((0, tenant_isolation_extension_1.createTenantIsolationExtension)({
|
|
96
|
+
orgScopedModels: getScopedModels(),
|
|
97
|
+
orgField,
|
|
98
|
+
mode,
|
|
99
|
+
orgIdSource: platform_common_1.getAmbientOrgId,
|
|
100
|
+
logger: tenantLogger,
|
|
101
|
+
}));
|
|
102
|
+
const routedProps = new Set(['$transaction']);
|
|
103
|
+
for (const modelName of getScopedModels()) {
|
|
104
|
+
routedProps.add(lowerFirst(modelName));
|
|
105
|
+
}
|
|
106
|
+
return new Proxy(raw, {
|
|
107
|
+
get(target, prop, _receiver) {
|
|
108
|
+
if (prop === 'forOrg') {
|
|
109
|
+
return forOrg;
|
|
110
|
+
}
|
|
111
|
+
if (prop === 'asSystem') {
|
|
112
|
+
return asSystem;
|
|
113
|
+
}
|
|
114
|
+
if (typeof prop === 'string' && routedProps.has(prop)) {
|
|
115
|
+
const value = ambientClient[prop];
|
|
116
|
+
return typeof value === 'function'
|
|
117
|
+
? value.bind(ambientClient)
|
|
118
|
+
: value;
|
|
119
|
+
}
|
|
120
|
+
const value = Reflect.get(target, prop, target);
|
|
121
|
+
return typeof value === 'function' ? value.bind(target) : value;
|
|
122
|
+
},
|
|
123
|
+
});
|
|
20
124
|
}
|
|
21
125
|
async onModuleInit() {
|
|
22
126
|
try {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prisma-service-factory.js","sourceRoot":"","sources":["../../../src/lib/nest/prisma-service-factory.ts"],"names":[],"mappings":";;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"prisma-service-factory.js","sourceRoot":"","sources":["../../../src/lib/nest/prisma-service-factory.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { type TenantIsolationViolation } from './tenant-isolation-error';
|
|
2
|
+
export declare enum PrismaModelOperation {
|
|
3
|
+
FindUnique = "findUnique",
|
|
4
|
+
FindUniqueOrThrow = "findUniqueOrThrow",
|
|
5
|
+
FindFirst = "findFirst",
|
|
6
|
+
FindFirstOrThrow = "findFirstOrThrow",
|
|
7
|
+
FindMany = "findMany",
|
|
8
|
+
Create = "create",
|
|
9
|
+
CreateMany = "createMany",
|
|
10
|
+
CreateManyAndReturn = "createManyAndReturn",
|
|
11
|
+
Update = "update",
|
|
12
|
+
UpdateMany = "updateMany",
|
|
13
|
+
UpdateManyAndReturn = "updateManyAndReturn",
|
|
14
|
+
Upsert = "upsert",
|
|
15
|
+
Delete = "delete",
|
|
16
|
+
DeleteMany = "deleteMany",
|
|
17
|
+
Aggregate = "aggregate",
|
|
18
|
+
Count = "count",
|
|
19
|
+
GroupBy = "groupBy"
|
|
20
|
+
}
|
|
21
|
+
export interface OrgScopeRequest {
|
|
22
|
+
readonly model: string;
|
|
23
|
+
readonly operation: string;
|
|
24
|
+
readonly args: Readonly<Record<string, unknown>> | undefined;
|
|
25
|
+
readonly orgId: string;
|
|
26
|
+
readonly orgField: string;
|
|
27
|
+
}
|
|
28
|
+
export interface OrgScopeOutcome {
|
|
29
|
+
readonly args: Record<string, unknown>;
|
|
30
|
+
readonly violations: readonly TenantIsolationViolation[];
|
|
31
|
+
}
|
|
32
|
+
export declare function scopeArgsToOrg(req: OrgScopeRequest): OrgScopeOutcome;
|
|
33
|
+
//# sourceMappingURL=org-scope.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"org-scope.d.ts","sourceRoot":"","sources":["../../../src/lib/tenant-isolation/org-scope.ts"],"names":[],"mappings":"AAOA,OAAO,EAEL,KAAK,wBAAwB,EAC9B,MAAM,0BAA0B,CAAC;AAQlC,oBAAY,oBAAoB;IAC9B,UAAU,eAAe;IACzB,iBAAiB,sBAAsB;IACvC,SAAS,cAAc;IACvB,gBAAgB,qBAAqB;IACrC,QAAQ,aAAa;IACrB,MAAM,WAAW;IACjB,UAAU,eAAe;IACzB,mBAAmB,wBAAwB;IAC3C,MAAM,WAAW;IACjB,UAAU,eAAe;IACzB,mBAAmB,wBAAwB;IAC3C,MAAM,WAAW;IACjB,MAAM,WAAW;IACjB,UAAU,eAAe;IACzB,SAAS,cAAc;IACvB,KAAK,UAAU;IACf,OAAO,YAAY;CACpB;AAyED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC;IAC7D,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,eAAe;IAM9B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACvC,QAAQ,CAAC,UAAU,EAAE,SAAS,wBAAwB,EAAE,CAAC;CAC1D;AAsID,wBAAgB,cAAc,CAAC,GAAG,EAAE,eAAe,GAAG,eAAe,CA6CpE"}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PrismaModelOperation = void 0;
|
|
4
|
+
exports.scopeArgsToOrg = scopeArgsToOrg;
|
|
5
|
+
const tenant_isolation_error_1 = require("./tenant-isolation-error");
|
|
6
|
+
var PrismaModelOperation;
|
|
7
|
+
(function (PrismaModelOperation) {
|
|
8
|
+
PrismaModelOperation["FindUnique"] = "findUnique";
|
|
9
|
+
PrismaModelOperation["FindUniqueOrThrow"] = "findUniqueOrThrow";
|
|
10
|
+
PrismaModelOperation["FindFirst"] = "findFirst";
|
|
11
|
+
PrismaModelOperation["FindFirstOrThrow"] = "findFirstOrThrow";
|
|
12
|
+
PrismaModelOperation["FindMany"] = "findMany";
|
|
13
|
+
PrismaModelOperation["Create"] = "create";
|
|
14
|
+
PrismaModelOperation["CreateMany"] = "createMany";
|
|
15
|
+
PrismaModelOperation["CreateManyAndReturn"] = "createManyAndReturn";
|
|
16
|
+
PrismaModelOperation["Update"] = "update";
|
|
17
|
+
PrismaModelOperation["UpdateMany"] = "updateMany";
|
|
18
|
+
PrismaModelOperation["UpdateManyAndReturn"] = "updateManyAndReturn";
|
|
19
|
+
PrismaModelOperation["Upsert"] = "upsert";
|
|
20
|
+
PrismaModelOperation["Delete"] = "delete";
|
|
21
|
+
PrismaModelOperation["DeleteMany"] = "deleteMany";
|
|
22
|
+
PrismaModelOperation["Aggregate"] = "aggregate";
|
|
23
|
+
PrismaModelOperation["Count"] = "count";
|
|
24
|
+
PrismaModelOperation["GroupBy"] = "groupBy";
|
|
25
|
+
})(PrismaModelOperation || (exports.PrismaModelOperation = PrismaModelOperation = {}));
|
|
26
|
+
var WhereShape;
|
|
27
|
+
(function (WhereShape) {
|
|
28
|
+
WhereShape["None"] = "none";
|
|
29
|
+
WhereShape["Filter"] = "filter";
|
|
30
|
+
WhereShape["Unique"] = "unique";
|
|
31
|
+
})(WhereShape || (WhereShape = {}));
|
|
32
|
+
const OPERATION_SCOPE_SPECS = {
|
|
33
|
+
[PrismaModelOperation.FindUnique]: { where: WhereShape.Unique },
|
|
34
|
+
[PrismaModelOperation.FindUniqueOrThrow]: { where: WhereShape.Unique },
|
|
35
|
+
[PrismaModelOperation.FindFirst]: { where: WhereShape.Filter },
|
|
36
|
+
[PrismaModelOperation.FindFirstOrThrow]: { where: WhereShape.Filter },
|
|
37
|
+
[PrismaModelOperation.FindMany]: { where: WhereShape.Filter },
|
|
38
|
+
[PrismaModelOperation.Create]: {
|
|
39
|
+
where: WhereShape.None,
|
|
40
|
+
createDataKey: 'data',
|
|
41
|
+
},
|
|
42
|
+
[PrismaModelOperation.CreateMany]: {
|
|
43
|
+
where: WhereShape.None,
|
|
44
|
+
createDataKey: 'data',
|
|
45
|
+
},
|
|
46
|
+
[PrismaModelOperation.CreateManyAndReturn]: {
|
|
47
|
+
where: WhereShape.None,
|
|
48
|
+
createDataKey: 'data',
|
|
49
|
+
},
|
|
50
|
+
[PrismaModelOperation.Update]: {
|
|
51
|
+
where: WhereShape.Unique,
|
|
52
|
+
updateDataKey: 'data',
|
|
53
|
+
},
|
|
54
|
+
[PrismaModelOperation.UpdateMany]: {
|
|
55
|
+
where: WhereShape.Filter,
|
|
56
|
+
updateDataKey: 'data',
|
|
57
|
+
},
|
|
58
|
+
[PrismaModelOperation.UpdateManyAndReturn]: {
|
|
59
|
+
where: WhereShape.Filter,
|
|
60
|
+
updateDataKey: 'data',
|
|
61
|
+
},
|
|
62
|
+
[PrismaModelOperation.Upsert]: {
|
|
63
|
+
where: WhereShape.Unique,
|
|
64
|
+
createDataKey: 'create',
|
|
65
|
+
updateDataKey: 'update',
|
|
66
|
+
},
|
|
67
|
+
[PrismaModelOperation.Delete]: { where: WhereShape.Unique },
|
|
68
|
+
[PrismaModelOperation.DeleteMany]: { where: WhereShape.Filter },
|
|
69
|
+
[PrismaModelOperation.Aggregate]: { where: WhereShape.Filter },
|
|
70
|
+
[PrismaModelOperation.Count]: { where: WhereShape.Filter },
|
|
71
|
+
[PrismaModelOperation.GroupBy]: { where: WhereShape.Filter },
|
|
72
|
+
};
|
|
73
|
+
function isPlainObject(value) {
|
|
74
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
75
|
+
}
|
|
76
|
+
function appendAndClause(existing, clause) {
|
|
77
|
+
if (existing === undefined) {
|
|
78
|
+
return [clause];
|
|
79
|
+
}
|
|
80
|
+
if (Array.isArray(existing)) {
|
|
81
|
+
return [...existing, clause];
|
|
82
|
+
}
|
|
83
|
+
return [existing, clause];
|
|
84
|
+
}
|
|
85
|
+
function andOrgIntoWhere(where, orgField, orgId) {
|
|
86
|
+
const orgClause = { [orgField]: orgId };
|
|
87
|
+
if (where === undefined || where === null) {
|
|
88
|
+
return orgClause;
|
|
89
|
+
}
|
|
90
|
+
if (!isPlainObject(where)) {
|
|
91
|
+
return where;
|
|
92
|
+
}
|
|
93
|
+
return { ...where, AND: appendAndClause(where['AND'], orgClause) };
|
|
94
|
+
}
|
|
95
|
+
function extractScalarWriteValue(value) {
|
|
96
|
+
if (isPlainObject(value) && 'set' in value) {
|
|
97
|
+
return value['set'];
|
|
98
|
+
}
|
|
99
|
+
return value;
|
|
100
|
+
}
|
|
101
|
+
function orgMismatchViolation(req, actual, path) {
|
|
102
|
+
return {
|
|
103
|
+
kind: tenant_isolation_error_1.TenantIsolationViolationKind.OrgMismatch,
|
|
104
|
+
model: req.model,
|
|
105
|
+
operation: req.operation,
|
|
106
|
+
orgField: req.orgField,
|
|
107
|
+
expectedOrgId: req.orgId,
|
|
108
|
+
actualOrgId: String(actual),
|
|
109
|
+
path,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
function stampCreateRow(row, req, path, violations) {
|
|
113
|
+
if (!isPlainObject(row)) {
|
|
114
|
+
return row;
|
|
115
|
+
}
|
|
116
|
+
const current = row[req.orgField];
|
|
117
|
+
if (current === undefined) {
|
|
118
|
+
return { ...row, [req.orgField]: req.orgId };
|
|
119
|
+
}
|
|
120
|
+
if (extractScalarWriteValue(current) !== req.orgId) {
|
|
121
|
+
violations.push(orgMismatchViolation(req, extractScalarWriteValue(current), path));
|
|
122
|
+
}
|
|
123
|
+
return row;
|
|
124
|
+
}
|
|
125
|
+
function stampCreatePayload(payload, req, path, violations) {
|
|
126
|
+
if (Array.isArray(payload)) {
|
|
127
|
+
return payload.map((row) => stampCreateRow(row, req, path, violations));
|
|
128
|
+
}
|
|
129
|
+
return stampCreateRow(payload, req, path, violations);
|
|
130
|
+
}
|
|
131
|
+
function checkUpdatePayload(payload, req, path, violations) {
|
|
132
|
+
if (!isPlainObject(payload)) {
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
const current = payload[req.orgField];
|
|
136
|
+
if (current === undefined) {
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
if (extractScalarWriteValue(current) !== req.orgId) {
|
|
140
|
+
violations.push(orgMismatchViolation(req, extractScalarWriteValue(current), path));
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
function scopeArgsToOrg(req) {
|
|
144
|
+
const spec = OPERATION_SCOPE_SPECS[req.operation];
|
|
145
|
+
if (spec === undefined) {
|
|
146
|
+
return {
|
|
147
|
+
args: { ...(req.args ?? {}) },
|
|
148
|
+
violations: [
|
|
149
|
+
{
|
|
150
|
+
kind: tenant_isolation_error_1.TenantIsolationViolationKind.UnsupportedOperation,
|
|
151
|
+
model: req.model,
|
|
152
|
+
operation: req.operation,
|
|
153
|
+
orgField: req.orgField,
|
|
154
|
+
expectedOrgId: req.orgId,
|
|
155
|
+
},
|
|
156
|
+
],
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
const violations = [];
|
|
160
|
+
const args = { ...(req.args ?? {}) };
|
|
161
|
+
if (spec.where !== WhereShape.None) {
|
|
162
|
+
args['where'] = andOrgIntoWhere(args['where'], req.orgField, req.orgId);
|
|
163
|
+
}
|
|
164
|
+
if (spec.createDataKey !== undefined) {
|
|
165
|
+
args[spec.createDataKey] = stampCreatePayload(args[spec.createDataKey], req, spec.createDataKey, violations);
|
|
166
|
+
}
|
|
167
|
+
if (spec.updateDataKey !== undefined) {
|
|
168
|
+
checkUpdatePayload(args[spec.updateDataKey], req, spec.updateDataKey, violations);
|
|
169
|
+
}
|
|
170
|
+
return { args, violations };
|
|
171
|
+
}
|
|
172
|
+
//# sourceMappingURL=org-scope.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"org-scope.js","sourceRoot":"","sources":["../../../src/lib/tenant-isolation/org-scope.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export declare enum TenantIsolationViolationKind {
|
|
2
|
+
OrgMismatch = "org_mismatch",
|
|
3
|
+
MissingOrgContext = "missing_org_context",
|
|
4
|
+
UnsupportedOperation = "unsupported_operation"
|
|
5
|
+
}
|
|
6
|
+
export interface TenantIsolationViolation {
|
|
7
|
+
readonly kind: TenantIsolationViolationKind;
|
|
8
|
+
readonly model: string;
|
|
9
|
+
readonly operation: string;
|
|
10
|
+
readonly orgField: string;
|
|
11
|
+
readonly expectedOrgId?: string;
|
|
12
|
+
readonly actualOrgId?: string;
|
|
13
|
+
readonly path?: string;
|
|
14
|
+
}
|
|
15
|
+
export declare function formatTenantIsolationViolation(violation: TenantIsolationViolation): string;
|
|
16
|
+
export declare class TenantIsolationError extends Error {
|
|
17
|
+
readonly violation: TenantIsolationViolation;
|
|
18
|
+
constructor(violation: TenantIsolationViolation);
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=tenant-isolation-error.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tenant-isolation-error.d.ts","sourceRoot":"","sources":["../../../src/lib/tenant-isolation/tenant-isolation-error.ts"],"names":[],"mappings":"AAaA,oBAAY,4BAA4B;IACtC,WAAW,iBAAiB;IAC5B,iBAAiB,wBAAwB;IACzC,oBAAoB,0BAA0B;CAC/C;AAGD,MAAM,WAAW,wBAAwB;IACvC,QAAQ,CAAC,IAAI,EAAE,4BAA4B,CAAC;IAE5C,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IAEvB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAE3B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAE1B,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAEhC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAE9B,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;CACxB;AAGD,wBAAgB,8BAA8B,CAC5C,SAAS,EAAE,wBAAwB,GAClC,MAAM,CAwBR;AAOD,qBAAa,oBAAqB,SAAQ,KAAK;IAC7C,QAAQ,CAAC,SAAS,EAAE,wBAAwB,CAAC;gBAEjC,SAAS,EAAE,wBAAwB;CAKhD"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TenantIsolationError = exports.TenantIsolationViolationKind = void 0;
|
|
4
|
+
exports.formatTenantIsolationViolation = formatTenantIsolationViolation;
|
|
5
|
+
var TenantIsolationViolationKind;
|
|
6
|
+
(function (TenantIsolationViolationKind) {
|
|
7
|
+
TenantIsolationViolationKind["OrgMismatch"] = "org_mismatch";
|
|
8
|
+
TenantIsolationViolationKind["MissingOrgContext"] = "missing_org_context";
|
|
9
|
+
TenantIsolationViolationKind["UnsupportedOperation"] = "unsupported_operation";
|
|
10
|
+
})(TenantIsolationViolationKind || (exports.TenantIsolationViolationKind = TenantIsolationViolationKind = {}));
|
|
11
|
+
function formatTenantIsolationViolation(violation) {
|
|
12
|
+
const site = `${violation.model}.${violation.operation}`;
|
|
13
|
+
switch (violation.kind) {
|
|
14
|
+
case TenantIsolationViolationKind.OrgMismatch:
|
|
15
|
+
return (`[tenant-isolation] org mismatch on ${site}: args` +
|
|
16
|
+
`${violation.path !== undefined ? ` "${violation.path}"` : ''} set ` +
|
|
17
|
+
`${violation.orgField}="${violation.actualOrgId}" but the client is ` +
|
|
18
|
+
`scoped to "${violation.expectedOrgId}".`);
|
|
19
|
+
case TenantIsolationViolationKind.MissingOrgContext:
|
|
20
|
+
return (`[tenant-isolation] ${site} executed with no org context. Outside a ` +
|
|
21
|
+
`request (boot, migrations, seeding, workers, Temporal activities) ` +
|
|
22
|
+
`there is no ambient org — use client.forOrg(orgId) for explicit ` +
|
|
23
|
+
`scoping, or client.asSystem() for a legitimate cross-org system path.`);
|
|
24
|
+
case TenantIsolationViolationKind.UnsupportedOperation:
|
|
25
|
+
return (`[tenant-isolation] unrecognized Prisma operation "${violation.operation}" ` +
|
|
26
|
+
`on org-scoped model ${violation.model} — tenant isolation cannot prove ` +
|
|
27
|
+
`it is org-scoped. Add it to PrismaModelOperation or exempt the model.`);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
class TenantIsolationError extends Error {
|
|
31
|
+
violation;
|
|
32
|
+
constructor(violation) {
|
|
33
|
+
super(formatTenantIsolationViolation(violation));
|
|
34
|
+
this.name = 'TenantIsolationError';
|
|
35
|
+
this.violation = violation;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
exports.TenantIsolationError = TenantIsolationError;
|
|
39
|
+
//# sourceMappingURL=tenant-isolation-error.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tenant-isolation-error.js","sourceRoot":"","sources":["../../../src/lib/tenant-isolation/tenant-isolation-error.ts"],"names":[],"mappings":";;;AAqCA,wEA0BC;AAlDD,IAAY,4BAIX;AAJD,WAAY,4BAA4B;IACtC,4DAA4B,CAAA;IAC5B,yEAAyC,CAAA;IACzC,8EAA8C,CAAA;AAChD,CAAC,EAJW,4BAA4B,4CAA5B,4BAA4B,QAIvC;AAoBD,SAAgB,8BAA8B,CAC5C,SAAmC;IAEnC,MAAM,IAAI,GAAG,GAAG,SAAS,CAAC,KAAK,IAAI,SAAS,CAAC,SAAS,EAAE,CAAC;IACzD,QAAQ,SAAS,CAAC,IAAI,EAAE,CAAC;QACvB,KAAK,4BAA4B,CAAC,WAAW;YAC3C,OAAO,CACL,sCAAsC,IAAI,QAAQ;gBAClD,GAAG,SAAS,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,SAAS,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,EAAE,OAAO;gBACpE,GAAG,SAAS,CAAC,QAAQ,KAAK,SAAS,CAAC,WAAW,sBAAsB;gBACrE,cAAc,SAAS,CAAC,aAAa,IAAI,CAC1C,CAAC;QACJ,KAAK,4BAA4B,CAAC,iBAAiB;YACjD,OAAO,CACL,sBAAsB,IAAI,2CAA2C;gBACrE,oEAAoE;gBACpE,kEAAkE;gBAClE,uEAAuE,CACxE,CAAC;QACJ,KAAK,4BAA4B,CAAC,oBAAoB;YACpD,OAAO,CACL,qDAAqD,SAAS,CAAC,SAAS,IAAI;gBAC5E,uBAAuB,SAAS,CAAC,KAAK,mCAAmC;gBACzE,uEAAuE,CACxE,CAAC;IACN,CAAC;AACH,CAAC;AAOD,MAAa,oBAAqB,SAAQ,KAAK;IACpC,SAAS,CAA2B;IAE7C,YAAY,SAAmC;QAC7C,KAAK,CAAC,8BAA8B,CAAC,SAAS,CAAC,CAAC,CAAC;QACjD,IAAI,CAAC,IAAI,GAAG,sBAAsB,CAAC;QACnC,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;IAC7B,CAAC;CACF;AARD,oDAQC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export declare enum TenantIsolationMode {
|
|
2
|
+
Enforce = "enforce",
|
|
3
|
+
Observe = "observe",
|
|
4
|
+
Off = "off"
|
|
5
|
+
}
|
|
6
|
+
export declare const TENANT_ISOLATION_MODE_ENV_VAR = "XEMA_TENANT_ISOLATION_MODE";
|
|
7
|
+
export declare function resolveTenantIsolationMode(explicit?: TenantIsolationMode, env?: NodeJS.ProcessEnv): TenantIsolationMode;
|
|
8
|
+
//# sourceMappingURL=tenant-isolation-mode.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tenant-isolation-mode.d.ts","sourceRoot":"","sources":["../../../src/lib/tenant-isolation/tenant-isolation-mode.ts"],"names":[],"mappings":"AAcA,oBAAY,mBAAmB;IAC7B,OAAO,YAAY;IACnB,OAAO,YAAY;IACnB,GAAG,QAAQ;CACZ;AAGD,eAAO,MAAM,6BAA6B,+BAA+B,CAAC;AAe1E,wBAAgB,0BAA0B,CACxC,QAAQ,CAAC,EAAE,mBAAmB,EAC9B,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,mBAAmB,CAerB"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TENANT_ISOLATION_MODE_ENV_VAR = exports.TenantIsolationMode = void 0;
|
|
4
|
+
exports.resolveTenantIsolationMode = resolveTenantIsolationMode;
|
|
5
|
+
var TenantIsolationMode;
|
|
6
|
+
(function (TenantIsolationMode) {
|
|
7
|
+
TenantIsolationMode["Enforce"] = "enforce";
|
|
8
|
+
TenantIsolationMode["Observe"] = "observe";
|
|
9
|
+
TenantIsolationMode["Off"] = "off";
|
|
10
|
+
})(TenantIsolationMode || (exports.TenantIsolationMode = TenantIsolationMode = {}));
|
|
11
|
+
exports.TENANT_ISOLATION_MODE_ENV_VAR = 'XEMA_TENANT_ISOLATION_MODE';
|
|
12
|
+
const MODE_VALUES = Object.values(TenantIsolationMode);
|
|
13
|
+
function isTenantIsolationMode(value) {
|
|
14
|
+
return MODE_VALUES.includes(value);
|
|
15
|
+
}
|
|
16
|
+
function resolveTenantIsolationMode(explicit, env = process.env) {
|
|
17
|
+
if (explicit !== undefined) {
|
|
18
|
+
return explicit;
|
|
19
|
+
}
|
|
20
|
+
const raw = env[exports.TENANT_ISOLATION_MODE_ENV_VAR];
|
|
21
|
+
if (raw === undefined || raw === '') {
|
|
22
|
+
return TenantIsolationMode.Observe;
|
|
23
|
+
}
|
|
24
|
+
if (!isTenantIsolationMode(raw)) {
|
|
25
|
+
throw new Error(`[tenant-isolation] invalid ${exports.TENANT_ISOLATION_MODE_ENV_VAR}="${raw}" — ` +
|
|
26
|
+
`expected one of: ${MODE_VALUES.join(', ')}.`);
|
|
27
|
+
}
|
|
28
|
+
return raw;
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=tenant-isolation-mode.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tenant-isolation-mode.js","sourceRoot":"","sources":["../../../src/lib/tenant-isolation/tenant-isolation-mode.ts"],"names":[],"mappings":";;;AAoCA,gEAkBC;AAxCD,IAAY,mBAIX;AAJD,WAAY,mBAAmB;IAC7B,0CAAmB,CAAA;IACnB,0CAAmB,CAAA;IACnB,kCAAW,CAAA;AACb,CAAC,EAJW,mBAAmB,mCAAnB,mBAAmB,QAI9B;AAGY,QAAA,6BAA6B,GAAG,4BAA4B,CAAC;AAE1E,MAAM,WAAW,GAAsB,MAAM,CAAC,MAAM,CAAC,mBAAmB,CAAC,CAAC;AAE1E,SAAS,qBAAqB,CAAC,KAAa;IAC1C,OAAO,WAAW,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;AACrC,CAAC;AASD,SAAgB,0BAA0B,CACxC,QAA8B,EAC9B,MAAyB,OAAO,CAAC,GAAG;IAEpC,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;QAC3B,OAAO,QAAQ,CAAC;IAClB,CAAC;IACD,MAAM,GAAG,GAAG,GAAG,CAAC,qCAA6B,CAAC,CAAC;IAC/C,IAAI,GAAG,KAAK,SAAS,IAAI,GAAG,KAAK,EAAE,EAAE,CAAC;QACpC,OAAO,mBAAmB,CAAC,OAAO,CAAC;IACrC,CAAC;IACD,IAAI,CAAC,qBAAqB,CAAC,GAAG,CAAC,EAAE,CAAC;QAChC,MAAM,IAAI,KAAK,CACb,8BAA8B,qCAA6B,KAAK,GAAG,MAAM;YACvE,oBAAoB,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAChD,CAAC;IACJ,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { TenantIsolationMode } from './tenant-isolation-mode';
|
|
2
|
+
export declare const DEFAULT_ORG_FIELD = "orgId";
|
|
3
|
+
export interface TenantIsolationLogger {
|
|
4
|
+
warn(message: string): void;
|
|
5
|
+
debug(message: string): void;
|
|
6
|
+
}
|
|
7
|
+
export declare function collectOrgScopedModels(client: unknown, orgField: string, exemptModels: readonly string[]): ReadonlySet<string>;
|
|
8
|
+
export interface TenantIsolationQueryContext {
|
|
9
|
+
readonly model?: string;
|
|
10
|
+
readonly operation: string;
|
|
11
|
+
readonly args?: Record<string, unknown>;
|
|
12
|
+
readonly query: (args: unknown) => Promise<unknown>;
|
|
13
|
+
}
|
|
14
|
+
export interface CreateTenantIsolationExtensionOptions {
|
|
15
|
+
readonly orgScopedModels: ReadonlySet<string>;
|
|
16
|
+
readonly orgField: string;
|
|
17
|
+
readonly mode: TenantIsolationMode;
|
|
18
|
+
readonly orgIdSource: () => string | undefined;
|
|
19
|
+
readonly logger: TenantIsolationLogger;
|
|
20
|
+
}
|
|
21
|
+
export interface TenantIsolationExtensionDefinition {
|
|
22
|
+
readonly name: string;
|
|
23
|
+
readonly query: {
|
|
24
|
+
readonly $allModels: {
|
|
25
|
+
$allOperations(ctx: TenantIsolationQueryContext): Promise<unknown>;
|
|
26
|
+
};
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
export declare function createTenantIsolationExtension(options: CreateTenantIsolationExtensionOptions): TenantIsolationExtensionDefinition;
|
|
30
|
+
//# sourceMappingURL=tenant-isolation.extension.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tenant-isolation.extension.d.ts","sourceRoot":"","sources":["../../../src/lib/tenant-isolation/tenant-isolation.extension.ts"],"names":[],"mappings":"AAcA,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAG9D,eAAO,MAAM,iBAAiB,UAAU,CAAC;AAGzC,MAAM,WAAW,qBAAqB;IACpC,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B;AAsCD,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,OAAO,EACf,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,SAAS,MAAM,EAAE,GAC9B,WAAW,CAAC,MAAM,CAAC,CAoBrB;AAGD,MAAM,WAAW,2BAA2B;IAC1C,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACxC,QAAQ,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;CACrD;AAED,MAAM,WAAW,qCAAqC;IAEpD,QAAQ,CAAC,eAAe,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAC9C,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,IAAI,EAAE,mBAAmB,CAAC;IAMnC,QAAQ,CAAC,WAAW,EAAE,MAAM,MAAM,GAAG,SAAS,CAAC;IAC/C,QAAQ,CAAC,MAAM,EAAE,qBAAqB,CAAC;CACxC;AAGD,MAAM,WAAW,kCAAkC;IACjD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE;QACd,QAAQ,CAAC,UAAU,EAAE;YACnB,cAAc,CAAC,GAAG,EAAE,2BAA2B,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;SACpE,CAAC;KACH,CAAC;CACH;AAYD,wBAAgB,8BAA8B,CAC5C,OAAO,EAAE,qCAAqC,GAC7C,kCAAkC,CAkEpC"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DEFAULT_ORG_FIELD = void 0;
|
|
4
|
+
exports.collectOrgScopedModels = collectOrgScopedModels;
|
|
5
|
+
exports.createTenantIsolationExtension = createTenantIsolationExtension;
|
|
6
|
+
const org_scope_1 = require("./org-scope");
|
|
7
|
+
const tenant_isolation_error_1 = require("./tenant-isolation-error");
|
|
8
|
+
const tenant_isolation_mode_1 = require("./tenant-isolation-mode");
|
|
9
|
+
exports.DEFAULT_ORG_FIELD = 'orgId';
|
|
10
|
+
function readRuntimeModels(client) {
|
|
11
|
+
const models = client._runtimeDataModel?.models;
|
|
12
|
+
if (models === undefined) {
|
|
13
|
+
throw new Error('[tenant-isolation] the Prisma client exposes no _runtimeDataModel.models — ' +
|
|
14
|
+
'cannot detect org-scoped models. Tenant isolation requires a generated ' +
|
|
15
|
+
'@prisma/client >= 5 (this SDK peers on ^7).');
|
|
16
|
+
}
|
|
17
|
+
return models;
|
|
18
|
+
}
|
|
19
|
+
function collectOrgScopedModels(client, orgField, exemptModels) {
|
|
20
|
+
const models = readRuntimeModels(client);
|
|
21
|
+
for (const exempt of exemptModels) {
|
|
22
|
+
if (!(exempt in models)) {
|
|
23
|
+
throw new Error(`[tenant-isolation] exemptModels names unknown model "${exempt}" — ` +
|
|
24
|
+
`known models: ${Object.keys(models).join(', ')}.`);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
const scoped = new Set();
|
|
28
|
+
for (const [name, model] of Object.entries(models)) {
|
|
29
|
+
if (exemptModels.includes(name)) {
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
if (model.fields.some((field) => field.name === orgField)) {
|
|
33
|
+
scoped.add(name);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return scoped;
|
|
37
|
+
}
|
|
38
|
+
function createTenantIsolationExtension(options) {
|
|
39
|
+
const { orgScopedModels, orgField, mode, orgIdSource, logger } = options;
|
|
40
|
+
const handleViolations = (violations) => {
|
|
41
|
+
const [first] = violations;
|
|
42
|
+
if (first === undefined) {
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
if (mode === tenant_isolation_mode_1.TenantIsolationMode.Enforce) {
|
|
46
|
+
throw new tenant_isolation_error_1.TenantIsolationError(first);
|
|
47
|
+
}
|
|
48
|
+
for (const violation of violations) {
|
|
49
|
+
logger.warn((0, tenant_isolation_error_1.formatTenantIsolationViolation)(violation));
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
return {
|
|
53
|
+
name: 'xema-tenant-isolation',
|
|
54
|
+
query: {
|
|
55
|
+
$allModels: {
|
|
56
|
+
$allOperations(ctx) {
|
|
57
|
+
const { model, operation, args, query } = ctx;
|
|
58
|
+
if (mode === tenant_isolation_mode_1.TenantIsolationMode.Off ||
|
|
59
|
+
model === undefined ||
|
|
60
|
+
!orgScopedModels.has(model)) {
|
|
61
|
+
return query(args);
|
|
62
|
+
}
|
|
63
|
+
const orgId = orgIdSource();
|
|
64
|
+
if (orgId === undefined || orgId === '') {
|
|
65
|
+
handleViolations([
|
|
66
|
+
{
|
|
67
|
+
kind: tenant_isolation_error_1.TenantIsolationViolationKind.MissingOrgContext,
|
|
68
|
+
model,
|
|
69
|
+
operation,
|
|
70
|
+
orgField,
|
|
71
|
+
},
|
|
72
|
+
]);
|
|
73
|
+
return query(args);
|
|
74
|
+
}
|
|
75
|
+
const outcome = (0, org_scope_1.scopeArgsToOrg)({
|
|
76
|
+
model,
|
|
77
|
+
operation,
|
|
78
|
+
args,
|
|
79
|
+
orgId,
|
|
80
|
+
orgField,
|
|
81
|
+
});
|
|
82
|
+
if (outcome.violations.length > 0) {
|
|
83
|
+
handleViolations(outcome.violations);
|
|
84
|
+
return query(args);
|
|
85
|
+
}
|
|
86
|
+
return mode === tenant_isolation_mode_1.TenantIsolationMode.Enforce
|
|
87
|
+
? query(outcome.args)
|
|
88
|
+
: query(args);
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
//# sourceMappingURL=tenant-isolation.extension.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tenant-isolation.extension.js","sourceRoot":"","sources":["../../../src/lib/tenant-isolation/tenant-isolation.extension.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xemahq/biome-database-nest",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.2",
|
|
4
4
|
"description": "Layer 1 SDK that centralizes ALL database concerns for Xema biome services. Provides the single correct construction of a schema-qualified Prisma driver-adapter (the second-arg `{ schema }` that makes `@prisma/adapter-pg` qualify `biome_<id>.<table>` instead of `public.<table>`), the per-biome schema-naming convention, env/control-plane config resolution, and a boot-time bootstrap that creates the schema and runs migrations. Biomes carry zero DB boilerplate.",
|
|
5
|
+
"license": "Apache-2.0",
|
|
6
|
+
"author": "Neuralchowder Inc. <developer@xema.dev> (https://xema.dev)",
|
|
7
|
+
"homepage": "https://xema.dev",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/xema-dev/xema-kernel-sdk.git",
|
|
11
|
+
"directory": "packages/sdks/biome-database-nest"
|
|
12
|
+
},
|
|
13
|
+
"bugs": "https://github.com/xema-dev/xema-kernel-sdk/issues",
|
|
5
14
|
"publishConfig": {
|
|
6
15
|
"registry": "https://registry.npmjs.org/",
|
|
7
16
|
"access": "public"
|
|
@@ -18,12 +27,12 @@
|
|
|
18
27
|
"@types/node": "25.2.3",
|
|
19
28
|
"@types/pg": "^8.16.0",
|
|
20
29
|
"@xemahq/identity-client": "^0.3.5",
|
|
21
|
-
"@xemahq/kernel-contracts": "^0.
|
|
22
|
-
"@xemahq/service-registry-nest": "^0.2.0",
|
|
30
|
+
"@xemahq/kernel-contracts": "^0.48.0",
|
|
23
31
|
"jest": "^30.3.0",
|
|
24
32
|
"prettier": "3.6.2",
|
|
25
33
|
"ts-jest": "^29.4.9",
|
|
26
|
-
"typescript": "5.9.3"
|
|
34
|
+
"typescript": "5.9.3",
|
|
35
|
+
"@xemahq/service-registry-nest": "0.3.2"
|
|
27
36
|
},
|
|
28
37
|
"peerDependencies": {
|
|
29
38
|
"@nestjs/common": "^10 || ^11",
|
|
@@ -36,7 +45,7 @@
|
|
|
36
45
|
},
|
|
37
46
|
"dependencies": {
|
|
38
47
|
"@xemahq/migration-runner-prisma": "^0.1.2",
|
|
39
|
-
"@xemahq/platform-common": "^0.
|
|
48
|
+
"@xemahq/platform-common": "^0.3.0",
|
|
40
49
|
"pg": "8.16.3"
|
|
41
50
|
},
|
|
42
51
|
"exports": {
|