pipework 0.8.20 → 0.10.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/CHANGELOG.md +16 -0
- package/dist/async/jobs/abortable-delay.d.ts +3 -0
- package/dist/async/jobs/abortable-delay.d.ts.map +1 -0
- package/dist/async/jobs/abortable-delay.js +19 -0
- package/dist/async/jobs/abortable-delay.js.map +1 -0
- package/dist/async/jobs/exclusion.d.ts +25 -0
- package/dist/async/jobs/exclusion.d.ts.map +1 -0
- package/dist/async/jobs/exclusion.js +80 -0
- package/dist/async/jobs/exclusion.js.map +1 -0
- package/dist/async/jobs/index.d.ts +2 -1
- package/dist/async/jobs/index.d.ts.map +1 -1
- package/dist/async/jobs/index.js.map +1 -1
- package/dist/async/jobs/queue.d.ts +26 -2
- package/dist/async/jobs/queue.d.ts.map +1 -1
- package/dist/async/jobs/queue.js +129 -36
- package/dist/async/jobs/queue.js.map +1 -1
- package/dist/async/jobs/table.d.ts +14 -0
- package/dist/async/jobs/table.d.ts.map +1 -0
- package/dist/async/jobs/table.js +115 -0
- package/dist/async/jobs/table.js.map +1 -0
- package/dist/auth/tenant/index.d.ts +1 -1
- package/dist/auth/tenant/index.d.ts.map +1 -1
- package/dist/auth/tenant/index.js +1 -1
- package/dist/auth/tenant/index.js.map +1 -1
- package/dist/auth/tenant/namespace.d.ts +3 -3
- package/dist/auth/tenant/namespace.d.ts.map +1 -1
- package/dist/auth/tenant/namespace.js +3 -3
- package/dist/auth/tenant/namespace.js.map +1 -1
- package/dist/auth/tenant/rls.d.ts +24 -3
- package/dist/auth/tenant/rls.d.ts.map +1 -1
- package/dist/auth/tenant/rls.js +59 -19
- package/dist/auth/tenant/rls.js.map +1 -1
- package/dist/cli/commands/db.d.ts +8 -0
- package/dist/cli/commands/db.d.ts.map +1 -1
- package/dist/cli/commands/db.js +129 -72
- package/dist/cli/commands/db.js.map +1 -1
- package/dist/core/config/load.d.ts +2 -0
- package/dist/core/config/load.d.ts.map +1 -1
- package/dist/core/config/load.js +7 -3
- package/dist/core/config/load.js.map +1 -1
- package/dist/core/config/namespace.d.ts +3 -0
- package/dist/core/config/namespace.d.ts.map +1 -1
- package/dist/core/config/schema.d.ts +6 -0
- package/dist/core/config/schema.d.ts.map +1 -1
- package/dist/core/config/schema.js +3 -0
- package/dist/core/config/schema.js.map +1 -1
- package/dist/core/pipework.d.ts +3 -3
- package/dist/core/pipework.d.ts.map +1 -1
- package/dist/core/pipework.js +13 -5
- package/dist/core/pipework.js.map +1 -1
- package/dist/core/surface/worker.d.ts.map +1 -1
- package/dist/core/surface/worker.js +26 -9
- package/dist/core/surface/worker.js.map +1 -1
- package/dist/data/db/expressions.d.ts +17 -5
- package/dist/data/db/expressions.d.ts.map +1 -1
- package/dist/data/db/expressions.js +30 -5
- package/dist/data/db/expressions.js.map +1 -1
- package/dist/data/db/filter.d.ts +9 -1
- package/dist/data/db/filter.d.ts.map +1 -1
- package/dist/data/db/filter.js +9 -1
- package/dist/data/db/filter.js.map +1 -1
- package/dist/data/db/namespace.d.ts +8 -0
- package/dist/data/db/namespace.d.ts.map +1 -1
- package/dist/data/db/pool.d.ts +2 -0
- package/dist/data/db/pool.d.ts.map +1 -1
- package/dist/data/db/pool.js.map +1 -1
- package/dist/data/db/serializable.d.ts +2 -0
- package/dist/data/db/serializable.d.ts.map +1 -1
- package/dist/data/db/serializable.js +2 -1
- package/dist/data/db/serializable.js.map +1 -1
- package/dist/data/domain/build-factory.js +2 -0
- package/dist/data/domain/build-factory.js.map +1 -1
- package/dist/data/domain/build-schema.js +7 -0
- package/dist/data/domain/build-schema.js.map +1 -1
- package/dist/data/domain/build-table.d.ts.map +1 -1
- package/dist/data/domain/build-table.js +9 -1
- package/dist/data/domain/build-table.js.map +1 -1
- package/dist/data/domain/field.d.ts +2 -0
- package/dist/data/domain/field.d.ts.map +1 -1
- package/dist/data/domain/field.js +4 -0
- package/dist/data/domain/field.js.map +1 -1
- package/dist/data/domain/types.d.ts +1 -1
- package/dist/data/domain/types.d.ts.map +1 -1
- package/dist/data/domain/types.js.map +1 -1
- package/dist/data/migrate/diff.js +7 -0
- package/dist/data/migrate/diff.js.map +1 -1
- package/dist/data/migrate/generate.js +1 -1
- package/dist/data/migrate/generate.js.map +1 -1
- package/dist/data/migrate/internal-definitions.d.ts.map +1 -1
- package/dist/data/migrate/internal-definitions.js +9 -0
- package/dist/data/migrate/internal-definitions.js.map +1 -1
- package/dist/data/migrate/rls-generate.d.ts +1 -1
- package/dist/data/migrate/rls-generate.d.ts.map +1 -1
- package/dist/data/migrate/rls-generate.js +20 -4
- package/dist/data/migrate/rls-generate.js.map +1 -1
- package/dist/data/migrate/snapshot.js +7 -5
- package/dist/data/migrate/snapshot.js.map +1 -1
- package/dist/data/migrate/sql-emit.js +4 -1
- package/dist/data/migrate/sql-emit.js.map +1 -1
- package/dist/request/context/create.d.ts +6 -0
- package/dist/request/context/create.d.ts.map +1 -1
- package/dist/request/context/create.js +6 -0
- package/dist/request/context/create.js.map +1 -1
- package/dist/request/context/run-in-transaction.d.ts.map +1 -1
- package/dist/request/context/run-in-transaction.js +8 -2
- package/dist/request/context/run-in-transaction.js.map +1 -1
- package/dist/request/context/types.d.ts +4 -0
- package/dist/request/context/types.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
// The queue table as a pipe.define() definition, built from queue config.
|
|
2
|
+
//
|
|
3
|
+
// The table shape used to live in a hand-written schema.sql that no TS code
|
|
4
|
+
// referenced, so `pipework generate` could not emit it and a regenerated
|
|
5
|
+
// baseline silently dropped the queue migration. Expressing it as a
|
|
6
|
+
// DefinedTable puts it on the same generate path as every other table:
|
|
7
|
+
// createQueue() registers the definition here, and the migration generator
|
|
8
|
+
// merges this registry through its internalDefinitions hook.
|
|
9
|
+
import { define } from '../../data/domain/index.js';
|
|
10
|
+
import { field } from '../../data/domain/field.js';
|
|
11
|
+
import { schema } from '../../data/schema/namespace.js';
|
|
12
|
+
import { index } from '../../data/db/idx/index.js';
|
|
13
|
+
import { quoteIdentifier } from '../../data/db/identifiers.js';
|
|
14
|
+
import { sql } from '../../data/db/sql.js';
|
|
15
|
+
export const QUEUE_STATUSES = ['pending', 'claimed', 'running', 'completed', 'failed', 'cancelled', 'dead_letter'];
|
|
16
|
+
/** Column names every queue table owns — exclusion columns must not collide with these. */
|
|
17
|
+
export const QUEUE_BASE_COLUMNS = new Set([
|
|
18
|
+
'id', 'job_type', 'priority', 'status', 'payload', 'output', 'error_payload',
|
|
19
|
+
'created_lsn', 'queued_at', 'claimed_at', 'heartbeat_at', 'completed_at',
|
|
20
|
+
'worker_id', 'attempt_count', 'max_attempts', 'next_retry_at',
|
|
21
|
+
]);
|
|
22
|
+
function queueFields(exclusion) {
|
|
23
|
+
const fields = {
|
|
24
|
+
id: field.uuid().primaryKey().defaultRandom(),
|
|
25
|
+
jobType: field.text(),
|
|
26
|
+
priority: field.integer().default(0),
|
|
27
|
+
status: field.enum(QUEUE_STATUSES).default('pending'),
|
|
28
|
+
payload: field.jsonb(schema.check.json()),
|
|
29
|
+
output: field.jsonb(schema.check.json()).nullable(),
|
|
30
|
+
errorPayload: field.jsonb(schema.check.json()).nullable(),
|
|
31
|
+
createdLsn: field.pgLsn().default('currentWal'),
|
|
32
|
+
queuedAt: field.timestamp().default('now'),
|
|
33
|
+
claimedAt: field.timestamp().nullable(),
|
|
34
|
+
heartbeatAt: field.timestamp().nullable(),
|
|
35
|
+
completedAt: field.timestamp().nullable(),
|
|
36
|
+
workerId: field.text().nullable(),
|
|
37
|
+
attemptCount: field.integer().default(0),
|
|
38
|
+
maxAttempts: field.integer().default(1),
|
|
39
|
+
nextRetryAt: field.timestamp().nullable(),
|
|
40
|
+
};
|
|
41
|
+
if (exclusion?.tenantColumn !== undefined) {
|
|
42
|
+
fields[exclusion.tenantColumn] = field.uuid().tenant();
|
|
43
|
+
}
|
|
44
|
+
if (exclusion?.arrayColumn !== undefined) {
|
|
45
|
+
const column = exclusion.arrayElementType === 'text'
|
|
46
|
+
? schema.col.text(exclusion.arrayColumn).array()
|
|
47
|
+
: schema.col.uuid(exclusion.arrayColumn).array();
|
|
48
|
+
fields[exclusion.arrayColumn] = field.custom(column, schema.check.array(schema.check.string())).nullable();
|
|
49
|
+
}
|
|
50
|
+
if (exclusion?.blocksAllColumn !== undefined) {
|
|
51
|
+
fields[exclusion.blocksAllColumn] = field.boolean().default(false);
|
|
52
|
+
}
|
|
53
|
+
return fields;
|
|
54
|
+
}
|
|
55
|
+
const queueTables = new Map();
|
|
56
|
+
/**
|
|
57
|
+
* Builds (or reuses) the DefinedTable for a queue. Cached per table name so
|
|
58
|
+
* repeated createQueue() calls share one definition; the same name with a
|
|
59
|
+
* different shape is a config error, not a silent overwrite.
|
|
60
|
+
*/
|
|
61
|
+
export function queueDefinition(tableName, exclusion) {
|
|
62
|
+
const configKey = JSON.stringify(exclusion ?? null);
|
|
63
|
+
const existing = queueTables.get(tableName);
|
|
64
|
+
if (existing !== undefined) {
|
|
65
|
+
if (existing.configKey !== configKey) {
|
|
66
|
+
throw new Error(`[pipework] Queue table "${tableName}" is already defined with a different exclusion config.\n\n` +
|
|
67
|
+
` Existing: ${existing.configKey}\n` +
|
|
68
|
+
` New: ${configKey}\n` +
|
|
69
|
+
' One table, one shape — give the second queue its own table name.\n');
|
|
70
|
+
}
|
|
71
|
+
return existing.definition;
|
|
72
|
+
}
|
|
73
|
+
const definition = define(tableName, queueFields(exclusion), {
|
|
74
|
+
indexes: (t) => ({
|
|
75
|
+
claimable: index(`idx_${tableName}_claimable`)
|
|
76
|
+
.on(t['priority'].desc(), t['queuedAt'].asc())
|
|
77
|
+
.where(sql.raw(`status = 'pending'`)),
|
|
78
|
+
stale: index(`idx_${tableName}_stale`)
|
|
79
|
+
.on(t['heartbeatAt'])
|
|
80
|
+
.where(sql.raw(`status IN ('claimed', 'running')`)),
|
|
81
|
+
retryable: index(`idx_${tableName}_retryable`)
|
|
82
|
+
.on(t['nextRetryAt'])
|
|
83
|
+
.where(sql.raw(`status = 'pending' AND next_retry_at IS NOT NULL`)),
|
|
84
|
+
// The claim query's exclusion checks run under SERIALIZABLE; without a
|
|
85
|
+
// matching index the overlap NOT EXISTS scans the whole claimed/running
|
|
86
|
+
// set and takes coarse predicate locks, so claimers with disjoint arrays
|
|
87
|
+
// SSI-conflict and starve. Partial GIN keeps the locks at the key level;
|
|
88
|
+
// fastupdate must be off — a GIN pending list forces relation-level
|
|
89
|
+
// predicate locks, voiding the granularity.
|
|
90
|
+
...(exclusion?.arrayColumn !== undefined ? {
|
|
91
|
+
exclusionArray: index(`idx_${tableName}_excl_arr`)
|
|
92
|
+
.using('gin', t[exclusion.arrayColumn])
|
|
93
|
+
.with({ fastupdate: 'off' })
|
|
94
|
+
.where(sql.raw(`status IN ('claimed', 'running')`)),
|
|
95
|
+
} : {}),
|
|
96
|
+
// Same starvation mechanism for the blocks-all NOT EXISTS: without its
|
|
97
|
+
// own index that check scans every claimed/running row. This partial
|
|
98
|
+
// index contains only active blocks-all jobs (normally none), so
|
|
99
|
+
// ordinary claims neither read beyond it nor insert into it — only a
|
|
100
|
+
// genuine blocks-all claim conflicts, which is the intended semantics.
|
|
101
|
+
...(exclusion?.blocksAllColumn !== undefined ? {
|
|
102
|
+
exclusionBlocks: index(`idx_${tableName}_excl_blocks`)
|
|
103
|
+
.on(t[exclusion.tenantColumn ?? exclusion.blocksAllColumn])
|
|
104
|
+
.where(sql.raw(`status IN ('claimed', 'running') AND ${quoteIdentifier(exclusion.blocksAllColumn, 'exclusion column')} = true`)),
|
|
105
|
+
} : {}),
|
|
106
|
+
}),
|
|
107
|
+
});
|
|
108
|
+
queueTables.set(tableName, { definition, configKey });
|
|
109
|
+
return definition;
|
|
110
|
+
}
|
|
111
|
+
/** Registered queue definitions — merged into `pipework generate` via internalDefinitions. */
|
|
112
|
+
export function queueDefinitions() {
|
|
113
|
+
return new Map([...queueTables].map(([name, entry]) => [name, entry.definition]));
|
|
114
|
+
}
|
|
115
|
+
//# sourceMappingURL=table.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"table.js","sourceRoot":"","sources":["../../../src/async/jobs/table.ts"],"names":[],"mappings":"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"}
|
|
@@ -2,7 +2,7 @@ export type { TenantConfig, TenantContext } from './types.js';
|
|
|
2
2
|
export { validateTenantId, validateSessionVarName, validateSessionVarValue } from './validate.js';
|
|
3
3
|
export { propagateTenantLocals } from './propagate.js';
|
|
4
4
|
export { extractTenant, type TenantExtractionResult } from './extract.js';
|
|
5
|
-
export { tenantIsolationPolicy, enableTenantRls,
|
|
5
|
+
export { tenantIsolationPolicy, enableTenantRls, verifyPrincipalContext } from './rls.js';
|
|
6
6
|
export { scopeTable, getScopeConfig, isTenantScoped, type ScopeConfig } from './scope.js';
|
|
7
7
|
export { createScopedDb, createTenantGuardDb } from './scoped-db.js';
|
|
8
8
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/auth/tenant/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAC7D,OAAO,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAA;AACjG,OAAO,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAA;AACtD,OAAO,EAAE,aAAa,EAAE,KAAK,sBAAsB,EAAE,MAAM,cAAc,CAAA;AACzE,OAAO,EAAE,qBAAqB,EAAE,eAAe,EAAE,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/auth/tenant/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAC7D,OAAO,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAA;AACjG,OAAO,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAA;AACtD,OAAO,EAAE,aAAa,EAAE,KAAK,sBAAsB,EAAE,MAAM,cAAc,CAAA;AACzE,OAAO,EAAE,qBAAqB,EAAE,eAAe,EAAE,sBAAsB,EAAE,MAAM,UAAU,CAAA;AACzF,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,cAAc,EAAE,KAAK,WAAW,EAAE,MAAM,YAAY,CAAA;AACzF,OAAO,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAA"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export { validateTenantId, validateSessionVarName, validateSessionVarValue } from './validate.js';
|
|
2
2
|
export { propagateTenantLocals } from './propagate.js';
|
|
3
3
|
export { extractTenant } from './extract.js';
|
|
4
|
-
export { tenantIsolationPolicy, enableTenantRls,
|
|
4
|
+
export { tenantIsolationPolicy, enableTenantRls, verifyPrincipalContext } from './rls.js';
|
|
5
5
|
export { scopeTable, getScopeConfig, isTenantScoped } from './scope.js';
|
|
6
6
|
export { createScopedDb, createTenantGuardDb } from './scoped-db.js';
|
|
7
7
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/auth/tenant/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAA;AACjG,OAAO,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAA;AACtD,OAAO,EAAE,aAAa,EAA+B,MAAM,cAAc,CAAA;AACzE,OAAO,EAAE,qBAAqB,EAAE,eAAe,EAAE,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/auth/tenant/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAA;AACjG,OAAO,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAA;AACtD,OAAO,EAAE,aAAa,EAA+B,MAAM,cAAc,CAAA;AACzE,OAAO,EAAE,qBAAqB,EAAE,eAAe,EAAE,sBAAsB,EAAE,MAAM,UAAU,CAAA;AACzF,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,cAAc,EAAoB,MAAM,YAAY,CAAA;AACzF,OAAO,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAA"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { validateTenantId } from './validate.js';
|
|
2
|
-
import { enableTenantRls, tenantIsolationPolicy,
|
|
2
|
+
import { enableTenantRls, tenantIsolationPolicy, verifyPrincipalContext } from './rls.js';
|
|
3
3
|
import { propagateTenantLocals } from './propagate.js';
|
|
4
4
|
import { extractTenant } from './extract.js';
|
|
5
5
|
import { isTenantScoped } from './scope.js';
|
|
@@ -11,8 +11,8 @@ export declare const tenant: {
|
|
|
11
11
|
rls: typeof enableTenantRls;
|
|
12
12
|
/** Returns the SQL string for a tenant isolation RLS policy — use in migrations. */
|
|
13
13
|
policy: typeof tenantIsolationPolicy;
|
|
14
|
-
/** Cross-checks ALS tenant against Postgres
|
|
15
|
-
verify: typeof
|
|
14
|
+
/** Cross-checks the ALS principal (tenant, user, asOf) against the Postgres session GUCs — defense-in-depth. */
|
|
15
|
+
verify: typeof verifyPrincipalContext;
|
|
16
16
|
/** Sets pipework.tenant_id on the Postgres connection via set_config(). */
|
|
17
17
|
propagate: typeof propagateTenantLocals;
|
|
18
18
|
/** Extracts tenant ID from auth context using the configured extraction strategy. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"namespace.d.ts","sourceRoot":"","sources":["../../../src/auth/tenant/namespace.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAA;AAChD,OAAO,EAAE,eAAe,EAAE,qBAAqB,EAAE,
|
|
1
|
+
{"version":3,"file":"namespace.d.ts","sourceRoot":"","sources":["../../../src/auth/tenant/namespace.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAA;AAChD,OAAO,EAAE,eAAe,EAAE,qBAAqB,EAAE,sBAAsB,EAAE,MAAM,UAAU,CAAA;AACzF,OAAO,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAA;AACtD,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;AAC5C,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAE3C,sGAAsG;AACtG,eAAO,MAAM,MAAM;IACjB,wEAAwE;;IAExE,kFAAkF;;IAElF,oFAAoF;;IAEpF,gHAAgH;;IAEhH,2EAA2E;;IAE3E,qFAAqF;;IAErF,+FAA+F;;CAEhG,CAAA"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { validateTenantId } from './validate.js';
|
|
2
|
-
import { enableTenantRls, tenantIsolationPolicy,
|
|
2
|
+
import { enableTenantRls, tenantIsolationPolicy, verifyPrincipalContext } from './rls.js';
|
|
3
3
|
import { propagateTenantLocals } from './propagate.js';
|
|
4
4
|
import { extractTenant } from './extract.js';
|
|
5
5
|
import { isTenantScoped } from './scope.js';
|
|
@@ -11,8 +11,8 @@ export const tenant = {
|
|
|
11
11
|
rls: enableTenantRls,
|
|
12
12
|
/** Returns the SQL string for a tenant isolation RLS policy — use in migrations. */
|
|
13
13
|
policy: tenantIsolationPolicy,
|
|
14
|
-
/** Cross-checks ALS tenant against Postgres
|
|
15
|
-
verify:
|
|
14
|
+
/** Cross-checks the ALS principal (tenant, user, asOf) against the Postgres session GUCs — defense-in-depth. */
|
|
15
|
+
verify: verifyPrincipalContext,
|
|
16
16
|
/** Sets pipework.tenant_id on the Postgres connection via set_config(). */
|
|
17
17
|
propagate: propagateTenantLocals,
|
|
18
18
|
/** Extracts tenant ID from auth context using the configured extraction strategy. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"namespace.js","sourceRoot":"","sources":["../../../src/auth/tenant/namespace.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAA;AAChD,OAAO,EAAE,eAAe,EAAE,qBAAqB,EAAE,
|
|
1
|
+
{"version":3,"file":"namespace.js","sourceRoot":"","sources":["../../../src/auth/tenant/namespace.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAA;AAChD,OAAO,EAAE,eAAe,EAAE,qBAAqB,EAAE,sBAAsB,EAAE,MAAM,UAAU,CAAA;AACzF,OAAO,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAA;AACtD,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;AAC5C,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAE3C,sGAAsG;AACtG,MAAM,CAAC,MAAM,MAAM,GAAG;IACpB,wEAAwE;IACxE,QAAQ,EAAE,gBAAgB;IAC1B,kFAAkF;IAClF,GAAG,EAAE,eAAe;IACpB,oFAAoF;IACpF,MAAM,EAAE,qBAAqB;IAC7B,gHAAgH;IAChH,MAAM,EAAE,sBAAsB;IAC9B,2EAA2E;IAC3E,SAAS,EAAE,qBAAqB;IAChC,qFAAqF;IACrF,OAAO,EAAE,aAAa;IACtB,+FAA+F;IAC/F,cAAc;CACf,CAAA"}
|
|
@@ -1,5 +1,26 @@
|
|
|
1
1
|
import type { DB } from '../../data/db/index.js';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
/**
|
|
3
|
+
* SQL types a tenant discriminator column may be. The union members are
|
|
4
|
+
* literal Postgres type names, so they double as the cast target — a tenant
|
|
5
|
+
* policy compares the column against the session GUC cast to this type. A
|
|
6
|
+
* field whose kind is not one of these cannot be a tenant key; the generate
|
|
7
|
+
* layer rejects it before reaching here (see rls-generate.ts).
|
|
8
|
+
*/
|
|
9
|
+
export type TenantKeyType = 'uuid' | 'text' | 'integer' | 'bigint';
|
|
10
|
+
export declare function tenantIsolationPolicy(table: string, column?: string, keyType?: TenantKeyType, sessionVar?: string, force?: boolean): string;
|
|
11
|
+
export declare function enableTenantRls(db: DB, table: string, column?: string, keyType?: TenantKeyType, force?: boolean): Promise<void>;
|
|
12
|
+
/** The principal axes carried on the request/job context and propagated as GUCs. */
|
|
13
|
+
export interface ExpectedPrincipal {
|
|
14
|
+
readonly tenant: string | null;
|
|
15
|
+
readonly user: string | null;
|
|
16
|
+
readonly asOf: string | null;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Cross-checks the application's principal (tenant, user, asOf) against the GUCs
|
|
20
|
+
* actually set on the Postgres session — defense-in-depth against an
|
|
21
|
+
* AsyncLocalStorage context swap propagating one principal's identity onto
|
|
22
|
+
* another's transaction. All three axes are checked together so a mismatch on
|
|
23
|
+
* any one aborts before a query can read the wrong rows.
|
|
24
|
+
*/
|
|
25
|
+
export declare function verifyPrincipalContext(db: DB, expected: ExpectedPrincipal): Promise<void>;
|
|
5
26
|
//# sourceMappingURL=rls.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rls.d.ts","sourceRoot":"","sources":["../../../src/auth/tenant/rls.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,wBAAwB,CAAA;AAIhD,wBAAgB,qBAAqB,
|
|
1
|
+
{"version":3,"file":"rls.d.ts","sourceRoot":"","sources":["../../../src/auth/tenant/rls.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,wBAAwB,CAAA;AAIhD;;;;;;GAMG;AACH,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,MAAM,GAAG,SAAS,GAAG,QAAQ,CAAA;AAqBlE,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,MAAM,EACb,MAAM,GAAE,MAAoB,EAC5B,OAAO,GAAE,aAAsB,EAC/B,UAAU,GAAE,MAA6B,EACzC,KAAK,GAAE,OAAc,GACpB,MAAM,CAUR;AAED,wBAAsB,eAAe,CACnC,EAAE,EAAE,EAAE,EACN,KAAK,EAAE,MAAM,EACb,MAAM,GAAE,MAAoB,EAC5B,OAAO,GAAE,aAAsB,EAC/B,KAAK,GAAE,OAAc,GACpB,OAAO,CAAC,IAAI,CAAC,CASf;AAED,oFAAoF;AACpF,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;IAC9B,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;CAC7B;AAED;;;;;;GAMG;AACH,wBAAsB,sBAAsB,CAC1C,EAAE,EAAE,EAAE,EACN,QAAQ,EAAE,iBAAiB,GAC1B,OAAO,CAAC,IAAI,CAAC,CAoBf"}
|
package/dist/auth/tenant/rls.js
CHANGED
|
@@ -1,35 +1,75 @@
|
|
|
1
1
|
import { sql } from '../../data/db/sql.js';
|
|
2
2
|
import { quoteIdentifier } from '../../data/db/identifiers.js';
|
|
3
3
|
import { validateSessionVarName } from './validate.js';
|
|
4
|
-
|
|
4
|
+
function tenantPolicyName(table) {
|
|
5
|
+
return `pipework_tenant_isolation_${table.replace(/[^a-zA-Z0-9_]/g, '_')}`;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* The USING predicate of a tenant isolation policy.
|
|
9
|
+
*
|
|
10
|
+
* The cast (`::uuid` etc.) is required: current_setting returns text, and
|
|
11
|
+
* Postgres will not implicitly compare `uuid = text`. NULLIF(...,'') means an
|
|
12
|
+
* unset or explicitly-empty session GUC yields NULL — a deny-by-default match —
|
|
13
|
+
* instead of throwing on `''::uuid`. (current_setting(var, true) is itself NULL
|
|
14
|
+
* when the GUC was never set; the NULLIF only guards the empty-string case that
|
|
15
|
+
* set_config can produce.)
|
|
16
|
+
*/
|
|
17
|
+
function tenantUsingClause(column, keyType, sessionVar) {
|
|
18
|
+
const c = quoteIdentifier(column, 'column');
|
|
19
|
+
return `${c} = NULLIF(current_setting('${sessionVar}', true), '')::${keyType}`;
|
|
20
|
+
}
|
|
21
|
+
export function tenantIsolationPolicy(table, column = 'tenant_id', keyType = 'uuid', sessionVar = 'pipework.tenant_id', force = true) {
|
|
5
22
|
validateSessionVarName(sessionVar);
|
|
6
23
|
const t = quoteIdentifier(table, 'table');
|
|
7
|
-
const c = quoteIdentifier(column, 'column');
|
|
8
|
-
const policyName = `pipework_tenant_isolation_${table.replace(/[^a-zA-Z0-9_]/g, '_')}`;
|
|
9
24
|
return [
|
|
10
25
|
`ALTER TABLE ${t} ENABLE ROW LEVEL SECURITY`,
|
|
11
|
-
`ALTER TABLE ${t} FORCE ROW LEVEL SECURITY
|
|
12
|
-
`CREATE POLICY ${quoteIdentifier(
|
|
26
|
+
...(force ? [`ALTER TABLE ${t} FORCE ROW LEVEL SECURITY`] : []),
|
|
27
|
+
`CREATE POLICY ${quoteIdentifier(tenantPolicyName(table), 'policy')} ON ${t} USING (${tenantUsingClause(column, keyType, sessionVar)})`,
|
|
13
28
|
].join(';\n');
|
|
14
29
|
}
|
|
15
|
-
export async function enableTenantRls(db, table, column = 'tenant_id') {
|
|
30
|
+
export async function enableTenantRls(db, table, column = 'tenant_id', keyType = 'uuid', force = true) {
|
|
16
31
|
const t = quoteIdentifier(table, 'table');
|
|
17
|
-
const
|
|
18
|
-
const policyName = quoteIdentifier(`pipework_tenant_isolation_${table.replace(/[^a-zA-Z0-9_]/g, '_')}`, 'policy');
|
|
32
|
+
const policyName = quoteIdentifier(tenantPolicyName(table), 'policy');
|
|
19
33
|
await db.execute(sql.raw(`ALTER TABLE ${t} ENABLE ROW LEVEL SECURITY`));
|
|
20
|
-
|
|
21
|
-
|
|
34
|
+
if (force)
|
|
35
|
+
await db.execute(sql.raw(`ALTER TABLE ${t} FORCE ROW LEVEL SECURITY`));
|
|
36
|
+
await db.execute(sql.raw(`CREATE POLICY ${policyName} ON ${t} USING (${tenantUsingClause(column, keyType, 'pipework.tenant_id')})`));
|
|
22
37
|
}
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
38
|
+
/**
|
|
39
|
+
* Cross-checks the application's principal (tenant, user, asOf) against the GUCs
|
|
40
|
+
* actually set on the Postgres session — defense-in-depth against an
|
|
41
|
+
* AsyncLocalStorage context swap propagating one principal's identity onto
|
|
42
|
+
* another's transaction. All three axes are checked together so a mismatch on
|
|
43
|
+
* any one aborts before a query can read the wrong rows.
|
|
44
|
+
*/
|
|
45
|
+
export async function verifyPrincipalContext(db, expected) {
|
|
46
|
+
const result = await db.execute(sql `SELECT
|
|
47
|
+
current_setting('pipework.tenant_id', true) AS tenant_id,
|
|
48
|
+
current_setting('pipework.user_id', true) AS user_id,
|
|
49
|
+
current_setting('pipework.asof', true) AS asof`);
|
|
50
|
+
const row = result[0];
|
|
51
|
+
const mismatches = [];
|
|
52
|
+
if (normalizeGuc(row?.tenant_id) !== expected.tenant)
|
|
53
|
+
mismatches.push(formatAxis('tenant', expected.tenant, normalizeGuc(row?.tenant_id)));
|
|
54
|
+
if (normalizeGuc(row?.user_id) !== expected.user)
|
|
55
|
+
mismatches.push(formatAxis('user', expected.user, normalizeGuc(row?.user_id)));
|
|
56
|
+
if (normalizeGuc(row?.asof) !== expected.asOf)
|
|
57
|
+
mismatches.push(formatAxis('asOf', expected.asOf, normalizeGuc(row?.asof)));
|
|
58
|
+
if (mismatches.length > 0) {
|
|
59
|
+
throw new Error(`[pipework] Principal context mismatch between application and database.\n\n` +
|
|
60
|
+
mismatches.join('\n') + `\n\n` +
|
|
31
61
|
` This indicates an AsyncLocalStorage context integrity failure.\n` +
|
|
32
|
-
` The request has been aborted to prevent cross-tenant data access.\n`);
|
|
62
|
+
` The request has been aborted to prevent cross-tenant or cross-user data access.\n`);
|
|
33
63
|
}
|
|
34
64
|
}
|
|
65
|
+
// current_setting(var, true) is NULL when the GUC was never set; set_config can
|
|
66
|
+
// also leave an empty string. Both collapse to null so they compare equal to an
|
|
67
|
+
// absent application-side axis.
|
|
68
|
+
function normalizeGuc(raw) {
|
|
69
|
+
const v = raw ?? null;
|
|
70
|
+
return v === '' ? null : v;
|
|
71
|
+
}
|
|
72
|
+
function formatAxis(axis, app, pg) {
|
|
73
|
+
return ` ${axis}: application context = ${app ?? '(null)'}, PostgreSQL session = ${pg ?? '(null)'}`;
|
|
74
|
+
}
|
|
35
75
|
//# sourceMappingURL=rls.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rls.js","sourceRoot":"","sources":["../../../src/auth/tenant/rls.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,sBAAsB,CAAA;AAE1C,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAA;AAC9D,OAAO,EAAE,sBAAsB,EAAE,MAAM,eAAe,CAAA;
|
|
1
|
+
{"version":3,"file":"rls.js","sourceRoot":"","sources":["../../../src/auth/tenant/rls.ts"],"names":[],"mappings":"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"}
|
|
@@ -7,4 +7,12 @@ export interface DbOptions {
|
|
|
7
7
|
readonly adminUrl?: string | undefined;
|
|
8
8
|
}
|
|
9
9
|
export declare function db(opts: DbOptions): Promise<void>;
|
|
10
|
+
/**
|
|
11
|
+
* The DML grants for the non-owner runtime role, applied at provision time on a
|
|
12
|
+
* connection authenticated as the owner. The two ALTER DEFAULT PRIVILEGES lines
|
|
13
|
+
* are the drift guard: every future table the owner creates via migrations is
|
|
14
|
+
* granted to the runtime role automatically, so an RLS-enabled table can never
|
|
15
|
+
* be left ungranted — which would blank the rows the app is entitled to.
|
|
16
|
+
*/
|
|
17
|
+
export declare function buildRuntimeGrants(schema: string, ownerRole: string, runtimeRole: string): string[];
|
|
10
18
|
//# sourceMappingURL=db.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"db.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/db.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,CAAA;IACvC,QAAQ,CAAC,EAAE,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAChC,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;IACnC,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;IACtC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CACvC;AAED,wBAAsB,EAAE,CAAC,IAAI,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAuBvD"}
|
|
1
|
+
{"version":3,"file":"db.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/db.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,CAAA;IACvC,QAAQ,CAAC,EAAE,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAChC,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;IACnC,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;IACtC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CACvC;AAED,wBAAsB,EAAE,CAAC,IAAI,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAuBvD;AAyND;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,EAAE,CAWnG"}
|