@stratum-hq/lib 1.6.0 → 1.8.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/README.md +49 -20
- package/dist/crypto.d.ts.map +1 -1
- package/dist/crypto.js +50 -10
- package/dist/crypto.js.map +1 -1
- package/dist/index.d.ts +7 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +17 -1
- package/dist/index.js.map +1 -1
- package/dist/migrate-schemas.d.ts +4 -0
- package/dist/migrate-schemas.d.ts.map +1 -1
- package/dist/migrate-schemas.js +17 -7
- package/dist/migrate-schemas.js.map +1 -1
- package/dist/migrate.d.ts +24 -0
- package/dist/migrate.d.ts.map +1 -1
- package/dist/migrate.js +44 -6
- package/dist/migrate.js.map +1 -1
- package/dist/migration-sql.d.ts +25 -0
- package/dist/migration-sql.d.ts.map +1 -0
- package/dist/migration-sql.js +64 -0
- package/dist/migration-sql.js.map +1 -0
- package/dist/migrations/001_init.sql +3 -3
- package/dist/migrations/029_tenant_parent_cycle_guard.sql +2 -2
- package/dist/migrations/031_subtree_read_scope.sql +307 -0
- package/dist/migrations/032_control_role.sql +809 -0
- package/dist/migrations/033_api_keys_tenant_index.sql +14 -0
- package/dist/pinned-query.d.ts +29 -0
- package/dist/pinned-query.d.ts.map +1 -0
- package/dist/pinned-query.js +60 -0
- package/dist/pinned-query.js.map +1 -0
- package/dist/pool-helpers.d.ts +2 -0
- package/dist/pool-helpers.d.ts.map +1 -1
- package/dist/pool-helpers.js +17 -3
- package/dist/pool-helpers.js.map +1 -1
- package/dist/role-model.d.ts +178 -0
- package/dist/role-model.d.ts.map +1 -0
- package/dist/role-model.js +670 -0
- package/dist/role-model.js.map +1 -0
- package/dist/scoped-job.d.ts +9 -1
- package/dist/scoped-job.d.ts.map +1 -1
- package/dist/scoped-job.js +3 -2
- package/dist/scoped-job.js.map +1 -1
- package/dist/services/abac-service.js +1 -1
- package/dist/services/abac-service.js.map +1 -1
- package/dist/services/api-key-service.d.ts +13 -3
- package/dist/services/api-key-service.d.ts.map +1 -1
- package/dist/services/api-key-service.js +33 -9
- package/dist/services/api-key-service.js.map +1 -1
- package/dist/services/config-service.d.ts +15 -6
- package/dist/services/config-service.d.ts.map +1 -1
- package/dist/services/config-service.js +101 -55
- package/dist/services/config-service.js.map +1 -1
- package/dist/services/event-service.js +2 -2
- package/dist/services/event-service.js.map +1 -1
- package/dist/services/permission-service.js +1 -1
- package/dist/services/permission-service.js.map +1 -1
- package/dist/services/rate-limit-service.d.ts +3 -3
- package/dist/services/region-service.js +1 -1
- package/dist/services/region-service.js.map +1 -1
- package/dist/services/retention-service.d.ts +2 -2
- package/dist/services/retention-service.js +2 -2
- package/dist/services/role-service.d.ts +1 -1
- package/dist/services/role-service.js +1 -1
- package/dist/services/tenant-service.d.ts +2 -2
- package/dist/services/tenant-service.d.ts.map +1 -1
- package/dist/services/tenant-service.js +8 -8
- package/dist/services/tenant-service.js.map +1 -1
- package/dist/stratum-policies.d.ts +51 -0
- package/dist/stratum-policies.d.ts.map +1 -0
- package/dist/stratum-policies.js +217 -0
- package/dist/stratum-policies.js.map +1 -0
- package/dist/stratum-tables.d.ts.map +1 -1
- package/dist/stratum-tables.js +1 -0
- package/dist/stratum-tables.js.map +1 -1
- package/dist/stratum.d.ts +56 -11
- package/dist/stratum.d.ts.map +1 -1
- package/dist/stratum.js +82 -21
- package/dist/stratum.js.map +1 -1
- package/dist/telemetry.d.ts +2 -2
- package/dist/telemetry.js +4 -4
- package/dist/telemetry.js.map +1 -1
- package/dist/url-redaction.js +1 -1
- package/dist/url-redaction.js.map +1 -1
- package/package.json +21 -13
package/README.md
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# @stratum-hq/lib
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Multi-tenancy for Node.js and PostgreSQL: tenant hierarchy, config inheritance, row-level security, ABAC, audit log, and GDPR tooling. This is the framework-agnostic core of [Stratum](https://github.com/stratum-hq/Stratum). Embed it directly in your Node.js app. It talks straight to PostgreSQL with no HTTP server in between.
|
|
4
|
+
|
|
5
|
+
Read the documentation at [docs.stratum-hq.org/packages/lib](https://docs.stratum-hq.org/packages/lib/).
|
|
4
6
|
|
|
5
7
|
## Installation
|
|
6
8
|
|
|
@@ -26,7 +28,7 @@ const customer = await stratum.createTenant({
|
|
|
26
28
|
isolation_strategy: "SHARED_RLS",
|
|
27
29
|
});
|
|
28
30
|
|
|
29
|
-
// Config flows root → leaf
|
|
31
|
+
// Config flows root → leaf; children inherit automatically
|
|
30
32
|
await stratum.setConfig(msp.id, "max_seats", { value: 500, locked: true });
|
|
31
33
|
const config = await stratum.resolveConfig(customer.id);
|
|
32
34
|
// → { max_seats: { value: 500, inherited: true, locked: true } }
|
|
@@ -34,19 +36,46 @@ const config = await stratum.resolveConfig(customer.id);
|
|
|
34
36
|
const permissions = await stratum.resolvePermissions(customer.id);
|
|
35
37
|
```
|
|
36
38
|
|
|
37
|
-
The `pool` is **borrowed, not owned
|
|
39
|
+
The `pool` is **borrowed, not owned**: Stratum never creates or closes it. With `autoMigrate: true`, `initialize()` runs the schema migrations on first start; leave it off and manage migrations yourself via `migrate`.
|
|
40
|
+
|
|
41
|
+
## Admin pool and the control role (1.8, opt-in)
|
|
42
|
+
|
|
43
|
+
Migration 032 adds a NOLOGIN **control role** (`stratum_control` by default). Every Stratum table has a `stratum_control_plane` policy for that role, so a login that is a member of it reaches every row, and a session cannot become a member by setting anything. Give the library its own pool on such a login, and keep `pool` for your application:
|
|
44
|
+
|
|
45
|
+
```typescript
|
|
46
|
+
const adminPool = new Pool({ connectionString: process.env.DATABASE_ADMIN_URL }); // member of stratum_control
|
|
47
|
+
const pool = new Pool({ connectionString: process.env.DATABASE_URL }); // the application role
|
|
48
|
+
const stratum = new Stratum({ adminPool, pool, autoMigrate: true, enforceRls: true });
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
- Every library query and `autoMigrate` run on `adminPool`. The admin login needs neither SUPERUSER nor BYPASSRLS.
|
|
52
|
+
- `initialize()` checks both logins: the admin login must be a member of the control role; the application login must not be a superuser, have BYPASSRLS, be a member of the control role, own Stratum tables or functions, write any Stratum table, or read `api_keys`, `webhooks`, `regions` or `stratum_security`. It warns about each problem; with `enforceRls: true` a problem with the application login throws.
|
|
53
|
+
- `bootstrapRolesSql({ adminRole, appRole, controlRole })` returns the idempotent SQL a database administrator runs once (and again after a migration that adds a table): it creates the control role, makes the admin login a member, and limits the application login to `SELECT` on the read-list tables (`APP_READ_TABLES`). `stratum db roles` (`@stratum-hq/cli`) prints it, or applies it with `--apply`. It first stops when the Stratum tables carry objects the migrations did not create (rules, triggers or defaults that call other functions, changed Stratum functions), because such objects would run with the rights of the login that applies it, and later of the admin login.
|
|
54
|
+
- Applying the control role (`stratum_apply_control_role()`, run by migration 032 and by the bootstrap SQL) resets row-level security on every Stratum table: it drops all their policies, enables and forces RLS, and re-creates the canonical policies. `stratumPolicyDrift(pool)` reports how a database differs from that set; `stratum doctor` shows it.
|
|
55
|
+
- `inspectRoleModel({ appPool, adminPool })` returns the role-model checks as data, without logging, for your own health checks. `stratum doctor` and `stratum health` use it.
|
|
56
|
+
- Migration 032 creates and joins the control role itself only with the opt-in `applyControlRole: true` (`migrate()`, `migrateAllSchemas()`; `autoMigrate` sets it when it runs on `adminPool`), or when the migrating login is a superuser or already a member, and only when the migrating role can (CREATEROLE, or ADMIN on an existing role). Set the opt-in only for the admin login: it makes the migrating login a member of the control role. Otherwise 032 still completes: it applies everything that needs no role, skips the control role with a WARNING that prints the bootstrap SQL, and the database keeps the pre-1.8 behavior. `initialize()` and `stratum doctor` then report the hardening as not active. Running the bootstrap SQL as a superuser later applies it through the idempotent `stratum_apply_control_role(role_name, schema)` function that 032 installs.
|
|
57
|
+
- Migrations 029 and 031 created helper functions with `SET app.*` clauses, which PostgreSQL accepts only from a superuser. When the migrating role is not a superuser, `migrate()` drops those clause lines from these two files; 032 re-creates both functions without them when it applies the control role.
|
|
58
|
+
- The control role name: `migrate({ controlRole })`, `migrateAllSchemas({ controlRole })` and `new Stratum({ controlRole })` set it, and so does `ALTER DATABASE ... SET stratum.control_role = '...'` for every session of one database. Without either, the migration reuses the role the database already has, else `stratum_control`. Roles are cluster-wide, so give databases that must stay apart their own name.
|
|
59
|
+
- The legacy `app.bypass_rls` path stays open while `stratum_security.legacy_guc_bypass` is true, the 1.8 default. Once every client of the database uses `adminPool`, close it as a member of the control role with `stratum db lock` (or `UPDATE stratum_security SET legacy_guc_bypass = false`).
|
|
60
|
+
- Known limit: the tenant context is still the session setting `app.current_tenant_id`, so SQL that runs as the application login can choose a tenant id it knows and read that tenant's rows in the read-list tables. The role model keeps credentials and the bypass out of its reach, not a chosen tenant context.
|
|
61
|
+
- Step by step, including managed PostgreSQL: the [hardening guide](https://docs.stratum-hq.org/guides/hardening-roles/).
|
|
62
|
+
- Without `adminPool`, Stratum behaves as before and logs a deprecation warning once. `adminPool` becomes required in 2.0, which also removes the legacy path.
|
|
63
|
+
|
|
64
|
+
### API key hashes
|
|
65
|
+
|
|
66
|
+
Keys created before `STRATUM_API_KEY_HMAC_SECRET` was set carry an unkeyed SHA-256 hash (version 1). In 1.x, `validateApiKey` still accepts them by default (`allowLegacyKeyHashes: true`) and re-hashes each one with HMAC when it authenticates, logging a warning once per process. Set `allowLegacyKeyHashes: false` to accept only HMAC hashes once the secret is set; 2.0 makes that the default. Before upgrading to 2.0, rotate the keys that still have a version 1 hash (`SELECT id, name FROM api_keys WHERE hash_version = 1 AND revoked_at IS NULL`).
|
|
38
67
|
|
|
39
68
|
## API Summary
|
|
40
69
|
|
|
41
70
|
The `Stratum` instance covers the full tenant lifecycle:
|
|
42
71
|
|
|
43
|
-
- **Tenants
|
|
44
|
-
- **Config
|
|
45
|
-
- **Permissions & ABAC
|
|
46
|
-
- **API keys & roles
|
|
47
|
-
- **Webhooks & audit
|
|
48
|
-
- **GDPR & regions
|
|
49
|
-
- **Usage metering
|
|
72
|
+
- **Tenants**: `createTenant`, `getTenant`, `listTenants`, `updateTenant`, `moveTenant`, `getAncestors`, `getDescendants`, `batchCreateTenants`
|
|
73
|
+
- **Config**: `resolveConfig`, `setConfig`, `deleteConfig`, `batchSetConfig`, `diffConfig`. A sensitive value inherited from an ancestor resolves masked (`value: null`, `masked: true`); pass `{ revealSensitive: true }` in trusted server code that needs the secret.
|
|
74
|
+
- **Permissions & ABAC**: `resolvePermissions`, `createPermission`, `createAbacPolicy`, `evaluateAbac`
|
|
75
|
+
- **API keys & roles**: `createApiKey`, `validateApiKey`, `rotateApiKey`, `createRole`, `assignRoleToKey`
|
|
76
|
+
- **Webhooks & audit**: `createWebhook`, `testWebhook`, `queryAuditLogs`, `listFailedDeliveries`
|
|
77
|
+
- **GDPR & regions**: `exportTenantData`, `purgeTenant`, `grantConsent`, `createRegion`, `migrateRegion`
|
|
78
|
+
- **Usage metering**: `recordUsage`, `aggregateUsage` (see [docs/usage-metering.md](https://github.com/stratum-hq/Stratum/blob/main/docs/usage-metering.md))
|
|
50
79
|
|
|
51
80
|
Low-level pool helpers are also exported:
|
|
52
81
|
|
|
@@ -64,11 +93,11 @@ await withTransaction(pool, async (client) => {
|
|
|
64
93
|
establishes the tenant context in two layers for the whole duration of the job
|
|
65
94
|
and tears both down when the job settles:
|
|
66
95
|
|
|
67
|
-
- **AsyncLocalStorage
|
|
96
|
+
- **AsyncLocalStorage**: the tenant is placed in the SDK's ALS store, so code
|
|
68
97
|
inside the job reads it through `Stratum.currentTenantId()` /
|
|
69
98
|
`Stratum.currentTenantContext()` exactly as a request handler would. Each job
|
|
70
99
|
gets its own store, so concurrent jobs cannot observe each other's tenant.
|
|
71
|
-
- **Postgres row-level security
|
|
100
|
+
- **Postgres row-level security**: the job runs through the data-plane
|
|
72
101
|
`withTenantContext` (`@stratum-hq/db-adapters`), which opens a transaction and
|
|
73
102
|
issues `SET LOCAL app.current_tenant_id`. Every query the job makes on the
|
|
74
103
|
provided client is confined to that tenant by RLS, so it cannot read or write
|
|
@@ -82,7 +111,7 @@ import { runScopedJob } from "@stratum-hq/lib";
|
|
|
82
111
|
// `pool` must connect as a NON-superuser, NON-BYPASSRLS role, or RLS is a no-op.
|
|
83
112
|
await runScopedJob(pool, tenantId, async (client) => {
|
|
84
113
|
Stratum.currentTenantId(); // === tenantId
|
|
85
|
-
// Confined to `tenantId` by RLS
|
|
114
|
+
// Confined to `tenantId` by RLS; no app-layer WHERE filter needed.
|
|
86
115
|
await client.query("SELECT * FROM invoices WHERE status = 'pending'");
|
|
87
116
|
});
|
|
88
117
|
```
|
|
@@ -96,7 +125,7 @@ ADR 0001 (`docs/adr/0001-postgres-rls-defense-in-depth.md`).
|
|
|
96
125
|
|
|
97
126
|
## Rate Limiting
|
|
98
127
|
|
|
99
|
-
`RateLimiter` is a standalone, storage-agnostic per-tenant rate-limiting primitive. It is not part of the `Stratum` facade (which is bound to a single `pg.Pool`)
|
|
128
|
+
`RateLimiter` is a standalone, storage-agnostic per-tenant rate-limiting primitive. It is not part of the `Stratum` facade (which is bound to a single `pg.Pool`). The point is that its storage is pluggable. It ships with a process-local in-memory store and a documented `RateLimitStore` contract you can implement over Redis, Postgres, or anything else.
|
|
100
129
|
|
|
101
130
|
> This is distinct from the HTTP-layer rate limiting in `@stratum-hq/control-plane`. Use this to embed per-tenant limits directly in an application.
|
|
102
131
|
|
|
@@ -115,7 +144,7 @@ const limiter = new RateLimiter({
|
|
|
115
144
|
// one tenant's usage never affects another's.
|
|
116
145
|
const res = await limiter.checkLimit(tenantId, "api");
|
|
117
146
|
if (!res.allowed) {
|
|
118
|
-
throw new Error(`rate limited
|
|
147
|
+
throw new Error(`rate limited, retry in ${res.retryAfter}s`);
|
|
119
148
|
}
|
|
120
149
|
// res: { allowed, limit, remaining, resetAt, retryAfter }
|
|
121
150
|
```
|
|
@@ -130,7 +159,7 @@ For each tenant the limit is resolved in order:
|
|
|
130
159
|
2. the static `limits[tenantId]` override,
|
|
131
160
|
3. `defaultLimit`.
|
|
132
161
|
|
|
133
|
-
`resolveLimit` is the seam for **config inheritance
|
|
162
|
+
`resolveLimit` is the seam for **config inheritance**: back it with `stratum.resolveConfig` to drive limits from the tenant config tree:
|
|
134
163
|
|
|
135
164
|
```typescript
|
|
136
165
|
const limiter = new RateLimiter({
|
|
@@ -162,9 +191,9 @@ interface RateLimitStore {
|
|
|
162
191
|
|
|
163
192
|
Implementation notes:
|
|
164
193
|
|
|
165
|
-
- **Redis
|
|
166
|
-
- **Postgres
|
|
167
|
-
- **In-memory
|
|
194
|
+
- **Redis**: `INCR key`, then on a reply of `1`, `PEXPIRE key windowMs`; derive `resetAt` from `now + PTTL`. A short Lua script keeps it atomic and returns count + TTL in one round trip.
|
|
195
|
+
- **Postgres**: `INSERT ... ON CONFLICT (key) DO UPDATE` returning the new count, resetting the row when `reset_at` has passed.
|
|
196
|
+
- **In-memory**: `MemoryRateLimitStore`; single-process only, holds one entry per distinct `tenantId:key` pair. For high key cardinality or multi-process deployments, use a store with native TTL eviction.
|
|
168
197
|
|
|
169
198
|
## Error Handling
|
|
170
199
|
|
|
@@ -177,7 +206,7 @@ try {
|
|
|
177
206
|
await stratum.setConfig(childId, "max_seats", { value: 999 });
|
|
178
207
|
} catch (err) {
|
|
179
208
|
if (err instanceof ConfigLockedError) {
|
|
180
|
-
// A parent locked this key
|
|
209
|
+
// A parent locked this key, so the child cannot override
|
|
181
210
|
}
|
|
182
211
|
}
|
|
183
212
|
```
|
package/dist/crypto.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"crypto.d.ts","sourceRoot":"","sources":["../src/crypto.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"crypto.d.ts","sourceRoot":"","sources":["../src/crypto.ts"],"names":[],"mappings":"AA+HA,qFAAqF;AACrF,wBAAgB,OAAO,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAEjD;AAED;;;2FAG2F;AAC3F,wBAAgB,OAAO,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAkBjD;AAED;;0EAE0E;AAC1E,wBAAgB,sBAAsB,CAAC,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAEvG;AAED;;+EAE+E;AAC/E,wBAAgB,sBAAsB,CAAC,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAM9G"}
|
package/dist/crypto.js
CHANGED
|
@@ -8,6 +8,7 @@ exports.decrypt = decrypt;
|
|
|
8
8
|
exports.encryptWithKeyMaterial = encryptWithKeyMaterial;
|
|
9
9
|
exports.decryptWithKeyMaterial = decryptWithKeyMaterial;
|
|
10
10
|
const node_crypto_1 = __importDefault(require("node:crypto"));
|
|
11
|
+
const core_1 = require("@stratum-hq/core");
|
|
11
12
|
const ALGORITHM = "aes-256-gcm";
|
|
12
13
|
const IV_LENGTH = 12;
|
|
13
14
|
const AUTH_TAG_LENGTH = 16;
|
|
@@ -25,30 +26,65 @@ function nodeEnvRequiringKeyMaterial() {
|
|
|
25
26
|
// salt is not secret; the key material is. Every other environment requires an
|
|
26
27
|
// explicit STRATUM_HKDF_SALT.
|
|
27
28
|
const NON_PRODUCTION_DEFAULT_SALT = "stratum-non-production-hkdf-salt-v1";
|
|
29
|
+
// Development and test only fallback key material.
|
|
30
|
+
const NON_PRODUCTION_DEFAULT_KEY = "stratum-dev-key";
|
|
31
|
+
// The shortest STRATUM_ENCRYPTION_KEY accepted outside development and test.
|
|
32
|
+
const MIN_KEY_BYTES = 32;
|
|
33
|
+
// The format of STRATUM_HKDF_SALT. Buffer.from(salt, "hex") silently shortens any other string.
|
|
34
|
+
const HEX_SALT = /^(?:[0-9a-fA-F]{2})+$/;
|
|
28
35
|
const HKDF_SALT = (() => {
|
|
29
|
-
if (process.env.STRATUM_HKDF_SALT) {
|
|
30
|
-
return Buffer.from(process.env.STRATUM_HKDF_SALT, "hex");
|
|
31
|
-
}
|
|
32
36
|
const strictEnv = nodeEnvRequiringKeyMaterial();
|
|
37
|
+
const salt = process.env.STRATUM_HKDF_SALT;
|
|
38
|
+
if (salt) {
|
|
39
|
+
if (!HEX_SALT.test(salt)) {
|
|
40
|
+
throw new Error("STRATUM_HKDF_SALT must be a non-empty, even-length hex string");
|
|
41
|
+
}
|
|
42
|
+
const bytes = Buffer.from(salt, "hex");
|
|
43
|
+
if (strictEnv && bytes.equals(Buffer.from(NON_PRODUCTION_DEFAULT_SALT, "utf8"))) {
|
|
44
|
+
throw new Error(`STRATUM_HKDF_SALT is the built-in development salt; set a random salt in ${strictEnv}`);
|
|
45
|
+
}
|
|
46
|
+
return bytes;
|
|
47
|
+
}
|
|
33
48
|
if (strictEnv) {
|
|
34
49
|
throw new Error(`STRATUM_HKDF_SALT must be set in ${strictEnv}`);
|
|
35
50
|
}
|
|
36
51
|
return Buffer.from(NON_PRODUCTION_DEFAULT_SALT, "utf8");
|
|
37
52
|
})();
|
|
53
|
+
// Outside development and test, the key is checked when the module loads, so
|
|
54
|
+
// a deployment without usable key material refuses to start instead of failing
|
|
55
|
+
// on its first sensitive operation.
|
|
56
|
+
(() => {
|
|
57
|
+
const strictEnv = nodeEnvRequiringKeyMaterial();
|
|
58
|
+
if (!strictEnv)
|
|
59
|
+
return;
|
|
60
|
+
const key = process.env.STRATUM_ENCRYPTION_KEY;
|
|
61
|
+
if (!key) {
|
|
62
|
+
throw new Error(`STRATUM_ENCRYPTION_KEY must be set in ${strictEnv}` +
|
|
63
|
+
(process.env.WEBHOOK_ENCRYPTION_KEY
|
|
64
|
+
? ". WEBHOOK_ENCRYPTION_KEY is read only in development and test; set STRATUM_ENCRYPTION_KEY to its value"
|
|
65
|
+
: ""));
|
|
66
|
+
}
|
|
67
|
+
if (key === NON_PRODUCTION_DEFAULT_KEY) {
|
|
68
|
+
throw new Error(`STRATUM_ENCRYPTION_KEY is the built-in development key; set random key material in ${strictEnv}`);
|
|
69
|
+
}
|
|
70
|
+
if (Buffer.byteLength(key, "utf8") < MIN_KEY_BYTES) {
|
|
71
|
+
throw new Error(`STRATUM_ENCRYPTION_KEY must be at least ${MIN_KEY_BYTES} bytes in ${strictEnv}`);
|
|
72
|
+
}
|
|
73
|
+
})();
|
|
38
74
|
function hkdfDeriveKey(keyMaterial, salt = HKDF_SALT, info = "stratum-aes-key") {
|
|
39
75
|
return Buffer.from(node_crypto_1.default.hkdfSync("sha256", Buffer.from(keyMaterial, "utf8"), salt, info, 32));
|
|
40
76
|
}
|
|
41
77
|
function getEncryptionKeyMaterial() {
|
|
42
|
-
const
|
|
78
|
+
const strictEnv = nodeEnvRequiringKeyMaterial();
|
|
79
|
+
// WEBHOOK_ENCRYPTION_KEY is a legacy name, read only in development and test.
|
|
80
|
+
const envKey = process.env.STRATUM_ENCRYPTION_KEY ?? (strictEnv ? undefined : process.env.WEBHOOK_ENCRYPTION_KEY);
|
|
43
81
|
if (envKey) {
|
|
44
82
|
return envKey;
|
|
45
83
|
}
|
|
46
|
-
const strictEnv = nodeEnvRequiringKeyMaterial();
|
|
47
84
|
if (strictEnv) {
|
|
48
85
|
throw new Error(`STRATUM_ENCRYPTION_KEY must be set in ${strictEnv}`);
|
|
49
86
|
}
|
|
50
|
-
|
|
51
|
-
return "stratum-dev-key";
|
|
87
|
+
return NON_PRODUCTION_DEFAULT_KEY;
|
|
52
88
|
}
|
|
53
89
|
function getEncryptionKey() {
|
|
54
90
|
return hkdfDeriveKey(getEncryptionKeyMaterial());
|
|
@@ -76,7 +112,7 @@ function decryptWithKey(encrypted, key) {
|
|
|
76
112
|
[ivHex, authTagHex, ciphertextHex] = parts;
|
|
77
113
|
}
|
|
78
114
|
else {
|
|
79
|
-
throw new
|
|
115
|
+
throw new core_1.DecryptionError("format");
|
|
80
116
|
}
|
|
81
117
|
const iv = Buffer.from(ivHex, "hex");
|
|
82
118
|
const authTag = Buffer.from(authTagHex, "hex");
|
|
@@ -105,10 +141,14 @@ function decrypt(encrypted) {
|
|
|
105
141
|
return decryptWithKey(encrypted, deriveKey(previousKey || getEncryptionKeyMaterial(), previousSalt));
|
|
106
142
|
}
|
|
107
143
|
catch {
|
|
108
|
-
// Both pairs failed
|
|
144
|
+
// Both pairs failed: report the original error
|
|
109
145
|
}
|
|
110
146
|
}
|
|
111
|
-
|
|
147
|
+
if (err instanceof core_1.DecryptionError)
|
|
148
|
+
throw err;
|
|
149
|
+
// Node reports a failed GCM authentication as "Unsupported state or
|
|
150
|
+
// unable to authenticate data", which does not say what to check.
|
|
151
|
+
throw new core_1.DecryptionError("authentication", { cause: err });
|
|
112
152
|
}
|
|
113
153
|
}
|
|
114
154
|
/** Encrypts a value with the key derived from `keyMaterial` and `saltHex`.
|
package/dist/crypto.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"crypto.js","sourceRoot":"","sources":["../src/crypto.ts"],"names":[],"mappings":";;;;;
|
|
1
|
+
{"version":3,"file":"crypto.js","sourceRoot":"","sources":["../src/crypto.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -2,15 +2,19 @@ export { Stratum, type StratumOptions } from "./stratum.js";
|
|
|
2
2
|
export { migrate, type MigrateOptions } from "./migrate.js";
|
|
3
3
|
export { migrateAllSchemas, type MigrateSchemasOptions, type MigrateSchemasResult, } from "./migrate-schemas.js";
|
|
4
4
|
export { STRATUM_TABLES } from "./stratum-tables.js";
|
|
5
|
+
export { STRATUM_CONTROL_ROLE } from "./migration-sql.js";
|
|
6
|
+
export { PINNED_SEARCH_PATH, pinnedQuery, schemaOfTable, withPinnedSearchPath } from "./pinned-query.js";
|
|
7
|
+
export { stratumPolicyDrift, STRATUM_RLS_TABLES, type PolicyDriftOptions, } from "./stratum-policies.js";
|
|
8
|
+
export { bootstrapRolesSql, inspectRoleModel, APP_READ_TABLES, type BootstrapRolesOptions, type RoleModelReport, type InspectRoleModelOptions, } from "./role-model.js";
|
|
5
9
|
export { withClient, withTransaction } from "./pool-helpers.js";
|
|
6
10
|
export { runScopedJob, type ScopedJobFn, type RunScopedJobOptions, } from "./scoped-job.js";
|
|
7
11
|
export { verifyWebhookSignature, signWebhookPayload, DEFAULT_WEBHOOK_TOLERANCE_SECONDS, type VerifyWebhookOptions, } from "./webhook-signature.js";
|
|
8
12
|
export { traced, getTracer, isTracingEnabled } from "./telemetry.js";
|
|
9
|
-
export { ErrorCode, StratumError, TenantNotFoundError, TenantAlreadyExistsError, TenantHasChildrenError, TenantCycleDetectedError, TenantArchivedError, TenantSuspendedError, TenantPendingError, TenantProvisioningError, InvalidTenantStateError, TenantContextNotFoundError, IsolationViolationError, IsolationStrategyUnsupportedError, PermissionLockedError, PermissionNotFoundError, PermissionRevocationDeniedError, ConfigLockedError, ConfigNotFoundError, ValidationError, UnauthorizedError, ForbiddenError, RegionInUseError, RegionNotActiveError, WebhookNotFoundError, WebhookDeliveryError, WebhookUrlValidationError, RegionNotFoundError, AbacPolicyNotFoundError, InvalidAbacOperatorError, AbacPolicyLockedError, } from "@stratum-hq/core";
|
|
10
|
-
export type { TenantNode, TenantContext, CreateTenantInput, UpdateTenantInput, MoveTenantInput, PaginationInput, PaginatedResult, ConfigEntry, SetConfigInput, BatchSetConfigEntry, ResolvedConfigEntry, ResolvedConfig, PermissionPolicy, CreatePermissionInput, UpdatePermissionInput, ResolvedPermission, } from "@stratum-hq/core";
|
|
13
|
+
export { ErrorCode, StratumError, TenantNotFoundError, TenantAlreadyExistsError, TenantHasChildrenError, TenantCycleDetectedError, TenantArchivedError, TenantSuspendedError, TenantPendingError, TenantProvisioningError, InvalidTenantStateError, TenantContextNotFoundError, IsolationViolationError, IsolationStrategyUnsupportedError, PermissionLockedError, PermissionNotFoundError, PermissionRevocationDeniedError, ConfigLockedError, ConfigNotFoundError, ValidationError, UnauthorizedError, ForbiddenError, RegionInUseError, RegionNotActiveError, WebhookNotFoundError, WebhookDeliveryError, WebhookUrlValidationError, RegionNotFoundError, AbacPolicyNotFoundError, InvalidAbacOperatorError, AbacPolicyLockedError, DecryptionError, } from "@stratum-hq/core";
|
|
14
|
+
export type { TenantNode, TenantContext, CreateTenantInput, UpdateTenantInput, MoveTenantInput, PaginationInput, PaginatedResult, ConfigEntry, SetConfigInput, BatchSetConfigEntry, ResolvedConfigEntry, ResolveConfigOptions, ResolvedConfig, PermissionPolicy, CreatePermissionInput, UpdatePermissionInput, ResolvedPermission, } from "@stratum-hq/core";
|
|
11
15
|
export { RateLimiter, MemoryRateLimitStore } from "./services/rate-limit-service.js";
|
|
12
16
|
export type { RateLimit, RateLimitResult, RateLimitState, RateLimitStore, RateLimitResolver, RateLimiterOptions, } from "./services/rate-limit-service.js";
|
|
13
|
-
export type { ApiKeyRecord, CreatedApiKey, ValidatedApiKey, CreateApiKeyOptions } from "./services/api-key-service.js";
|
|
17
|
+
export type { ApiKeyRecord, CreatedApiKey, ValidatedApiKey, CreateApiKeyOptions, ValidateApiKeyOptions, } from "./services/api-key-service.js";
|
|
14
18
|
export type { BatchCreateResult } from "./services/tenant-service.js";
|
|
15
19
|
export type { KeyRotationResult, KeyRotationSalts, KeyRotationUnreadableRow } from "./services/key-rotation-service.js";
|
|
16
20
|
export type { DeliveryStats } from "./services/event-service.js";
|
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,EAAE,OAAO,EAAE,KAAK,cAAc,EAAE,MAAM,cAAc,CAAC;AAC5D,OAAO,EAAE,OAAO,EAAE,KAAK,cAAc,EAAE,MAAM,cAAc,CAAC;AAC5D,OAAO,EACL,iBAAiB,EACjB,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,GAC1B,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAChE,OAAO,EACL,YAAY,EACZ,KAAK,WAAW,EAChB,KAAK,mBAAmB,GACzB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,sBAAsB,EACtB,kBAAkB,EAClB,iCAAiC,EACjC,KAAK,oBAAoB,GAC1B,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAKrE,OAAO,EACL,SAAS,EACT,YAAY,EACZ,mBAAmB,EACnB,wBAAwB,EACxB,sBAAsB,EACtB,wBAAwB,EACxB,mBAAmB,EACnB,oBAAoB,EACpB,kBAAkB,EAClB,uBAAuB,EACvB,uBAAuB,EACvB,0BAA0B,EAC1B,uBAAuB,EACvB,iCAAiC,EACjC,qBAAqB,EACrB,uBAAuB,EACvB,+BAA+B,EAC/B,iBAAiB,EACjB,mBAAmB,EACnB,eAAe,EACf,iBAAiB,EACjB,cAAc,EACd,gBAAgB,EAChB,oBAAoB,EACpB,oBAAoB,EACpB,oBAAoB,EACpB,yBAAyB,EACzB,mBAAmB,EACnB,uBAAuB,EACvB,wBAAwB,EACxB,qBAAqB,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.WebhookUrlValidationError = exports.WebhookDeliveryError = exports.WebhookNotFoundError = exports.RegionNotActiveError = exports.RegionInUseError = exports.ForbiddenError = exports.UnauthorizedError = exports.ValidationError = exports.ConfigNotFoundError = exports.ConfigLockedError = exports.PermissionRevocationDeniedError = exports.PermissionNotFoundError = exports.PermissionLockedError = exports.IsolationStrategyUnsupportedError = exports.IsolationViolationError = exports.TenantContextNotFoundError = exports.InvalidTenantStateError = exports.TenantProvisioningError = exports.TenantPendingError = exports.TenantSuspendedError = exports.TenantArchivedError = exports.TenantCycleDetectedError = exports.TenantHasChildrenError = exports.TenantAlreadyExistsError = exports.TenantNotFoundError = exports.StratumError = exports.ErrorCode = exports.isTracingEnabled = exports.getTracer = exports.traced = exports.DEFAULT_WEBHOOK_TOLERANCE_SECONDS = exports.signWebhookPayload = exports.verifyWebhookSignature = exports.runScopedJob = exports.withTransaction = exports.withClient = exports.APP_READ_TABLES = exports.inspectRoleModel = exports.bootstrapRolesSql = exports.STRATUM_RLS_TABLES = exports.stratumPolicyDrift = exports.withPinnedSearchPath = exports.schemaOfTable = exports.pinnedQuery = exports.PINNED_SEARCH_PATH = exports.STRATUM_CONTROL_ROLE = exports.STRATUM_TABLES = exports.migrateAllSchemas = exports.migrate = exports.Stratum = void 0;
|
|
4
|
+
exports.noopLogger = exports.defaultLogger = exports.MemoryRateLimitStore = exports.RateLimiter = exports.DecryptionError = exports.AbacPolicyLockedError = exports.InvalidAbacOperatorError = exports.AbacPolicyNotFoundError = exports.RegionNotFoundError = void 0;
|
|
4
5
|
var stratum_js_1 = require("./stratum.js");
|
|
5
6
|
Object.defineProperty(exports, "Stratum", { enumerable: true, get: function () { return stratum_js_1.Stratum; } });
|
|
6
7
|
var migrate_js_1 = require("./migrate.js");
|
|
@@ -9,6 +10,20 @@ var migrate_schemas_js_1 = require("./migrate-schemas.js");
|
|
|
9
10
|
Object.defineProperty(exports, "migrateAllSchemas", { enumerable: true, get: function () { return migrate_schemas_js_1.migrateAllSchemas; } });
|
|
10
11
|
var stratum_tables_js_1 = require("./stratum-tables.js");
|
|
11
12
|
Object.defineProperty(exports, "STRATUM_TABLES", { enumerable: true, get: function () { return stratum_tables_js_1.STRATUM_TABLES; } });
|
|
13
|
+
var migration_sql_js_1 = require("./migration-sql.js");
|
|
14
|
+
Object.defineProperty(exports, "STRATUM_CONTROL_ROLE", { enumerable: true, get: function () { return migration_sql_js_1.STRATUM_CONTROL_ROLE; } });
|
|
15
|
+
var pinned_query_js_1 = require("./pinned-query.js");
|
|
16
|
+
Object.defineProperty(exports, "PINNED_SEARCH_PATH", { enumerable: true, get: function () { return pinned_query_js_1.PINNED_SEARCH_PATH; } });
|
|
17
|
+
Object.defineProperty(exports, "pinnedQuery", { enumerable: true, get: function () { return pinned_query_js_1.pinnedQuery; } });
|
|
18
|
+
Object.defineProperty(exports, "schemaOfTable", { enumerable: true, get: function () { return pinned_query_js_1.schemaOfTable; } });
|
|
19
|
+
Object.defineProperty(exports, "withPinnedSearchPath", { enumerable: true, get: function () { return pinned_query_js_1.withPinnedSearchPath; } });
|
|
20
|
+
var stratum_policies_js_1 = require("./stratum-policies.js");
|
|
21
|
+
Object.defineProperty(exports, "stratumPolicyDrift", { enumerable: true, get: function () { return stratum_policies_js_1.stratumPolicyDrift; } });
|
|
22
|
+
Object.defineProperty(exports, "STRATUM_RLS_TABLES", { enumerable: true, get: function () { return stratum_policies_js_1.STRATUM_RLS_TABLES; } });
|
|
23
|
+
var role_model_js_1 = require("./role-model.js");
|
|
24
|
+
Object.defineProperty(exports, "bootstrapRolesSql", { enumerable: true, get: function () { return role_model_js_1.bootstrapRolesSql; } });
|
|
25
|
+
Object.defineProperty(exports, "inspectRoleModel", { enumerable: true, get: function () { return role_model_js_1.inspectRoleModel; } });
|
|
26
|
+
Object.defineProperty(exports, "APP_READ_TABLES", { enumerable: true, get: function () { return role_model_js_1.APP_READ_TABLES; } });
|
|
12
27
|
var pool_helpers_js_1 = require("./pool-helpers.js");
|
|
13
28
|
Object.defineProperty(exports, "withClient", { enumerable: true, get: function () { return pool_helpers_js_1.withClient; } });
|
|
14
29
|
Object.defineProperty(exports, "withTransaction", { enumerable: true, get: function () { return pool_helpers_js_1.withTransaction; } });
|
|
@@ -57,6 +72,7 @@ Object.defineProperty(exports, "RegionNotFoundError", { enumerable: true, get: f
|
|
|
57
72
|
Object.defineProperty(exports, "AbacPolicyNotFoundError", { enumerable: true, get: function () { return core_1.AbacPolicyNotFoundError; } });
|
|
58
73
|
Object.defineProperty(exports, "InvalidAbacOperatorError", { enumerable: true, get: function () { return core_1.InvalidAbacOperatorError; } });
|
|
59
74
|
Object.defineProperty(exports, "AbacPolicyLockedError", { enumerable: true, get: function () { return core_1.AbacPolicyLockedError; } });
|
|
75
|
+
Object.defineProperty(exports, "DecryptionError", { enumerable: true, get: function () { return core_1.DecryptionError; } });
|
|
60
76
|
var rate_limit_service_js_1 = require("./services/rate-limit-service.js");
|
|
61
77
|
Object.defineProperty(exports, "RateLimiter", { enumerable: true, get: function () { return rate_limit_service_js_1.RateLimiter; } });
|
|
62
78
|
Object.defineProperty(exports, "MemoryRateLimitStore", { enumerable: true, get: function () { return rate_limit_service_js_1.MemoryRateLimitStore; } });
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;AAAA,2CAA4D;AAAnD,qGAAA,OAAO,OAAA;AAChB,2CAA4D;AAAnD,qGAAA,OAAO,OAAA;AAChB,2DAI8B;AAH5B,uHAAA,iBAAiB,OAAA;AAInB,yDAAqD;AAA5C,mHAAA,cAAc,OAAA;AACvB,uDAA0D;AAAjD,wHAAA,oBAAoB,OAAA;AAC7B,qDAAyG;AAAhG,qHAAA,kBAAkB,OAAA;AAAE,8GAAA,WAAW,OAAA;AAAE,gHAAA,aAAa,OAAA;AAAE,uHAAA,oBAAoB,OAAA;AAC7E,6DAI+B;AAH7B,yHAAA,kBAAkB,OAAA;AAClB,yHAAA,kBAAkB,OAAA;AAGpB,iDAOyB;AANvB,kHAAA,iBAAiB,OAAA;AACjB,iHAAA,gBAAgB,OAAA;AAChB,gHAAA,eAAe,OAAA;AAKjB,qDAAgE;AAAvD,6GAAA,UAAU,OAAA;AAAE,kHAAA,eAAe,OAAA;AACpC,iDAIyB;AAHvB,6GAAA,YAAY,OAAA;AAId,+DAKgC;AAJ9B,8HAAA,sBAAsB,OAAA;AACtB,0HAAA,kBAAkB,OAAA;AAClB,yIAAA,iCAAiC,OAAA;AAGnC,+CAAqE;AAA5D,sGAAA,MAAM,OAAA;AAAE,yGAAA,SAAS,OAAA;AAAE,gHAAA,gBAAgB,OAAA;AAE5C,kFAAkF;AAClF,+EAA+E;AAC/E,8EAA8E;AAC9E,yCAiC0B;AAhCxB,iGAAA,SAAS,OAAA;AACT,oGAAA,YAAY,OAAA;AACZ,2GAAA,mBAAmB,OAAA;AACnB,gHAAA,wBAAwB,OAAA;AACxB,8GAAA,sBAAsB,OAAA;AACtB,gHAAA,wBAAwB,OAAA;AACxB,2GAAA,mBAAmB,OAAA;AACnB,4GAAA,oBAAoB,OAAA;AACpB,0GAAA,kBAAkB,OAAA;AAClB,+GAAA,uBAAuB,OAAA;AACvB,+GAAA,uBAAuB,OAAA;AACvB,kHAAA,0BAA0B,OAAA;AAC1B,+GAAA,uBAAuB,OAAA;AACvB,yHAAA,iCAAiC,OAAA;AACjC,6GAAA,qBAAqB,OAAA;AACrB,+GAAA,uBAAuB,OAAA;AACvB,uHAAA,+BAA+B,OAAA;AAC/B,yGAAA,iBAAiB,OAAA;AACjB,2GAAA,mBAAmB,OAAA;AACnB,uGAAA,eAAe,OAAA;AACf,yGAAA,iBAAiB,OAAA;AACjB,sGAAA,cAAc,OAAA;AACd,wGAAA,gBAAgB,OAAA;AAChB,4GAAA,oBAAoB,OAAA;AACpB,4GAAA,oBAAoB,OAAA;AACpB,4GAAA,oBAAoB,OAAA;AACpB,iHAAA,yBAAyB,OAAA;AACzB,2GAAA,mBAAmB,OAAA;AACnB,+GAAA,uBAAuB,OAAA;AACvB,gHAAA,wBAAwB,OAAA;AACxB,6GAAA,qBAAqB,OAAA;AACrB,uGAAA,eAAe,OAAA;AAwBjB,0EAAqF;AAA5E,oHAAA,WAAW,OAAA;AAAE,6HAAA,oBAAoB,OAAA;AAiC1C,yCAAwD;AAA/C,0GAAA,aAAa,OAAA;AAAE,uGAAA,UAAU,OAAA"}
|
|
@@ -4,6 +4,10 @@ export interface MigrateSchemasOptions {
|
|
|
4
4
|
concurrency?: number;
|
|
5
5
|
onProgress?: (schema: string, index: number, total: number) => void;
|
|
6
6
|
enforceRls?: boolean;
|
|
7
|
+
/** The control role for migration 032; see MigrateOptions.controlRole. */
|
|
8
|
+
controlRole?: string;
|
|
9
|
+
/** Lets migration 032 grant the control role to the login of `pool`; see MigrateOptions.applyControlRole. */
|
|
10
|
+
applyControlRole?: boolean;
|
|
7
11
|
}
|
|
8
12
|
export interface MigrateSchemasResult {
|
|
9
13
|
succeeded: string[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"migrate-schemas.d.ts","sourceRoot":"","sources":["../src/migrate-schemas.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,MAAM,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"migrate-schemas.d.ts","sourceRoot":"","sources":["../src/migrate-schemas.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,MAAM,IAAI,CAAC;AAKpB,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACpE,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,0EAA0E;IAC1E,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,6GAA6G;IAC7G,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED,MAAM,WAAW,oBAAoB;IACnC,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,MAAM,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,KAAK,CAAA;KAAE,EAAE,CAAC;CAC5C;AAED;;;;;GAKG;AACH,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,qBAAqB,GAC7B,OAAO,CAAC,oBAAoB,CAAC,CAoE/B"}
|
package/dist/migrate-schemas.js
CHANGED
|
@@ -7,6 +7,8 @@ exports.migrateAllSchemas = migrateAllSchemas;
|
|
|
7
7
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
8
8
|
const node_path_1 = __importDefault(require("node:path"));
|
|
9
9
|
const pool_helpers_js_1 = require("./pool-helpers.js");
|
|
10
|
+
const migration_sql_js_1 = require("./migration-sql.js");
|
|
11
|
+
const migrate_js_1 = require("./migrate.js");
|
|
10
12
|
/**
|
|
11
13
|
* Discover all tenant schemas (tenants with SCHEMA_PER_TENANT isolation)
|
|
12
14
|
* and run the standard migration set against each one.
|
|
@@ -14,7 +16,12 @@ const pool_helpers_js_1 = require("./pool-helpers.js");
|
|
|
14
16
|
* Continues on error: schemas that fail are collected in result.failed[].
|
|
15
17
|
*/
|
|
16
18
|
async function migrateAllSchemas(options) {
|
|
17
|
-
const { pool, concurrency = 5, onProgress, enforceRls } = options;
|
|
19
|
+
const { pool, concurrency = 5, onProgress, enforceRls, controlRole, applyControlRole } = options;
|
|
20
|
+
if (controlRole !== undefined)
|
|
21
|
+
(0, migration_sql_js_1.assertRoleName)(controlRole, "control role");
|
|
22
|
+
if (enforceRls) {
|
|
23
|
+
await (0, migrate_js_1.assertRoleSubjectToRls)(pool);
|
|
24
|
+
}
|
|
18
25
|
// Discover tenant schemas. The tenants registry is under FORCE RLS, so
|
|
19
26
|
// discovery runs under the control-plane bypass like every other lib read.
|
|
20
27
|
const { rows } = await (0, pool_helpers_js_1.withClient)(pool, (client) => client.query(`SELECT slug FROM tenants WHERE isolation_strategy = 'SCHEMA_PER_TENANT' AND (deleted_at IS NULL) ORDER BY slug`));
|
|
@@ -32,15 +39,16 @@ async function migrateAllSchemas(options) {
|
|
|
32
39
|
.readdirSync(migrationsDir)
|
|
33
40
|
.filter((f) => f.endsWith(".sql"))
|
|
34
41
|
.sort();
|
|
35
|
-
const
|
|
42
|
+
const superuser = await (0, migration_sql_js_1.isSuperuser)(pool);
|
|
43
|
+
const migrations = files.map((file) => ({
|
|
36
44
|
name: file,
|
|
37
|
-
sql: node_fs_1.default.readFileSync(node_path_1.default.join(migrationsDir, file), "utf-8"),
|
|
45
|
+
sql: (0, migration_sql_js_1.migrationSql)(file, node_fs_1.default.readFileSync(node_path_1.default.join(migrationsDir, file), "utf-8"), superuser),
|
|
38
46
|
}));
|
|
39
47
|
// Process schemas in chunks of `concurrency` size
|
|
40
48
|
let completed = 0;
|
|
41
49
|
for (let i = 0; i < schemas.length; i += concurrency) {
|
|
42
50
|
const chunk = schemas.slice(i, i + concurrency);
|
|
43
|
-
const results = await Promise.allSettled(chunk.map((schema) => migrateSchema(pool, schema,
|
|
51
|
+
const results = await Promise.allSettled(chunk.map((schema) => migrateSchema(pool, schema, migrations, enforceRls, controlRole, applyControlRole)));
|
|
44
52
|
for (let j = 0; j < results.length; j++) {
|
|
45
53
|
const schema = chunk[j];
|
|
46
54
|
const outcome = results[j];
|
|
@@ -60,7 +68,7 @@ async function migrateAllSchemas(options) {
|
|
|
60
68
|
}
|
|
61
69
|
return result;
|
|
62
70
|
}
|
|
63
|
-
async function migrateSchema(pool, schema, migrations, enforceRls) {
|
|
71
|
+
async function migrateSchema(pool, schema, migrations, enforceRls, controlRole, applyControlRole) {
|
|
64
72
|
// Use a hash of the schema name for a unique advisory lock key per schema
|
|
65
73
|
const lockKey = hashSchemaLock(schema);
|
|
66
74
|
const quoted = quoteIdent(schema);
|
|
@@ -69,7 +77,7 @@ async function migrateSchema(pool, schema, migrations, enforceRls) {
|
|
|
69
77
|
try {
|
|
70
78
|
await client.query("BEGIN");
|
|
71
79
|
// Advisory lock scoped to this schema
|
|
72
|
-
await client.query(`SELECT pg_advisory_xact_lock($1)`, [lockKey]);
|
|
80
|
+
await client.query(`SELECT pg_catalog.pg_advisory_xact_lock($1::pg_catalog.int8)`, [lockKey]);
|
|
73
81
|
// Set search_path to the tenant schema. public stays on the path because
|
|
74
82
|
// the migration SQL uses extension types and functions installed there
|
|
75
83
|
// (ltree, uuid_generate_v4).
|
|
@@ -93,6 +101,8 @@ async function migrateSchema(pool, schema, migrations, enforceRls) {
|
|
|
93
101
|
if (enforceRls) {
|
|
94
102
|
await client.query("SET LOCAL stratum.enforce_rls = 'on'");
|
|
95
103
|
}
|
|
104
|
+
await (0, migration_sql_js_1.setControlRole)(client, controlRole);
|
|
105
|
+
await (0, migration_sql_js_1.setApplyControlRole)(client, applyControlRole);
|
|
96
106
|
await client.query(migration.sql);
|
|
97
107
|
await client.query(`INSERT INTO ${quoted}._migrations (name) VALUES ($1)`, [
|
|
98
108
|
migration.name,
|
|
@@ -126,7 +136,7 @@ function hashSchemaLock(schema) {
|
|
|
126
136
|
return (hash ^ 0x5354524d) | 0; // XOR with "STRM"
|
|
127
137
|
}
|
|
128
138
|
function quoteIdent(name) {
|
|
129
|
-
// Simple identifier quoting
|
|
139
|
+
// Simple identifier quoting: disallow anything that could break out
|
|
130
140
|
if (!/^[a-z_][a-z0-9_]*$/i.test(name)) {
|
|
131
141
|
throw new Error(`Invalid schema name: ${name}`);
|
|
132
142
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"migrate-schemas.js","sourceRoot":"","sources":["../src/migrate-schemas.ts"],"names":[],"mappings":";;;;;
|
|
1
|
+
{"version":3,"file":"migrate-schemas.js","sourceRoot":"","sources":["../src/migrate-schemas.ts"],"names":[],"mappings":"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"}
|
package/dist/migrate.d.ts
CHANGED
|
@@ -3,6 +3,30 @@ export interface MigrateOptions {
|
|
|
3
3
|
pool: pg.Pool;
|
|
4
4
|
/** When true, SET stratum.enforce_rls = 'on' before running migrations (hard-fail on BYPASSRLS). */
|
|
5
5
|
enforceRls?: boolean;
|
|
6
|
+
/**
|
|
7
|
+
* The name of the NOLOGIN control role that migration 032 creates and names
|
|
8
|
+
* in its policies. Default: the stratum.control_role setting of the session
|
|
9
|
+
* (for example from ALTER DATABASE ... SET), then the role the database
|
|
10
|
+
* already uses, then `stratum_control`. Roles are cluster-wide, so set it
|
|
11
|
+
* when several databases on one server need separate control roles.
|
|
12
|
+
*/
|
|
13
|
+
controlRole?: string;
|
|
14
|
+
/**
|
|
15
|
+
* Lets migration 032 create the control role and grant it to the login of
|
|
16
|
+
* `pool`, then apply it. Set it only when `pool` is the library's admin
|
|
17
|
+
* login (the login behind adminPool), never the application's: a member
|
|
18
|
+
* of the control role passes every Stratum policy. Stratum's autoMigrate
|
|
19
|
+
* sets it when it runs on adminPool. Without it, 032 applies the control
|
|
20
|
+
* role only when that grants nothing new (a superuser, or a login that is
|
|
21
|
+
* already a member), and otherwise warns with the SQL to run.
|
|
22
|
+
*/
|
|
23
|
+
applyControlRole?: boolean;
|
|
6
24
|
}
|
|
25
|
+
/**
|
|
26
|
+
* Throws when the connecting role has BYPASSRLS, so row-level security would
|
|
27
|
+
* not apply to it. Used by every enforceRls entry point, so the check does not
|
|
28
|
+
* depend on migration 001 running.
|
|
29
|
+
*/
|
|
30
|
+
export declare function assertRoleSubjectToRls(pool: pg.Pool): Promise<void>;
|
|
7
31
|
export declare function migrate(options: MigrateOptions): Promise<void>;
|
|
8
32
|
//# sourceMappingURL=migrate.d.ts.map
|
package/dist/migrate.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"migrate.d.ts","sourceRoot":"","sources":["../src/migrate.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,MAAM,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"migrate.d.ts","sourceRoot":"","sources":["../src/migrate.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,MAAM,IAAI,CAAC;AAIpB,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC;IACd,oGAAoG;IACpG,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;;;;;OAQG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED;;;;GAIG;AACH,wBAAsB,sBAAsB,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAUzE;AAED,wBAAsB,OAAO,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAoFpE"}
|