@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.
@@ -2,12 +2,40 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.StratumTypeOrmSubscriber = void 0;
4
4
  exports.registerStratumSubscriber = registerStratumSubscriber;
5
+ const node_async_hooks_1 = require("node:async_hooks");
5
6
  const sdk_1 = require("@stratum-hq/sdk");
6
7
  const utils_js_1 = require("../utils.js");
7
8
  // TypeORM writes an upsert on MySQL as INSERT ... ON DUPLICATE KEY UPDATE and
8
9
  // quotes each column with backticks.
9
10
  const UPSERT_CLAUSE = /\bON\s+DUPLICATE\s+KEY\s+UPDATE\b/i;
10
11
  const TENANT_ASSIGNMENT = /`tenant_id`\s*=/i;
12
+ // The target of an INSERT, as TypeORM writes it: `table` or `schema`.`table`.
13
+ const INSERT_TARGET = /^\s*INSERT\s+(?:IGNORE\s+)?INTO\s+(?:`((?:[^`]|``)+)`\.)?`((?:[^`]|``)+)`/i;
14
+ const UPDATE_OR_DELETE = /^\s*(?:UPDATE|DELETE)\b/i;
15
+ // The target of a TRUNCATE, as TypeORM writes it: `table` or `schema`.`table`.
16
+ const TRUNCATE_TARGET = /^\s*TRUNCATE\s+(?:TABLE\s+)?(?:`((?:[^`]|``)+)`\.)?`?((?:[^`\s;]|``)+)`?/i;
17
+ const TENANT_COLUMN_SQL = "SELECT COUNT(*) AS n FROM information_schema.COLUMNS " +
18
+ "WHERE TABLE_SCHEMA = COALESCE(?, DATABASE()) AND TABLE_NAME = ? AND LOWER(COLUMN_NAME) = 'tenant_id'";
19
+ const TENANT_PARAMETER = "stratumTenantId";
20
+ const SCOPED_EXECUTE = Symbol("stratum.tenantScopedExecute");
21
+ const SCOPED_GET_QUERY = Symbol("stratum.tenantScopedGetQuery");
22
+ const SCOPED_INSERT = Symbol("stratum.tenantScopedInsert");
23
+ const EXEMPT_VIEW_EXPRESSION = Symbol("stratum.exemptViewExpression");
24
+ /** Inserted values whose primary key was already checked. */
25
+ const keyChecked = new WeakSet();
26
+ /** Values of an upsert while its builder runs; their keys are checked in beforeQuery instead. */
27
+ const upsertValues = new WeakSet();
28
+ /** True while a view entity's query builder expression is being built. */
29
+ const buildingViewExpression = new node_async_hooks_1.AsyncLocalStorage();
30
+ /** Select query builders returned by a view entity's expression. */
31
+ const viewExpressionBuilders = new WeakSet();
32
+ /** The WHERE addition that addTenantCondition set on each write builder's expression map. */
33
+ const scopedWriteConditions = new WeakMap();
34
+ /** Data sources whose update and delete query builders are tenant-scoped. */
35
+ const scopedDataSources = new WeakSet();
36
+ const UNIQUE_KEYS_SQL = "SELECT INDEX_NAME AS index_name, COLUMN_NAME AS column_name " +
37
+ "FROM information_schema.STATISTICS " +
38
+ "WHERE TABLE_SCHEMA = COALESCE(?, DATABASE()) AND TABLE_NAME = ? AND NON_UNIQUE = 0";
11
39
  /**
12
40
  * TypeORM subscriber that injects tenant_id into inserted entities via ALS and
13
41
  * never lets an update change tenant_id.
@@ -15,34 +43,94 @@ const TENANT_ASSIGNMENT = /`tenant_id`\s*=/i;
15
43
  * Register it with `registerStratumSubscriber(dataSource)` after
16
44
  * `dataSource.initialize()`. TypeORM's `subscribers` option only loads classes
17
45
  * decorated with `@EventSubscriber()`, so it does not load this class.
46
+ * Registration also scopes TypeORM's update, delete and soft-delete query
47
+ * builders (repository update(), delete(), save() of an existing row, remove(),
48
+ * softDelete(), restore()) to the current tenant: `tenant_id = <tenant>` is
49
+ * ANDed to their WHERE clause, and they are refused outside a tenant context.
18
50
  *
19
- * Known limitation: TypeORM subscribers cannot intercept query filtering.
20
- * This subscriber handles writes only. Use the shared-table adapter's
21
- * structured methods for tenant-scoped reads.
51
+ * Registration scopes reads the same way. Every SQL statement that TypeORM's
52
+ * select query builder produces (repository find*, findOne*, count*, exists*,
53
+ * sum/average/min/max, preload(), query builder getMany/getOne/getRawMany/
54
+ * getRawOne/getCount/getManyAndCount/getExists/stream, relation loading, the
55
+ * row that save() loads, and the count and pagination queries TypeORM builds
56
+ * internally) ANDs `tenant_id = <tenant>` for the FROM entity to its WHERE
57
+ * clause and adds it to the ON condition of each joined entity, so a LEFT JOIN
58
+ * still returns the parent row. Reads of an entity with a tenant_id column are
59
+ * refused outside a tenant context.
60
+ *
61
+ * Not scoped: raw SQL (`dataSource.query()`, `queryRunner.query()`), SQL taken
62
+ * from a builder's getQuery() / getQueryAndParameters() and run by hand, reads
63
+ * from a table that has no entity on the data source, and many-to-many junction
64
+ * tables. Add the tenant condition to those yourself, or use the shared-table
65
+ * adapter's structured methods.
22
66
  */
23
67
  class StratumTypeOrmSubscriber {
24
- /** Injects the current tenant's ID into the entity before insert. */
68
+ /**
69
+ * Injects the current tenant's ID into the entity before insert, into the
70
+ * property mapped to the tenant_id column.
71
+ *
72
+ * When the entity supplies its whole primary key and the insert builder has
73
+ * not checked it yet (save() runs this hook before the builder), the key is
74
+ * checked against the table first, so a save() with another tenant's key is
75
+ * refused rather than inserted. See checkKeyIsFree.
76
+ *
77
+ * @returns A promise that rejects when the key belongs to another tenant's row.
78
+ */
25
79
  beforeInsert(event) {
26
80
  const context = (0, sdk_1.getTenantContext)();
27
81
  (0, utils_js_1.assertTenantId)(context.tenant_id);
28
- event.entity["tenant_id"] = context.tenant_id;
82
+ let key;
83
+ if (event.metadata && !keyChecked.has(event.entity) && !upsertValues.has(event.entity)) {
84
+ keyChecked.add(event.entity);
85
+ key = primaryKeyOf(event.metadata, event.entity);
86
+ }
87
+ setTenant(event.entity, event.metadata, context.tenant_id);
88
+ if (key && event.metadata && event.queryRunner) {
89
+ return checkKeyIsFree(event.queryRunner, event.metadata, key, context.tenant_id);
90
+ }
29
91
  }
30
92
  /**
31
93
  * Keeps tenant_id out of an update. On save() the loaded value is restored,
32
94
  * so the column is unchanged; on a repository or query builder update the
33
95
  * column is dropped from the SET values (MySQL column names are
34
96
  * case-insensitive, so any letter case is dropped).
97
+ *
98
+ * @throws Error when save() loaded a row whose tenant_id is not the current
99
+ * tenant's. The loaded tenant_id is not copied onto the entity.
35
100
  */
36
101
  beforeUpdate(event) {
37
102
  const entity = event.entity;
38
103
  if (!entity)
39
104
  return;
105
+ const loaded = event.databaseEntity;
106
+ if (loaded) {
107
+ const loadedProps = new Set(Object.keys(loaded).filter((key) => key.toLowerCase() === "tenant_id"));
108
+ for (const column of event.metadata?.columns ?? []) {
109
+ if (column.databaseName.toLowerCase() === "tenant_id" && column.propertyName in loaded) {
110
+ loadedProps.add(column.propertyName);
111
+ }
112
+ }
113
+ if (loadedProps.size > 0) {
114
+ const context = (0, sdk_1.getTenantContext)();
115
+ (0, utils_js_1.assertTenantId)(context.tenant_id);
116
+ for (const prop of loadedProps) {
117
+ if (loaded[prop] !== context.tenant_id) {
118
+ throw new Error("Stratum: save() refused, because the row belongs to another tenant.");
119
+ }
120
+ }
121
+ }
122
+ }
40
123
  const tenantProps = new Set(Object.keys(entity).filter((key) => key.toLowerCase() === "tenant_id"));
41
124
  for (const column of event.metadata?.columns ?? []) {
42
125
  if (column.databaseName.toLowerCase() === "tenant_id" && column.propertyName in entity) {
43
126
  tenantProps.add(column.propertyName);
44
127
  }
45
128
  }
129
+ // A relation whose join column is tenant_id writes that column too.
130
+ for (const prop of tenantRelationProps(event.metadata)) {
131
+ if (prop in entity)
132
+ tenantProps.add(prop);
133
+ }
46
134
  for (const prop of tenantProps) {
47
135
  const loaded = event.databaseEntity;
48
136
  if (loaded && prop in loaded) {
@@ -54,29 +142,401 @@ class StratumTypeOrmSubscriber {
54
142
  }
55
143
  }
56
144
  /**
57
- * Rejects an upsert that writes tenant_id on a key conflict.
145
+ * Rejects an upsert that could update another tenant's row.
58
146
  *
59
147
  * A conflict update must never change the tenant of an existing row. A
60
148
  * subscriber cannot remove one column from the conflict update, so the
61
149
  * statement fails before it runs.
62
150
  *
151
+ * MySQL runs ON DUPLICATE KEY UPDATE on a conflict with any unique key of
152
+ * the table, whatever conflict columns were passed to upsert() or
153
+ * orUpdate(). The upsert is therefore allowed only when every unique key
154
+ * (including the primary key) of the target table contains tenant_id, so a
155
+ * conflict can only ever be with a row of the same tenant. The keys are read
156
+ * from information_schema through the same query runner.
157
+ *
63
158
  * @throws Error when the ON DUPLICATE KEY UPDATE clause assigns tenant_id.
159
+ * @returns A promise that rejects when a unique key of the table does not
160
+ * include tenant_id, or when the table cannot be determined.
64
161
  */
65
162
  beforeQuery(event) {
163
+ if (event.dataSource &&
164
+ !scopedDataSources.has(event.dataSource) &&
165
+ UPDATE_OR_DELETE.test(event.query)) {
166
+ throw new Error("Stratum: register the subscriber with registerStratumSubscriber(dataSource), " +
167
+ "which scopes updates and deletes to the current tenant.");
168
+ }
169
+ if (/^\s*TRUNCATE\b/i.test(event.query))
170
+ return assertNotTenantTable(event);
66
171
  const clause = event.query.split(UPSERT_CLAUSE)[1];
67
- if (clause !== undefined && TENANT_ASSIGNMENT.test(clause)) {
172
+ if (clause === undefined)
173
+ return;
174
+ if (TENANT_ASSIGNMENT.test(clause)) {
68
175
  throw new Error("Stratum: an upsert must not update tenant_id on conflict. " +
69
176
  "Remove tenant_id from the entity values or from the orUpdate() columns.");
70
177
  }
178
+ return assertUniqueKeysIncludeTenant(event);
71
179
  }
72
180
  }
73
181
  exports.StratumTypeOrmSubscriber = StratumTypeOrmSubscriber;
182
+ /**
183
+ * ANDs `tenant_id = <current tenant>` to the WHERE clause of an update, delete
184
+ * or soft-delete builder whose data source has the subscriber. Entities without
185
+ * a tenant_id column are left alone; a target without metadata (a table name)
186
+ * is treated as a tenant table.
187
+ */
188
+ function addTenantCondition(builder) {
189
+ if (!builder.connection.subscribers.some((s) => s instanceof StratumTypeOrmSubscriber))
190
+ return;
191
+ const alias = builder.expressionMap.mainAlias;
192
+ let column = "tenant_id";
193
+ if (alias?.hasMetadata) {
194
+ const tenantColumn = alias.metadata.columns.find((c) => c.databaseName.toLowerCase() === "tenant_id");
195
+ if (!tenantColumn)
196
+ return;
197
+ column = tenantColumn.databaseName;
198
+ }
199
+ const context = (0, sdk_1.getTenantContext)();
200
+ (0, utils_js_1.assertTenantId)(context.tenant_id);
201
+ stripTenantFromUpdateValues(builder);
202
+ const qualified = builder.expressionMap.aliasNamePrefixingEnabled && alias
203
+ ? `${builder.escape(alias.name)}.${builder.escape(column)}`
204
+ : builder.escape(column);
205
+ const condition = `${qualified} = :${TENANT_PARAMETER}`;
206
+ const existing = builder.expressionMap.extraAppendedAndWhereCondition;
207
+ // A builder that is executed again already carries the condition set here.
208
+ if (scopedWriteConditions.get(builder.expressionMap) !== existing) {
209
+ // TypeORM ANDs this condition, in its own parentheses, to the caller's
210
+ // WHERE clause, so an orWhere() cannot widen the statement past the tenant.
211
+ builder.expressionMap.extraAppendedAndWhereCondition = existing
212
+ ? `(${existing}) AND ${condition}`
213
+ : condition;
214
+ scopedWriteConditions.set(builder.expressionMap, builder.expressionMap.extraAppendedAndWhereCondition);
215
+ }
216
+ builder.setParameter(TENANT_PARAMETER, context.tenant_id);
217
+ }
218
+ /**
219
+ * Drops tenant_id from the SET values of an update builder, so an update never
220
+ * moves a row to another tenant, with or without listeners (beforeUpdate).
221
+ */
222
+ function stripTenantFromUpdateValues(builder) {
223
+ const map = builder.expressionMap;
224
+ const values = map.valuesSet;
225
+ if (map.queryType !== "update" || !values || typeof values !== "object")
226
+ return;
227
+ const alias = map.mainAlias;
228
+ const metadata = alias?.hasMetadata ? alias.metadata : undefined;
229
+ const tenantProps = new Set([
230
+ ...(metadata?.columns ?? []).filter((c) => c.databaseName.toLowerCase() === "tenant_id").map((c) => c.propertyName),
231
+ ...tenantRelationProps(metadata),
232
+ ]);
233
+ const keys = Object.keys(values);
234
+ const isTenantKey = (key) => key.toLowerCase() === "tenant_id" || tenantProps.has(key);
235
+ if (!keys.some(isTenantKey))
236
+ return;
237
+ map.valuesSet = Object.fromEntries(keys.filter((key) => !isTenantKey(key)).map((key) => [key, values[key]]));
238
+ }
239
+ /** Wraps execute() of TypeORM's update, delete and soft-delete query builder classes, once. */
240
+ function scopeWriteBuilders(dataSource) {
241
+ const builders = [
242
+ dataSource.createQueryBuilder().update(),
243
+ dataSource.createQueryBuilder().delete(),
244
+ dataSource.createQueryBuilder().softDelete(),
245
+ ];
246
+ for (const builder of builders) {
247
+ const proto = Object.getPrototypeOf(builder);
248
+ if (proto[SCOPED_EXECUTE])
249
+ continue;
250
+ const original = proto.execute;
251
+ proto.execute = function () {
252
+ addTenantCondition(this);
253
+ return original.call(this);
254
+ };
255
+ proto[SCOPED_EXECUTE] = true;
256
+ }
257
+ scopedDataSources.add(dataSource);
258
+ }
259
+ /** Returns the tenant_id column of an entity's metadata, if it has one. */
260
+ function tenantColumnIn(metadata) {
261
+ return metadata?.columns.find((c) => c.databaseName.toLowerCase() === "tenant_id");
262
+ }
263
+ /** Returns the properties of relations whose join columns include tenant_id. */
264
+ function tenantRelationProps(metadata) {
265
+ return (metadata?.relations ?? [])
266
+ .filter((r) => (r.joinColumns ?? []).some((c) => c.databaseName.toLowerCase() === "tenant_id"))
267
+ .map((r) => r.propertyName);
268
+ }
269
+ /**
270
+ * Writes the tenant into the property mapped to the tenant_id column, and
271
+ * removes any relation whose join column is tenant_id, because TypeORM reads
272
+ * the column's value from such a relation first.
273
+ */
274
+ function setTenant(entity, metadata, tenantId) {
275
+ for (const prop of tenantRelationProps(metadata))
276
+ delete entity[prop];
277
+ const column = tenantColumnIn(metadata);
278
+ if (!column) {
279
+ entity["tenant_id"] = tenantId;
280
+ }
281
+ else if (column.setEntityValue) {
282
+ column.setEntityValue(entity, tenantId);
283
+ }
284
+ else {
285
+ entity[column.propertyName] = tenantId;
286
+ }
287
+ }
288
+ /**
289
+ * Returns the primary key values that an entity supplies, keyed by column
290
+ * name. Returns undefined when the entity has no tenant_id column or any part
291
+ * of its primary key is missing or computed by SQL.
292
+ */
293
+ function primaryKeyOf(metadata, entity) {
294
+ if (metadata.primaryColumns.length === 0 || !tenantColumnIn(metadata))
295
+ return undefined;
296
+ const key = new Map();
297
+ for (const column of metadata.primaryColumns) {
298
+ const value = column.getEntityValue ? column.getEntityValue(entity) : entity[column.propertyName];
299
+ if (value === undefined || value === null || typeof value === "function")
300
+ return undefined;
301
+ key.set(column.databaseName, value);
302
+ }
303
+ return key;
304
+ }
305
+ /**
306
+ * Refuses an insert whose primary key belongs to another tenant's row. The
307
+ * lookup is raw SQL, so it is not tenant-scoped, and its result is used only
308
+ * for this decision.
309
+ */
310
+ async function checkKeyIsFree(runner, metadata, key, tenantId) {
311
+ const quote = (name) => "`" + name.replace(/`/g, "``") + "`";
312
+ const tenantColumn = tenantColumnIn(metadata);
313
+ const table = metadata.tablePath.split(".").map(quote).join(".");
314
+ const where = [...key.keys()].map((column) => `${quote(column)} = ?`).join(" AND ");
315
+ const rows = (await runner.query(`SELECT ${quote(tenantColumn.databaseName)} AS tenant_id FROM ${table} WHERE ${where} LIMIT 1`, [...key.values()]));
316
+ if (rows.length > 0 && rows[0].tenant_id !== tenantId) {
317
+ throw new Error("Stratum: insert refused, because a row with this primary key belongs to another tenant.");
318
+ }
319
+ }
320
+ /**
321
+ * Prepares an insert builder whose data source has the subscriber.
322
+ *
323
+ * - INSERT ... SELECT into a tenant entity is refused, because the tenant of
324
+ * the selected rows cannot be set. Into other tables, the tenant parameter
325
+ * of a scoped SELECT is copied to the insert.
326
+ * - Each value of a tenant entity gets the current tenant, whether or not
327
+ * listeners (beforeInsert) run.
328
+ * - A plain insert whose value supplies its whole primary key is refused when
329
+ * a row with that key belongs to another tenant. The lookup is raw SQL, so it
330
+ * is not tenant-scoped, and its result is used only for this decision.
331
+ * Upserts are checked in beforeQuery against the table's unique keys.
332
+ */
333
+ async function prepareInsert(builder) {
334
+ if (!builder.connection.subscribers.some((s) => s instanceof StratumTypeOrmSubscriber))
335
+ return [];
336
+ const map = builder.expressionMap;
337
+ const metadata = map.mainAlias?.hasMetadata ? map.mainAlias.metadata : undefined;
338
+ const tenantColumn = tenantColumnIn(metadata);
339
+ if (map.insertFromSelect) {
340
+ if (tenantColumn) {
341
+ throw new Error("Stratum: INSERT ... SELECT into a tenant table is refused, because the tenant of the selected rows " +
342
+ "cannot be set. Load the rows and insert them as entities instead.");
343
+ }
344
+ if (map.insertFromSelect.getQuery().includes(`:${TENANT_PARAMETER}`)) {
345
+ const context = (0, sdk_1.getTenantContext)();
346
+ (0, utils_js_1.assertTenantId)(context.tenant_id);
347
+ builder.setParameter(TENANT_PARAMETER, context.tenant_id);
348
+ }
349
+ return [];
350
+ }
351
+ if (!metadata || !tenantColumn)
352
+ return [];
353
+ const context = (0, sdk_1.getTenantContext)();
354
+ (0, utils_js_1.assertTenantId)(context.tenant_id);
355
+ const values = (Array.isArray(map.valuesSet) ? map.valuesSet : [map.valuesSet]).filter((value) => !!value && typeof value === "object");
356
+ const runner = builder.queryRunner ?? builder.connection;
357
+ for (const value of values) {
358
+ if (map.onUpdate || map.onIgnore) {
359
+ upsertValues.add(value);
360
+ }
361
+ else if (!keyChecked.has(value)) {
362
+ keyChecked.add(value);
363
+ const key = primaryKeyOf(metadata, value);
364
+ if (key)
365
+ await checkKeyIsFree(runner, metadata, key, context.tenant_id);
366
+ }
367
+ setTenant(value, metadata, context.tenant_id);
368
+ }
369
+ return values;
370
+ }
371
+ /** Returns the tenant_id column of an entity alias, or undefined when it has none. */
372
+ function tenantColumnOf(alias) {
373
+ if (!alias || alias.subQuery || !alias.hasMetadata)
374
+ return undefined;
375
+ return alias.metadata.columns.find((c) => c.databaseName.toLowerCase() === "tenant_id")?.databaseName;
376
+ }
377
+ /**
378
+ * Builds the SQL of a select query builder whose data source has the
379
+ * subscriber, with `tenant_id = <current tenant>` ANDed to the WHERE clause for
380
+ * the FROM entity and to the ON condition of each joined entity that has a
381
+ * tenant_id column. The conditions are added only while the SQL is built and
382
+ * removed afterwards, so a builder that is built again, cloned, or used as a
383
+ * subquery never carries the condition twice.
384
+ */
385
+ function buildScopedSelect(builder, build) {
386
+ if (!builder.connection.subscribers.some((s) => s instanceof StratumTypeOrmSubscriber))
387
+ return build();
388
+ // A view's definition is a schema object, built by synchronize() outside any
389
+ // tenant context. Reads from the view entity are scoped like any entity.
390
+ if (buildingViewExpression.getStore() || viewExpressionBuilders.has(builder))
391
+ return build();
392
+ const map = builder.expressionMap;
393
+ const condition = (alias, column) => `${builder.escape(alias.name)}.${builder.escape(column)} = :${TENANT_PARAMETER}`;
394
+ const mainColumn = tenantColumnOf(map.mainAlias);
395
+ const joins = map.joinAttributes
396
+ .map((join) => ({ join, column: tenantColumnOf(join.alias) }))
397
+ .filter((entry) => entry.column !== undefined);
398
+ const setTenantParameter = () => {
399
+ const context = (0, sdk_1.getTenantContext)();
400
+ (0, utils_js_1.assertTenantId)(context.tenant_id);
401
+ builder.setParameter(TENANT_PARAMETER, context.tenant_id);
402
+ };
403
+ if (mainColumn === undefined && joins.length === 0) {
404
+ const sql = build();
405
+ // SQL built by another scoped builder can be embedded here, for example
406
+ // TypeORM's pagination query, which selects from a clone of the builder.
407
+ if (sql.includes(`:${TENANT_PARAMETER}`))
408
+ setTenantParameter();
409
+ return sql;
410
+ }
411
+ setTenantParameter();
412
+ const extra = map.extraAppendedAndWhereCondition;
413
+ const joinConditions = joins.map(({ join }) => join.condition);
414
+ try {
415
+ if (mainColumn !== undefined) {
416
+ const main = condition(map.mainAlias, mainColumn);
417
+ // TypeORM ANDs this condition, in its own parentheses, to the caller's
418
+ // WHERE clause, so an orWhere() cannot widen the read past the tenant.
419
+ map.extraAppendedAndWhereCondition = extra ? `(${extra}) AND ${main}` : main;
420
+ }
421
+ for (const { join, column } of joins) {
422
+ // In the ON condition rather than the WHERE clause, so a LEFT JOIN keeps
423
+ // the parent row and only drops the other tenant's joined row.
424
+ const own = condition(join.alias, column);
425
+ join.condition = join.condition ? `(${join.condition}) AND ${own}` : own;
426
+ }
427
+ return build();
428
+ }
429
+ finally {
430
+ map.extraAppendedAndWhereCondition = extra;
431
+ joins.forEach(({ join }, i) => {
432
+ join.condition = joinConditions[i];
433
+ });
434
+ }
435
+ }
436
+ /**
437
+ * Wraps getQuery() of TypeORM's select query builder class, once. Every read
438
+ * the builder runs (loadRawResults, stream, the count, exists and pagination
439
+ * queries, and subqueries) builds its SQL through getQuery().
440
+ */
441
+ function scopeSelectBuilder(dataSource) {
442
+ const proto = Object.getPrototypeOf(dataSource.createQueryBuilder());
443
+ if (proto[SCOPED_GET_QUERY])
444
+ return;
445
+ if (!Object.prototype.hasOwnProperty.call(proto, "getQuery") || typeof proto.getQuery !== "function") {
446
+ throw new Error("Stratum: this TypeORM version's select query builder is not supported, so reads cannot be tenant-scoped.");
447
+ }
448
+ const original = proto.getQuery;
449
+ proto.getQuery = function () {
450
+ return buildScopedSelect(this, () => original.call(this));
451
+ };
452
+ proto[SCOPED_GET_QUERY] = true;
453
+ }
454
+ /** Wraps execute() of TypeORM's insert query builder class, once (see prepareInsert). */
455
+ function scopeInsertBuilder(dataSource) {
456
+ const proto = Object.getPrototypeOf(dataSource.createQueryBuilder().insert());
457
+ if (proto[SCOPED_INSERT])
458
+ return;
459
+ const original = proto.execute;
460
+ proto.execute = async function () {
461
+ const values = await prepareInsert(this);
462
+ try {
463
+ return await original.call(this);
464
+ }
465
+ finally {
466
+ // An upsert's values are exempt from the key check only while it runs.
467
+ for (const value of values)
468
+ upsertValues.delete(value);
469
+ }
470
+ };
471
+ proto[SCOPED_INSERT] = true;
472
+ }
473
+ /**
474
+ * Marks the query builders that view entity expressions return, so that
475
+ * synchronize() can build the view definition without the read scope.
476
+ */
477
+ function exemptViewExpressions(dataSource) {
478
+ for (const metadata of dataSource.entityMetadatas ?? []) {
479
+ const expression = metadata.expression;
480
+ if (typeof expression !== "function" || expression[EXEMPT_VIEW_EXPRESSION])
481
+ continue;
482
+ const exempt = Object.assign((source) => buildingViewExpression.run(true, () => {
483
+ const builder = expression(source);
484
+ viewExpressionBuilders.add(builder);
485
+ return builder;
486
+ }), { [EXEMPT_VIEW_EXPRESSION]: true });
487
+ metadata.expression = exempt;
488
+ }
489
+ }
490
+ /**
491
+ * Refuses a TRUNCATE (Repository.clear(), QueryRunner.clearTable()) of a table
492
+ * with a tenant_id column, because it would empty the table for every tenant.
493
+ * A TRUNCATE whose table cannot be checked is refused too.
494
+ */
495
+ async function assertNotTenantTable(event) {
496
+ const target = TRUNCATE_TARGET.exec(event.query);
497
+ const unquote = (name) => (name === undefined ? null : name.replace(/``/g, "`"));
498
+ const table = target ? unquote(target[2]) : null;
499
+ let isTenantTable = true;
500
+ if (table && event.queryRunner) {
501
+ const rows = (await event.queryRunner.query(TENANT_COLUMN_SQL, [unquote(target?.[1]), table]));
502
+ isTenantTable = Number(rows[0]?.n ?? 1) > 0;
503
+ }
504
+ if (isTenantTable) {
505
+ throw new Error(`Stratum: TRUNCATE of "${table ?? "unknown table"}" is refused, because it would remove every tenant's rows. ` +
506
+ "Delete the current tenant's rows instead.");
507
+ }
508
+ }
509
+ async function assertUniqueKeysIncludeTenant(event) {
510
+ const target = INSERT_TARGET.exec(event.query);
511
+ if (!target || !event.queryRunner) {
512
+ throw new Error("Stratum: an upsert is refused because its table's unique keys cannot be checked for tenant_id.");
513
+ }
514
+ const unquote = (name) => (name === undefined ? null : name.replace(/``/g, "`"));
515
+ const table = unquote(target[2]);
516
+ const rows = (await event.queryRunner.query(UNIQUE_KEYS_SQL, [unquote(target[1]), table]));
517
+ const keys = new Map();
518
+ for (const row of rows) {
519
+ const hasTenant = row.column_name.toLowerCase() === "tenant_id";
520
+ keys.set(row.index_name, (keys.get(row.index_name) ?? false) || hasTenant);
521
+ }
522
+ for (const [key, hasTenant] of keys) {
523
+ if (!hasTenant) {
524
+ throw new Error(`Stratum: an upsert on "${table}" is refused, because its unique key "${key}" does not include tenant_id, ` +
525
+ "so a conflict could update another tenant's row. Add tenant_id to every unique key of the table, " +
526
+ "or look the row up by tenant before writing it.");
527
+ }
528
+ }
529
+ }
74
530
  /**
75
531
  * Returns the one StratumTypeOrmSubscriber on the data source, and adds it first when it is missing.
76
532
  *
77
533
  * A second call returns the subscriber that the first call added, so the
78
534
  * data source never runs the subscriber twice.
79
535
  *
536
+ * It also scopes TypeORM's select, update, delete and soft-delete query
537
+ * builders to the current tenant (see StratumTypeOrmSubscriber). The subscriber refuses an
538
+ * UPDATE or DELETE on a data source that was not registered this way.
539
+ *
80
540
  * @param dataSource - An initialized TypeORM `DataSource`.
81
541
  * @throws Error when the data source is not initialized. `initialize()` replaces
82
542
  * the subscriber list, so an earlier registration has no effect.
@@ -86,6 +546,10 @@ function registerStratumSubscriber(dataSource) {
86
546
  throw new Error("Stratum: call registerStratumSubscriber() after dataSource.initialize(). " +
87
547
  "initialize() replaces the subscriber list.");
88
548
  }
549
+ scopeSelectBuilder(dataSource);
550
+ scopeWriteBuilders(dataSource);
551
+ scopeInsertBuilder(dataSource);
552
+ exemptViewExpressions(dataSource);
89
553
  const existing = dataSource.subscribers.find((subscriber) => subscriber instanceof StratumTypeOrmSubscriber);
90
554
  if (existing)
91
555
  return existing;
@@ -1 +1 @@
1
- {"version":3,"file":"typeorm-subscriber.js","sourceRoot":"","sources":["../../src/integrations/typeorm-subscriber.ts"],"names":[],"mappings":";;;AAsHA,8DAkBC;AAxID,yCAAmD;AACnD,0CAA6C;AAiC7C,8EAA8E;AAC9E,qCAAqC;AACrC,MAAM,aAAa,GAAG,oCAAoC,CAAC;AAC3D,MAAM,iBAAiB,GAAG,kBAAkB,CAAC;AAE7C;;;;;;;;;;;GAWG;AACH,MAAa,wBAAwB;IACnC,qEAAqE;IACrE,YAAY,CAAC,KAAkB;QAC7B,MAAM,OAAO,GAAG,IAAA,sBAAgB,GAAE,CAAC;QACnC,IAAA,yBAAc,EAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QAClC,KAAK,CAAC,MAAM,CAAC,WAAW,CAAC,GAAG,OAAO,CAAC,SAAS,CAAC;IAChD,CAAC;IAED;;;;;OAKG;IACH,YAAY,CAAC,KAAkB;QAC7B,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC;QAC5B,IAAI,CAAC,MAAM;YAAE,OAAO;QAEpB,MAAM,WAAW,GAAG,IAAI,GAAG,CACzB,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,WAAW,EAAE,KAAK,WAAW,CAAC,CACvE,CAAC;QACF,KAAK,MAAM,MAAM,IAAI,KAAK,CAAC,QAAQ,EAAE,OAAO,IAAI,EAAE,EAAE,CAAC;YACnD,IAAI,MAAM,CAAC,YAAY,CAAC,WAAW,EAAE,KAAK,WAAW,IAAI,MAAM,CAAC,YAAY,IAAI,MAAM,EAAE,CAAC;gBACvF,WAAW,CAAC,GAAG,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;YACvC,CAAC;QACH,CAAC;QAED,KAAK,MAAM,IAAI,IAAI,WAAW,EAAE,CAAC;YAC/B,MAAM,MAAM,GAAG,KAAK,CAAC,cAAc,CAAC;YACpC,IAAI,MAAM,IAAI,IAAI,IAAI,MAAM,EAAE,CAAC;gBAC7B,MAAM,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC;YAC9B,CAAC;iBAAM,CAAC;gBACN,OAAO,MAAM,CAAC,IAAI,CAAC,CAAC;YACtB,CAAC;QACH,CAAC;IACH,CAAC;IAED;;;;;;;;OAQG;IACH,WAAW,CAAC,KAAuB;QACjC,MAAM,MAAM,GAAG,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC;QACnD,IAAI,MAAM,KAAK,SAAS,IAAI,iBAAiB,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;YAC3D,MAAM,IAAI,KAAK,CACb,4DAA4D;gBAC1D,yEAAyE,CAC5E,CAAC;QACJ,CAAC;IACH,CAAC;CACF;AAvDD,4DAuDC;AAED;;;;;;;;;GASG;AACH,SAAgB,yBAAyB,CACvC,UAAiC;IAEjC,IAAI,CAAC,UAAU,CAAC,aAAa,EAAE,CAAC;QAC9B,MAAM,IAAI,KAAK,CACb,2EAA2E;YACzE,4CAA4C,CAC/C,CAAC;IACJ,CAAC;IACD,MAAM,QAAQ,GAAG,UAAU,CAAC,WAAW,CAAC,IAAI,CAC1C,CAAC,UAAU,EAA0C,EAAE,CACrD,UAAU,YAAY,wBAAwB,CACjD,CAAC;IACF,IAAI,QAAQ;QAAE,OAAO,QAAQ,CAAC;IAE9B,MAAM,UAAU,GAAG,IAAI,wBAAwB,EAAE,CAAC;IAClD,UAAU,CAAC,WAAW,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IACxC,OAAO,UAAU,CAAC;AACpB,CAAC"}
1
+ {"version":3,"file":"typeorm-subscriber.js","sourceRoot":"","sources":["../../src/integrations/typeorm-subscriber.ts"],"names":[],"mappings":"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"}
package/dist/types.d.ts CHANGED
@@ -39,8 +39,9 @@ export interface MysqlTableAdapterOptions {
39
39
  databaseName: string;
40
40
  /**
41
41
  * Every base table name that has a per-tenant copy. Required by
42
+ * scopedTable, which only accepts these base names, and by
42
43
  * purgeTenantData, which drops exactly `{base}_{slug}` for each entry and
43
- * nothing else. When set, scopedTable only accepts these base names.
44
+ * nothing else.
44
45
  * No entry may be another entry followed by `_`, since `{base}_{slug}` would
45
46
  * then be ambiguous between two tenants.
46
47
  */
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,mBAAmB;IAClC,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACzD,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC3D,OAAO,IAAI,IAAI,CAAC;IAChB,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACtB;AAED,MAAM,WAAW,aAAa;IAC5B,aAAa,IAAI,OAAO,CAAC,mBAAmB,CAAC,CAAC;IAC9C,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACzD,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACtB;AAID,MAAM,WAAW,YAAY;IAC3B,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;IAC1D,QAAQ,IAAI,YAAY,CAAC;CAC1B;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,OAAO,CAAC;IACjB,eAAe,EAAE,MAAM,CAAC;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,KAAK,CAAA;KAAE,CAAC,CAAC;CAChD;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAID,MAAM,WAAW,yBAAyB;IACxC,yCAAyC;IACzC,IAAI,EAAE,aAAa,CAAC;IACpB,gCAAgC;IAChC,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,wBAAwB;IACvC,yCAAyC;IACzC,IAAI,EAAE,aAAa,CAAC;IACpB,gCAAgC;IAChC,YAAY,EAAE,MAAM,CAAC;IACrB;;;;;;OAMG;IACH,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;CACvB;AAED,MAAM,WAAW,2BAA2B;IAC1C,kEAAkE;IAClE,UAAU,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;IACpE,uEAAuE;IACvE,OAAO,EAAE,MAAM,CAAC;IAChB,mDAAmD;IACnD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,oDAAoD;IACpD,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,uBAAuB;IACtC,kEAAkE;IAClE,UAAU,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;IACpE,2BAA2B;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,mDAAmD;IACnD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,oDAAoD;IACpD,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,mBAAmB;IAClC,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACzD,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC3D,OAAO,IAAI,IAAI,CAAC;IAChB,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACtB;AAED,MAAM,WAAW,aAAa;IAC5B,aAAa,IAAI,OAAO,CAAC,mBAAmB,CAAC,CAAC;IAC9C,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACzD,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACtB;AAID,MAAM,WAAW,YAAY;IAC3B,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;IAC1D,QAAQ,IAAI,YAAY,CAAC;CAC1B;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,OAAO,CAAC;IACjB,eAAe,EAAE,MAAM,CAAC;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,KAAK,CAAA;KAAE,CAAC,CAAC;CAChD;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAID,MAAM,WAAW,yBAAyB;IACxC,yCAAyC;IACzC,IAAI,EAAE,aAAa,CAAC;IACpB,gCAAgC;IAChC,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,wBAAwB;IACvC,yCAAyC;IACzC,IAAI,EAAE,aAAa,CAAC;IACpB,gCAAgC;IAChC,YAAY,EAAE,MAAM,CAAC;IACrB;;;;;;;OAOG;IACH,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;CACvB;AAED,MAAM,WAAW,2BAA2B;IAC1C,kEAAkE;IAClE,UAAU,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;IACpE,uEAAuE;IACvE,OAAO,EAAE,MAAM,CAAC;IAChB,mDAAmD;IACnD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,oDAAoD;IACpD,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,uBAAuB;IACtC,kEAAkE;IAClE,UAAU,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;IACpE,2BAA2B;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,mDAAmD;IACnD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,oDAAoD;IACpD,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stratum-hq/mysql",
3
- "version": "0.4.0",
3
+ "version": "0.6.0",
4
4
  "description": "MySQL tenant isolation for Stratum — shared table, table-per-tenant, and database-per-tenant strategies",
5
5
  "keywords": [
6
6
  "multi-tenancy",
@@ -38,8 +38,8 @@
38
38
  "clean": "rm -rf dist"
39
39
  },
40
40
  "dependencies": {
41
- "@stratum-hq/core": "^1.4.0",
42
- "@stratum-hq/sdk": "^1.2.0"
41
+ "@stratum-hq/core": "^1.5.0",
42
+ "@stratum-hq/sdk": "^1.3.0"
43
43
  },
44
44
  "peerDependencies": {
45
45
  "mysql2": "^3.0.0",
@@ -61,6 +61,7 @@
61
61
  "devDependencies": {
62
62
  "knex": "^3.3.0",
63
63
  "mysql2": "^3.24.4",
64
+ "sequelize": "^6.37.8",
64
65
  "typeorm": "^1.1.1",
65
66
  "typescript": "^5.9.0",
66
67
  "vitest": "^4.1.11"