@stratum-hq/mysql 0.4.0 → 0.6.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.
@@ -1,29 +1,420 @@
1
1
  "use strict";
2
- // ─── Structural types (no hard dependency on sequelize) ───
3
2
  Object.defineProperty(exports, "__esModule", { value: true });
4
3
  exports.withMysqlTenantScope = withMysqlTenantScope;
4
+ const node_async_hooks_1 = require("node:async_hooks");
5
+ const utils_js_1 = require("../utils.js");
6
+ const REFUSED = "Stratum: withMysqlTenantScope";
7
+ /** The tenant scope of the running withMysqlTenantScope callback. */
8
+ const scopeStorage = new node_async_hooks_1.AsyncLocalStorage();
9
+ /** True inside a model call that tenant scoping has already handled (but not inside its hooks). */
10
+ const scopedCall = new node_async_hooks_1.AsyncLocalStorage();
5
11
  /**
6
- * Runs fn inside a session-variable scope for MySQL tenant isolation.
7
- * Sets @stratum_tenant_id inside a Sequelize transaction and clears it in a
8
- * finally block, even if fn throws.
12
+ * The model method whose own options the next top-level _injectScope call
13
+ * scopes, and whether that method requires a where clause (update, destroy,
14
+ * increment), in which case a missing where is left for Sequelize to refuse.
15
+ */
16
+ const topLevelCall = new node_async_hooks_1.AsyncLocalStorage();
17
+ const PATCHED = Symbol("stratum.sequelizeTenantScope");
18
+ const guardedInstances = new WeakSet();
19
+ /**
20
+ * Runs fn with the tenant's rows scoped for Sequelize models, inside a
21
+ * transaction that also sets the @stratum_tenant_id session variable.
22
+ *
23
+ * Inside fn, for every model with a tenant_id attribute:
24
+ * - findAll, findOne, findByPk, findAndCountAll, count, sum, min, max and
25
+ * reload() see only the tenant's rows, and every include of a tenant model
26
+ * (including those that scopes and hooks add) is filtered in its join
27
+ * condition, so a non-required include keeps the parent;
28
+ * - update, destroy, restore and increment/decrement change only the tenant's
29
+ * rows; updates never write tenant_id (by attribute or column name), and
30
+ * increment/decrement of tenant_id is refused. update, destroy and increment
31
+ * without a where clause are still refused by Sequelize;
32
+ * - create, save of a new instance and bulkCreate write the tenant's tenant_id;
33
+ * - save, destroy and restore of an instance whose row belongs to another
34
+ * tenant, or of a model without a primary key, throw;
35
+ * - upsert, bulkCreate with updateOnDuplicate, truncate, and `or` / `right`
36
+ * on an include of a tenant model are refused, and a query that carries a
37
+ * tenant model but bypasses these methods throws, also inside model hooks;
38
+ * - right before each query the tenant condition is checked again, so a query
39
+ * whose condition a hook removed is refused.
40
+ * Hooks passed `hooks: false` do not skip the filter. Raw `sequelize.query()`
41
+ * and models without a tenant_id attribute are not scoped. Outside fn,
42
+ * Sequelize behaves as usual.
9
43
  *
10
- * The variable exists only on the transaction's connection. fn receives that
11
- * transaction as its second argument, and every query inside fn must pass it
12
- * (`{ transaction }`); a query without it runs on another pooled connection
13
- * where the variable is not set.
44
+ * The session variable exists only on the transaction's connection. fn
45
+ * receives that transaction as its second argument; pass it to queries that
46
+ * must run in the transaction.
47
+ *
48
+ * @throws Error when sequelize is not a Sequelize v6 instance, because the
49
+ * tenant scope could not be applied.
14
50
  */
15
51
  async function withMysqlTenantScope(sequelize, tenantId, fn) {
52
+ (0, utils_js_1.assertTenantId)(tenantId);
53
+ installTenantScoping(sequelize);
16
54
  return sequelize.transaction(async (transaction) => {
17
55
  await sequelize.query("SET @stratum_tenant_id = ?", {
18
56
  replacements: [tenantId],
19
57
  transaction,
20
58
  });
21
59
  try {
22
- return await fn(sequelize, transaction);
60
+ return await scopeStorage.run({ sequelize, tenantId }, () => fn(sequelize, transaction));
23
61
  }
24
62
  finally {
25
63
  await sequelize.query("SET @stratum_tenant_id = NULL", { transaction });
26
64
  }
27
65
  });
28
66
  }
67
+ /** Patches Sequelize's Model class once and guards this instance's queries. */
68
+ function installTenantScoping(sequelize) {
69
+ const candidate = sequelize;
70
+ const Model = candidate.constructor?.Model;
71
+ const Op = candidate.constructor?.Op;
72
+ if (typeof Model !== "function" || !Op || typeof candidate.addHook !== "function") {
73
+ throw new Error(`${REFUSED} needs a Sequelize v6 instance, because it scopes the tenant's rows through Sequelize's models.`);
74
+ }
75
+ const statics = Model;
76
+ if (!statics[PATCHED]) {
77
+ patchModel(statics, Op.and);
78
+ statics[PATCHED] = true;
79
+ }
80
+ if (!guardedInstances.has(sequelize)) {
81
+ // Any model query that did not go through the scoped methods is refused.
82
+ candidate.addHook("beforeQuery", (options) => {
83
+ const scope = scopeStorage.getStore();
84
+ if (!scope || scope.sequelize !== sequelize || scopedCall.getStore())
85
+ return;
86
+ const instance = options.instance;
87
+ const model = options.model ?? instance?.constructor;
88
+ if (model && tenantAttribute(model)) {
89
+ throw new Error(`${REFUSED}: this query on a tenant model is not tenant-scoped and was refused.`);
90
+ }
91
+ });
92
+ guardQueryInterface(candidate, Op.and);
93
+ guardedInstances.add(sequelize);
94
+ }
95
+ }
96
+ /**
97
+ * Checks the statements that Sequelize's query interface is about to build for
98
+ * a tenant model inside the callback. Hooks run after the tenant condition is
99
+ * added and can replace a where clause or add includes, so the condition is
100
+ * verified here, right before the query, and the query is refused when it is
101
+ * missing. Values written to tenant_id (by attribute or column name, in any
102
+ * letter case) are dropped from updates and replaced on inserts.
103
+ */
104
+ function guardQueryInterface(sequelize, and) {
105
+ const queryInterface = sequelize.getQueryInterface();
106
+ const active = (model) => {
107
+ const scope = scopeStorage.getStore();
108
+ if (!model || !scope || scope.sequelize !== sequelize || !tenantAttribute(model))
109
+ return undefined;
110
+ return scope.tenantId;
111
+ };
112
+ const hasTenantCondition = (where, names, tenantId) => {
113
+ if (!where || typeof where !== "object")
114
+ return false;
115
+ if (Array.isArray(where))
116
+ return where.some((part) => hasTenantCondition(part, names, tenantId));
117
+ const record = where;
118
+ if (Object.keys(record).some((key) => names.has(key.toLowerCase()) && record[key] === tenantId))
119
+ return true;
120
+ // Only AND positions count; a condition under OR does not scope the query.
121
+ return record[and] !== undefined && hasTenantCondition(record[and], names, tenantId);
122
+ };
123
+ const requireTenantCondition = (model, where, tenantId) => {
124
+ if (!hasTenantCondition(where, tenantNames(model), tenantId)) {
125
+ throw new Error(`${REFUSED}: this query is not tenant-scoped, because its tenant condition is missing ` +
126
+ "(a hook may have replaced the where clause or added an include), and was refused.");
127
+ }
128
+ };
129
+ const checkIncludes = (include, tenantId) => {
130
+ for (const item of Array.isArray(include) ? include : include ? [include] : []) {
131
+ const entry = item;
132
+ const model = entry.model;
133
+ if (!entry._pseudo && model && tenantAttribute(model)) {
134
+ if (entry.or || entry.right) {
135
+ throw new Error(`${REFUSED}: "or" and "right" are refused on an include of a tenant model.`);
136
+ }
137
+ requireTenantCondition(model, entry.where, tenantId);
138
+ }
139
+ checkIncludes(entry.include, tenantId);
140
+ }
141
+ };
142
+ const withoutTenantKeys = (model, values) => {
143
+ const names = tenantNames(model);
144
+ return Object.fromEntries(Object.entries(values ?? {}).filter(([key]) => !names.has(key.toLowerCase())));
145
+ };
146
+ const withTenantValue = (model, values, tenantId) => ({
147
+ ...withoutTenantKeys(model, values),
148
+ [tenantField(model)]: tenantId,
149
+ });
150
+ const wrap = (name, check) => {
151
+ const original = queryInterface[name];
152
+ queryInterface[name] = function (...args) {
153
+ try {
154
+ check(args);
155
+ }
156
+ catch (error) {
157
+ return Promise.reject(error);
158
+ }
159
+ return original.apply(this, args);
160
+ };
161
+ };
162
+ wrap("select", (args) => {
163
+ const model = args[0];
164
+ const options = args[2];
165
+ const tenantId = active(model);
166
+ if (tenantId)
167
+ requireTenantCondition(model, options.where, tenantId);
168
+ const scope = scopeStorage.getStore();
169
+ if (scope && scope.sequelize === sequelize)
170
+ checkIncludes(options.include, scope.tenantId);
171
+ });
172
+ wrap("rawSelect", (args) => {
173
+ const options = args[1];
174
+ const model = args[3];
175
+ const tenantId = active(model);
176
+ if (tenantId)
177
+ requireTenantCondition(model, options.where, tenantId);
178
+ const scope = scopeStorage.getStore();
179
+ if (scope && scope.sequelize === sequelize)
180
+ checkIncludes(options.include, scope.tenantId);
181
+ });
182
+ wrap("bulkUpdate", (args) => {
183
+ const model = args[3]?.model;
184
+ const tenantId = active(model);
185
+ if (!tenantId)
186
+ return;
187
+ requireTenantCondition(model, args[2], tenantId);
188
+ args[1] = withoutTenantKeys(model, args[1]);
189
+ });
190
+ wrap("bulkDelete", (args) => {
191
+ const model = args[3];
192
+ const tenantId = active(model);
193
+ if (tenantId)
194
+ requireTenantCondition(model, args[1], tenantId);
195
+ });
196
+ for (const name of ["increment", "decrement"]) {
197
+ wrap(name, (args) => {
198
+ const model = args[0];
199
+ const tenantId = active(model);
200
+ if (!tenantId)
201
+ return;
202
+ const names = tenantNames(model);
203
+ const touched = [...Object.keys(args[3] ?? {}), ...Object.keys(args[4] ?? {})];
204
+ if (touched.some((key) => names.has(key.toLowerCase()))) {
205
+ throw new Error(`${REFUSED}: increment or decrement of tenant_id is refused.`);
206
+ }
207
+ requireTenantCondition(model, args[2], tenantId);
208
+ });
209
+ }
210
+ wrap("update", (args) => {
211
+ const model = args[0].constructor;
212
+ if (active(model))
213
+ args[2] = withoutTenantKeys(model, args[2]);
214
+ });
215
+ wrap("insert", (args) => {
216
+ const model = args[0]?.constructor;
217
+ const tenantId = active(model);
218
+ if (tenantId)
219
+ args[2] = withTenantValue(model, args[2], tenantId);
220
+ });
221
+ wrap("bulkInsert", (args) => {
222
+ const model = args[2]?.model;
223
+ const tenantId = active(model);
224
+ if (tenantId) {
225
+ args[1] = args[1].map((record) => withTenantValue(model, record, tenantId));
226
+ }
227
+ });
228
+ }
229
+ /** The tenant attribute's name and column name, lower-cased. */
230
+ function tenantNames(model) {
231
+ const attribute = tenantAttribute(model);
232
+ if (!attribute)
233
+ return new Set();
234
+ return new Set([attribute.toLowerCase(), tenantField(model).toLowerCase()]);
235
+ }
236
+ /** The column name of the tenant attribute. */
237
+ function tenantField(model) {
238
+ const attribute = tenantAttribute(model);
239
+ return model.rawAttributes?.[attribute]?.field ?? attribute;
240
+ }
241
+ /** Returns the attribute that maps to the tenant_id column, if the model has one. */
242
+ function tenantAttribute(model) {
243
+ for (const [name, attribute] of Object.entries(model.rawAttributes ?? {})) {
244
+ if ((attribute.field ?? name).toLowerCase() === "tenant_id")
245
+ return name;
246
+ }
247
+ return undefined;
248
+ }
249
+ /** Returns the running scope when it belongs to this model's Sequelize instance. */
250
+ function scopeFor(model) {
251
+ const scope = scopeStorage.getStore();
252
+ return scope && scope.sequelize === model.sequelize ? scope : undefined;
253
+ }
254
+ function patchModel(Model, and) {
255
+ const withTenant = (where, attribute, tenantId) => where === undefined || where === null
256
+ ? { [attribute]: tenantId }
257
+ : { [and]: [where, { [attribute]: tenantId }] };
258
+ // Sequelize merges scopes into a call's options, and into each include, in
259
+ // _injectScope. The tenant condition is added after that merge, so scope
260
+ // includes, scope where clauses and includes added by hooks are all covered.
261
+ const originalInject = Model._injectScope;
262
+ Model._injectScope = function (options) {
263
+ const result = originalInject.call(this, options);
264
+ const active = scopeFor(this);
265
+ const attribute = tenantAttribute(this);
266
+ if (!active || !attribute)
267
+ return result;
268
+ if (options.association !== undefined) {
269
+ // An include being validated (_validateIncludedElement).
270
+ if (options.or || options.right) {
271
+ throw new Error(`${REFUSED}: "or" and "right" are refused on an include of a tenant model.`);
272
+ }
273
+ // An include with a where clause defaults to required; keep the
274
+ // caller's join type so a LEFT JOIN still returns the parent row.
275
+ if (options.required === undefined)
276
+ options.required = !!options.where;
277
+ options.where = withTenant(options.where, attribute, active.tenantId);
278
+ return result;
279
+ }
280
+ const call = topLevelCall.getStore();
281
+ if (call?.model === this && call.requiresWhere && (options.where === undefined || options.where === null)) {
282
+ return result;
283
+ }
284
+ options.where = withTenant(options.where, attribute, active.tenantId);
285
+ return result;
286
+ };
287
+ // Model hooks run user code; the beforeQuery guard applies there again.
288
+ const originalRunHooks = Model.runHooks;
289
+ Model.runHooks = function (...args) {
290
+ return scopedCall.run(false, () => originalRunHooks.apply(this, args));
291
+ };
292
+ const wrapStatic = (name, requiresWhere, scope) => {
293
+ const original = Model[name];
294
+ Model[name] = function (...args) {
295
+ const active = scopeFor(this);
296
+ if (!active)
297
+ return original.apply(this, args);
298
+ const scopedArgs = scope(this, args, active.tenantId);
299
+ const run = () => scopedCall.run(true, () => original.apply(this, scopedArgs));
300
+ return requiresWhere === undefined ? run() : topLevelCall.run({ model: this, requiresWhere }, run);
301
+ };
302
+ };
303
+ const unchanged = (_model, args) => args;
304
+ const refuseForTenantModel = (what) => (model, args) => {
305
+ if (tenantAttribute(model))
306
+ throw new Error(`${REFUSED}: ${what} on a tenant model is refused.`);
307
+ return args;
308
+ };
309
+ wrapStatic("findAll", false, unchanged);
310
+ wrapStatic("aggregate", false, unchanged);
311
+ wrapStatic("increment", true, unchanged);
312
+ wrapStatic("update", true, (model, [values, options]) => {
313
+ // Drop tenant_id by attribute or column name, in any letter case.
314
+ const names = tenantNames(model);
315
+ const isTenantKey = (key) => typeof key === "string" && names.has(key.toLowerCase());
316
+ const scopedValues = Object.fromEntries(Object.entries(values ?? {}).filter(([key]) => !isTenantKey(key)));
317
+ const scopedOptions = { ...(options ?? {}) };
318
+ if (Array.isArray(scopedOptions.fields))
319
+ scopedOptions.fields = scopedOptions.fields.filter((f) => !isTenantKey(f));
320
+ return [scopedValues, scopedOptions];
321
+ });
322
+ wrapStatic("destroy", true, (model, args) => {
323
+ if (tenantAttribute(model) && args[0]?.truncate) {
324
+ throw new Error(`${REFUSED}: truncate on a tenant model is refused, because it removes every tenant's rows.`);
325
+ }
326
+ return args;
327
+ });
328
+ // restore() does not go through _injectScope; Sequelize does not require a
329
+ // where clause for it, so the tenant condition is always added.
330
+ wrapStatic("restore", undefined, (model, [options], tenantId) => {
331
+ const attribute = tenantAttribute(model);
332
+ const scoped = { ...(options ?? {}) };
333
+ if (attribute)
334
+ scoped.where = withTenant(scoped.where, attribute, tenantId);
335
+ return [scoped];
336
+ });
337
+ wrapStatic("upsert", undefined, refuseForTenantModel("upsert"));
338
+ wrapStatic("bulkCreate", undefined, (model, [records, options], tenantId) => {
339
+ const attribute = tenantAttribute(model);
340
+ if (!attribute)
341
+ return [records, options];
342
+ const scopedOptions = { ...(options ?? {}) };
343
+ if (scopedOptions.updateOnDuplicate) {
344
+ throw new Error(`${REFUSED}: bulkCreate with updateOnDuplicate on a tenant model is refused.`);
345
+ }
346
+ if (Array.isArray(scopedOptions.fields) && !scopedOptions.fields.includes(attribute)) {
347
+ scopedOptions.fields = [...scopedOptions.fields, attribute];
348
+ }
349
+ const scopedRecords = records.map((record) => {
350
+ if (record instanceof Model) {
351
+ record.setDataValue(attribute, tenantId);
352
+ return record;
353
+ }
354
+ return { ...record, [attribute]: tenantId };
355
+ });
356
+ return [scopedRecords, scopedOptions];
357
+ });
358
+ const proto = Model.prototype;
359
+ const wrapInstance = (name) => {
360
+ const original = proto[name];
361
+ proto[name] = async function (...args) {
362
+ const model = this.constructor;
363
+ const active = scopeFor(model);
364
+ const attribute = tenantAttribute(model);
365
+ if (!active || !attribute)
366
+ return original.apply(this, args);
367
+ const options = { ...(args[0] ?? {}) };
368
+ const names = tenantNames(model);
369
+ const isTenantKey = (key) => typeof key === "string" && names.has(key.toLowerCase());
370
+ if (name === "save" && this.isNewRecord) {
371
+ if (Array.isArray(options.fields) && !options.fields.includes(attribute)) {
372
+ options.fields = [...options.fields, attribute];
373
+ }
374
+ }
375
+ else {
376
+ await assertRowOfTenant(this, attribute, active.tenantId, options.transaction);
377
+ // An existing row keeps its tenant: tenant_id is not written.
378
+ if (name === "save" && Array.isArray(options.fields)) {
379
+ options.fields = options.fields.filter((f) => !isTenantKey(f));
380
+ }
381
+ }
382
+ if (name === "save")
383
+ this.setDataValue(attribute, active.tenantId);
384
+ return scopedCall.run(true, () => original.apply(this, [options, ...args.slice(1)]));
385
+ };
386
+ };
387
+ wrapInstance("save");
388
+ wrapInstance("destroy");
389
+ wrapInstance("restore");
390
+ }
391
+ /**
392
+ * Throws when the row that an instance's primary key identifies belongs to
393
+ * another tenant. The lookup is raw SQL, so it is not tenant-scoped, and its
394
+ * result is used only for this decision.
395
+ */
396
+ async function assertRowOfTenant(instance, attribute, tenantId, transaction) {
397
+ const model = instance.constructor;
398
+ const sequelize = model.sequelize;
399
+ // Without a primary key, Sequelize identifies the row by the where clause
400
+ // of the query that loaded it, which cannot be checked here.
401
+ const key = (model.primaryKeyAttributes ?? []).length > 0 ? instance.where() : null;
402
+ const columns = key && typeof key === "object" ? Object.keys(key) : [];
403
+ if (!key || columns.length === 0) {
404
+ throw new Error(`${REFUSED}: the row's tenant cannot be checked, because the model has no primary key.`);
405
+ }
406
+ const tenantColumn = model.rawAttributes?.[attribute]?.field ?? attribute;
407
+ const sql = `SELECT ${(0, utils_js_1.escapeIdentifier)(tenantColumn)} AS tenant_id ` +
408
+ `FROM ${sequelize.getQueryInterface().queryGenerator.quoteTable(model.getTableName())} ` +
409
+ `WHERE ${columns.map((column) => `${(0, utils_js_1.escapeIdentifier)(column)} = ?`).join(" AND ")} LIMIT 1`;
410
+ const rows = (await sequelize.query(sql, {
411
+ replacements: columns.map((column) => key[column]),
412
+ transaction,
413
+ type: "SELECT",
414
+ raw: true,
415
+ }));
416
+ if (rows.length > 0 && rows[0].tenant_id !== tenantId) {
417
+ throw new Error(`${REFUSED}: the row belongs to another tenant.`);
418
+ }
419
+ }
29
420
  //# sourceMappingURL=sequelize.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"sequelize.js","sourceRoot":"","sources":["../../src/integrations/sequelize.ts"],"names":[],"mappings":";AAAA,6DAA6D;;AAiB7D,oDAgBC;AA1BD;;;;;;;;;GASG;AACI,KAAK,UAAU,oBAAoB,CACxC,SAAwB,EACxB,QAAgB,EAChB,EAAkE;IAElE,OAAO,SAAS,CAAC,WAAW,CAAC,KAAK,EAAE,WAAW,EAAE,EAAE;QACjD,MAAM,SAAS,CAAC,KAAK,CAAC,4BAA4B,EAAE;YAClD,YAAY,EAAE,CAAC,QAAQ,CAAC;YACxB,WAAW;SACZ,CAAC,CAAC;QACH,IAAI,CAAC;YACH,OAAO,MAAM,EAAE,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC;QAC1C,CAAC;gBAAS,CAAC;YACT,MAAM,SAAS,CAAC,KAAK,CAAC,+BAA+B,EAAE,EAAE,WAAW,EAAE,CAAC,CAAC;QAC1E,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC"}
1
+ {"version":3,"file":"sequelize.js","sourceRoot":"","sources":["../../src/integrations/sequelize.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,33 @@
1
+ /** The part of a TypeORM column's metadata that tenant scoping uses. */
2
+ interface ColumnLike {
3
+ propertyName: string;
4
+ databaseName: string;
5
+ getEntityValue?(entity: Record<string, unknown>): unknown;
6
+ setEntityValue?(entity: Record<string, unknown>, value: unknown): void;
7
+ }
8
+ /** The part of a TypeORM entity's metadata that tenant scoping uses. */
9
+ /** The part of a TypeORM relation's metadata that tenant scoping uses. */
10
+ interface RelationLike {
11
+ propertyName: string;
12
+ joinColumns?: {
13
+ databaseName: string;
14
+ }[];
15
+ }
16
+ interface EntityMetadataLike {
17
+ tablePath: string;
18
+ columns: ColumnLike[];
19
+ primaryColumns: ColumnLike[];
20
+ relations?: RelationLike[];
21
+ }
22
+ interface QueryRunnerLike {
23
+ query(sql: string, parameters?: unknown[]): Promise<unknown>;
24
+ }
1
25
  export interface InsertEvent {
2
26
  entity: Record<string, unknown>;
27
+ /** The inserted entity's metadata, when TypeORM has it. */
28
+ metadata?: EntityMetadataLike;
29
+ /** The query runner that will run the insert. */
30
+ queryRunner?: QueryRunnerLike;
3
31
  }
4
32
  export interface UpdateEvent {
5
33
  /** The entity being saved, or the SET values of a query builder / repository update. */
@@ -11,11 +39,18 @@ export interface UpdateEvent {
11
39
  propertyName: string;
12
40
  databaseName: string;
13
41
  }[];
42
+ relations?: RelationLike[];
14
43
  };
15
44
  }
16
45
  export interface BeforeQueryEvent {
17
46
  /** The SQL text that TypeORM is about to send to MySQL. */
18
47
  query: string;
48
+ /** The query runner that is about to send the query. */
49
+ queryRunner?: {
50
+ query(sql: string, parameters?: unknown[]): Promise<unknown>;
51
+ };
52
+ /** The data source that is about to send the query. */
53
+ dataSource?: object;
19
54
  }
20
55
  export interface EntitySubscriberInterface {
21
56
  beforeInsert?(event: InsertEvent): void | Promise<void>;
@@ -26,6 +61,20 @@ export interface EntitySubscriberInterface {
26
61
  export interface TypeOrmDataSourceLike {
27
62
  readonly isInitialized: boolean;
28
63
  readonly subscribers: unknown[];
64
+ /**
65
+ * Used once to reach TypeORM's select, insert, update, delete and soft-delete
66
+ * query builder classes. The returned builder is itself the select query builder.
67
+ */
68
+ createQueryBuilder(): {
69
+ insert(): object;
70
+ update(): object;
71
+ delete(): object;
72
+ softDelete(): object;
73
+ };
74
+ /** Entity and view metadata; view expressions built by a query builder are exempt from the read scope. */
75
+ readonly entityMetadatas?: readonly {
76
+ expression?: unknown;
77
+ }[];
29
78
  }
30
79
  /**
31
80
  * TypeORM subscriber that injects tenant_id into inserted entities via ALS and
@@ -34,31 +83,69 @@ export interface TypeOrmDataSourceLike {
34
83
  * Register it with `registerStratumSubscriber(dataSource)` after
35
84
  * `dataSource.initialize()`. TypeORM's `subscribers` option only loads classes
36
85
  * decorated with `@EventSubscriber()`, so it does not load this class.
86
+ * Registration also scopes TypeORM's update, delete and soft-delete query
87
+ * builders (repository update(), delete(), save() of an existing row, remove(),
88
+ * softDelete(), restore()) to the current tenant: `tenant_id = <tenant>` is
89
+ * ANDed to their WHERE clause, and they are refused outside a tenant context.
37
90
  *
38
- * Known limitation: TypeORM subscribers cannot intercept query filtering.
39
- * This subscriber handles writes only. Use the shared-table adapter's
40
- * structured methods for tenant-scoped reads.
91
+ * Registration scopes reads the same way. Every SQL statement that TypeORM's
92
+ * select query builder produces (repository find*, findOne*, count*, exists*,
93
+ * sum/average/min/max, preload(), query builder getMany/getOne/getRawMany/
94
+ * getRawOne/getCount/getManyAndCount/getExists/stream, relation loading, the
95
+ * row that save() loads, and the count and pagination queries TypeORM builds
96
+ * internally) ANDs `tenant_id = <tenant>` for the FROM entity to its WHERE
97
+ * clause and adds it to the ON condition of each joined entity, so a LEFT JOIN
98
+ * still returns the parent row. Reads of an entity with a tenant_id column are
99
+ * refused outside a tenant context.
100
+ *
101
+ * Not scoped: raw SQL (`dataSource.query()`, `queryRunner.query()`), SQL taken
102
+ * from a builder's getQuery() / getQueryAndParameters() and run by hand, reads
103
+ * from a table that has no entity on the data source, and many-to-many junction
104
+ * tables. Add the tenant condition to those yourself, or use the shared-table
105
+ * adapter's structured methods.
41
106
  */
42
107
  export declare class StratumTypeOrmSubscriber implements EntitySubscriberInterface {
43
- /** Injects the current tenant's ID into the entity before insert. */
44
- beforeInsert(event: InsertEvent): void;
108
+ /**
109
+ * Injects the current tenant's ID into the entity before insert, into the
110
+ * property mapped to the tenant_id column.
111
+ *
112
+ * When the entity supplies its whole primary key and the insert builder has
113
+ * not checked it yet (save() runs this hook before the builder), the key is
114
+ * checked against the table first, so a save() with another tenant's key is
115
+ * refused rather than inserted. See checkKeyIsFree.
116
+ *
117
+ * @returns A promise that rejects when the key belongs to another tenant's row.
118
+ */
119
+ beforeInsert(event: InsertEvent): void | Promise<void>;
45
120
  /**
46
121
  * Keeps tenant_id out of an update. On save() the loaded value is restored,
47
122
  * so the column is unchanged; on a repository or query builder update the
48
123
  * column is dropped from the SET values (MySQL column names are
49
124
  * case-insensitive, so any letter case is dropped).
125
+ *
126
+ * @throws Error when save() loaded a row whose tenant_id is not the current
127
+ * tenant's. The loaded tenant_id is not copied onto the entity.
50
128
  */
51
129
  beforeUpdate(event: UpdateEvent): void;
52
130
  /**
53
- * Rejects an upsert that writes tenant_id on a key conflict.
131
+ * Rejects an upsert that could update another tenant's row.
54
132
  *
55
133
  * A conflict update must never change the tenant of an existing row. A
56
134
  * subscriber cannot remove one column from the conflict update, so the
57
135
  * statement fails before it runs.
58
136
  *
137
+ * MySQL runs ON DUPLICATE KEY UPDATE on a conflict with any unique key of
138
+ * the table, whatever conflict columns were passed to upsert() or
139
+ * orUpdate(). The upsert is therefore allowed only when every unique key
140
+ * (including the primary key) of the target table contains tenant_id, so a
141
+ * conflict can only ever be with a row of the same tenant. The keys are read
142
+ * from information_schema through the same query runner.
143
+ *
59
144
  * @throws Error when the ON DUPLICATE KEY UPDATE clause assigns tenant_id.
145
+ * @returns A promise that rejects when a unique key of the table does not
146
+ * include tenant_id, or when the table cannot be determined.
60
147
  */
61
- beforeQuery(event: BeforeQueryEvent): void;
148
+ beforeQuery(event: BeforeQueryEvent): void | Promise<void>;
62
149
  }
63
150
  /**
64
151
  * Returns the one StratumTypeOrmSubscriber on the data source, and adds it first when it is missing.
@@ -66,9 +153,14 @@ export declare class StratumTypeOrmSubscriber implements EntitySubscriberInterfa
66
153
  * A second call returns the subscriber that the first call added, so the
67
154
  * data source never runs the subscriber twice.
68
155
  *
156
+ * It also scopes TypeORM's select, update, delete and soft-delete query
157
+ * builders to the current tenant (see StratumTypeOrmSubscriber). The subscriber refuses an
158
+ * UPDATE or DELETE on a data source that was not registered this way.
159
+ *
69
160
  * @param dataSource - An initialized TypeORM `DataSource`.
70
161
  * @throws Error when the data source is not initialized. `initialize()` replaces
71
162
  * the subscriber list, so an earlier registration has no effect.
72
163
  */
73
164
  export declare function registerStratumSubscriber(dataSource: TypeOrmDataSourceLike): StratumTypeOrmSubscriber;
165
+ export {};
74
166
  //# sourceMappingURL=typeorm-subscriber.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"typeorm-subscriber.d.ts","sourceRoot":"","sources":["../../src/integrations/typeorm-subscriber.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACjC;AAED,MAAM,WAAW,WAAW;IAC1B,wFAAwF;IACxF,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IACxC,yEAAyE;IACzE,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAChD,QAAQ,CAAC,EAAE;QAAE,OAAO,EAAE;YAAE,YAAY,EAAE,MAAM,CAAC;YAAC,YAAY,EAAE,MAAM,CAAA;SAAE,EAAE,CAAA;KAAE,CAAC;CAC1E;AAED,MAAM,WAAW,gBAAgB;IAC/B,2DAA2D;IAC3D,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,yBAAyB;IACxC,YAAY,CAAC,CAAC,KAAK,EAAE,WAAW,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACxD,YAAY,CAAC,CAAC,KAAK,EAAE,WAAW,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACxD,WAAW,CAAC,CAAC,KAAK,EAAE,gBAAgB,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC7D;AAED,4EAA4E;AAC5E,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC;IAChC,QAAQ,CAAC,WAAW,EAAE,OAAO,EAAE,CAAC;CACjC;AAOD;;;;;;;;;;;GAWG;AACH,qBAAa,wBAAyB,YAAW,yBAAyB;IACxE,qEAAqE;IACrE,YAAY,CAAC,KAAK,EAAE,WAAW,GAAG,IAAI;IAMtC;;;;;OAKG;IACH,YAAY,CAAC,KAAK,EAAE,WAAW,GAAG,IAAI;IAuBtC;;;;;;;;OAQG;IACH,WAAW,CAAC,KAAK,EAAE,gBAAgB,GAAG,IAAI;CAS3C;AAED;;;;;;;;;GASG;AACH,wBAAgB,yBAAyB,CACvC,UAAU,EAAE,qBAAqB,GAChC,wBAAwB,CAgB1B"}
1
+ {"version":3,"file":"typeorm-subscriber.d.ts","sourceRoot":"","sources":["../../src/integrations/typeorm-subscriber.ts"],"names":[],"mappings":"AAMA,wEAAwE;AACxE,UAAU,UAAU;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC;IAC1D,cAAc,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI,CAAC;CACxE;AAED,wEAAwE;AACxE,0EAA0E;AAC1E,UAAU,YAAY;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE;QAAE,YAAY,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CAC1C;AAED,UAAU,kBAAkB;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,cAAc,EAAE,UAAU,EAAE,CAAC;IAC7B,SAAS,CAAC,EAAE,YAAY,EAAE,CAAC;CAC5B;AAED,UAAU,eAAe;IACvB,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CAC9D;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,2DAA2D;IAC3D,QAAQ,CAAC,EAAE,kBAAkB,CAAC;IAC9B,iDAAiD;IACjD,WAAW,CAAC,EAAE,eAAe,CAAC;CAC/B;AAED,MAAM,WAAW,WAAW;IAC1B,wFAAwF;IACxF,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IACxC,yEAAyE;IACzE,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAChD,QAAQ,CAAC,EAAE;QAAE,OAAO,EAAE;YAAE,YAAY,EAAE,MAAM,CAAC;YAAC,YAAY,EAAE,MAAM,CAAA;SAAE,EAAE,CAAC;QAAC,SAAS,CAAC,EAAE,YAAY,EAAE,CAAA;KAAE,CAAC;CACtG;AAED,MAAM,WAAW,gBAAgB;IAC/B,2DAA2D;IAC3D,KAAK,EAAE,MAAM,CAAC;IACd,wDAAwD;IACxD,WAAW,CAAC,EAAE;QAAE,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;KAAE,CAAC;IAC/E,uDAAuD;IACvD,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,yBAAyB;IACxC,YAAY,CAAC,CAAC,KAAK,EAAE,WAAW,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACxD,YAAY,CAAC,CAAC,KAAK,EAAE,WAAW,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACxD,WAAW,CAAC,CAAC,KAAK,EAAE,gBAAgB,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC7D;AAED,4EAA4E;AAC5E,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC;IAChC,QAAQ,CAAC,WAAW,EAAE,OAAO,EAAE,CAAC;IAChC;;;OAGG;IACH,kBAAkB,IAAI;QAAE,MAAM,IAAI,MAAM,CAAC;QAAC,MAAM,IAAI,MAAM,CAAC;QAAC,MAAM,IAAI,MAAM,CAAC;QAAC,UAAU,IAAI,MAAM,CAAA;KAAE,CAAC;IACrG,0GAA0G;IAC1G,QAAQ,CAAC,eAAe,CAAC,EAAE,SAAS;QAAE,UAAU,CAAC,EAAE,OAAO,CAAA;KAAE,EAAE,CAAC;CAChE;AA2FD;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,qBAAa,wBAAyB,YAAW,yBAAyB;IACxE;;;;;;;;;;OAUG;IACH,YAAY,CAAC,KAAK,EAAE,WAAW,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IActD;;;;;;;;OAQG;IACH,YAAY,CAAC,KAAK,EAAE,WAAW,GAAG,IAAI;IAgDtC;;;;;;;;;;;;;;;;;OAiBG;IACH,WAAW,CAAC,KAAK,EAAE,gBAAgB,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;CAsB3D;AAuYD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,yBAAyB,CACvC,UAAU,EAAE,qBAAqB,GAChC,wBAAwB,CAoB1B"}