@stratum-hq/mysql 0.5.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.
- package/README.md +30 -8
- package/dist/integrations/sequelize.d.ts +29 -7
- package/dist/integrations/sequelize.d.ts.map +1 -1
- package/dist/integrations/sequelize.js +400 -9
- package/dist/integrations/sequelize.js.map +1 -1
- package/dist/integrations/typeorm-subscriber.d.ts +68 -8
- package/dist/integrations/typeorm-subscriber.d.ts.map +1 -1
- package/dist/integrations/typeorm-subscriber.js +321 -12
- package/dist/integrations/typeorm-subscriber.js.map +1 -1
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -79,7 +79,7 @@ await adapter.closeAll();
|
|
|
79
79
|
|
|
80
80
|
## ORM Integrations
|
|
81
81
|
|
|
82
|
-
### TypeORM Subscriber
|
|
82
|
+
### TypeORM Subscriber
|
|
83
83
|
|
|
84
84
|
```typescript
|
|
85
85
|
import { registerStratumSubscriber } from "@stratum-hq/mysql";
|
|
@@ -91,15 +91,21 @@ registerStratumSubscriber(dataSource);
|
|
|
91
91
|
|
|
92
92
|
Call `registerStratumSubscriber()` after `dataSource.initialize()`. It throws before that, because `initialize()` replaces the subscriber list. Do not put `StratumTypeOrmSubscriber` in the `subscribers` option: TypeORM only loads classes decorated with `@EventSubscriber()` from that option.
|
|
93
93
|
|
|
94
|
-
Inserts get the current tenant's `tenant_id
|
|
94
|
+
Inserts get the current tenant's `tenant_id`, in whichever entity property maps to the `tenant_id` column (a relation whose join column is `tenant_id` cannot set or change it). Updates never change `tenant_id`: `save()` keeps the loaded value, and `update()` / query builder updates drop it from the SET values. The insert and update query builders enforce both, so `save(entity, { listeners: false })` and `.callListeners(false)` do not skip them.
|
|
95
95
|
|
|
96
|
-
`registerStratumSubscriber()` also scopes updates and deletes to the current tenant: repository `update()`, `delete()`, `softDelete()`, `restore()`, `save()` of an existing row, `remove()`, and query builder updates and deletes get `tenant_id = <current tenant>` ANDed to their WHERE clause. A row of another tenant is left unchanged, `save()` of a row that belongs to another tenant throws
|
|
96
|
+
`registerStratumSubscriber()` also scopes updates and deletes to the current tenant: repository `update()`, `delete()`, `softDelete()`, `restore()`, `save()` of an existing row, `remove()`, and query builder updates and deletes get `tenant_id = <current tenant>` ANDed to their WHERE clause. A row of another tenant is left unchanged, and an update or delete of a tenant table outside a tenant context is refused. `save()` of a row that belongs to another tenant throws: before an insert (other than an upsert) whose entity supplies its whole primary key, the subscriber looks that key up without the read scope, uses the result only for this check, and refuses the insert when the row belongs to another tenant. An update or delete builder aimed at a raw table name (not an entity) is treated as a tenant table and always gets the tenant condition, so it fails on a table without `tenant_id`. `TRUNCATE` of a table with a `tenant_id` column (`repository.clear()`, `queryRunner.clearTable()`) is refused, because it would remove every tenant's rows. A subscriber added to `dataSource.subscribers` by hand refuses every UPDATE and DELETE, so always register it with `registerStratumSubscriber()`.
|
|
97
97
|
|
|
98
98
|
Upserts (`repository.upsert()` and `.orUpdate()`) also get the current tenant's `tenant_id` on insert. If the conflict update writes `tenant_id`, the subscriber rejects the statement before it runs. To upsert, leave `tenant_id` out of the entity values and out of the `orUpdate()` columns.
|
|
99
99
|
|
|
100
|
+
`INSERT ... SELECT` (`valuesFromSelect()`) into a tenant entity is refused, because the tenant of the selected rows cannot be set. Into another table it is allowed, and a SELECT of a tenant entity keeps its tenant condition.
|
|
101
|
+
|
|
100
102
|
MySQL applies `ON DUPLICATE KEY UPDATE` on a conflict with any unique key of the table, whatever conflict columns you pass. The subscriber therefore allows an upsert only when every unique key of the target table, including the primary key, contains `tenant_id` (for example `PRIMARY KEY (tenant_id, id)`). It reads the keys from `information_schema` before the statement runs, and rejects the upsert otherwise.
|
|
101
103
|
|
|
102
|
-
|
|
104
|
+
`registerStratumSubscriber()` also scopes reads. Every query that TypeORM's select query builder builds on the data source gets `tenant_id = <current tenant>`: repository `find*()`, `findOne*()`, `count*()`, `exists*()`, `sum()` / `average()` / `minimum()` / `maximum()` and `preload()`, query builder `getMany()`, `getOne()`, `getRawMany()`, `getRawOne()`, `getCount()`, `getManyAndCount()`, `getExists()` and `stream()`, relation loading (joins, eager relations, `relationLoadStrategy: "query"`), the row that `save()` loads before it writes, subqueries, and the count and pagination queries TypeORM builds internally. The condition is ANDed to the WHERE clause for the entity in FROM, so an `orWhere()` cannot widen the read, and it is added to the ON condition of every joined entity with a `tenant_id` column, so `leftJoinAndSelect()` still returns the parent row and leaves another tenant's related row out. A read of an entity with a `tenant_id` column outside a tenant context is refused. Entities without a `tenant_id` column, and data sources without the subscriber, are not affected.
|
|
105
|
+
|
|
106
|
+
A view entity whose `expression` is a query builder is created by `synchronize()` without the tenant condition, because the view definition is one schema object shared by all tenants. Reads from a view entity are scoped like reads from a table when the view has a `tenant_id` column. A view without a `tenant_id` column is read unscoped.
|
|
107
|
+
|
|
108
|
+
**Limitation:** raw SQL (`dataSource.query()`, `queryRunner.query()`), SQL taken from `getQuery()` / `getQueryAndParameters()` and run by hand (a select builder's SQL includes the tenant condition, but an update or delete builder adds it only when its `execute()` runs), reads from and inserts into a table name that does not exactly match (including letter case) the name or table name of an entity on the data source (such inserts are not given a tenant_id), and many-to-many junction tables are not scoped. Add the tenant condition to those yourself, or use the shared-table adapter's structured methods.
|
|
103
109
|
|
|
104
110
|
### Knex Helper
|
|
105
111
|
|
|
@@ -121,13 +127,29 @@ Joins (`join()`, `leftJoin()`, `crossJoin()`, `joinRaw()` and the other join for
|
|
|
121
127
|
import { withMysqlTenantScope } from "@stratum-hq/mysql";
|
|
122
128
|
|
|
123
129
|
await withMysqlTenantScope(sequelize, "tenant-a", async (scoped, transaction) => {
|
|
124
|
-
//
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
const [rows] = await scoped.query("SELECT @stratum_tenant_id", { transaction });
|
|
130
|
+
// Models with a tenant_id attribute see and change only this tenant's rows.
|
|
131
|
+
const notes = await Note.findAll({ include: ["owner"], transaction });
|
|
132
|
+
await Note.create({ name: "new" }, { transaction }); // tenant_id is set for you
|
|
128
133
|
});
|
|
129
134
|
```
|
|
130
135
|
|
|
136
|
+
Inside the callback, for every model with a `tenant_id` attribute:
|
|
137
|
+
|
|
138
|
+
- `findAll()`, `findOne()`, `findByPk()`, `findAndCountAll()`, `count()`, `sum()`, `min()`, `max()` and `reload()` return only the tenant's rows. Every include of a tenant model is filtered in its join condition, including includes added by default and named scopes, by an included model's own default scope, by `include: { all: true }` and by hooks, so an include that is not `required` still returns the parent row. A scope's own where clause is kept.
|
|
139
|
+
- `update()`, `destroy()`, `restore()`, `increment()` and `decrement()` change only the tenant's rows. Updates never write `tenant_id`, whether it is given by attribute name or column name, in any letter case, and `increment()` / `decrement()` of `tenant_id` are refused. As outside the helper, `update()`, `destroy()` and `increment()` without a `where` are refused by Sequelize.
|
|
140
|
+
- `create()`, `save()` of a new instance and `bulkCreate()` write the tenant's `tenant_id`, whatever value the caller passed, and add it to a `fields` list that leaves it out.
|
|
141
|
+
- `save()`, `destroy()` and `restore()` of an instance whose row belongs to another tenant throw, and so do they for an existing instance of a model without a primary key, whose row cannot be checked.
|
|
142
|
+
- `upsert()`, `bulkCreate()` with `updateOnDuplicate`, `truncate()`, and `or: true` or `right: true` on an include of a tenant model are refused.
|
|
143
|
+
- Right before each query, the tenant condition is checked again on the where clause and on every include of a tenant model. A query whose condition a hook removed (for example a `beforeFind` hook that replaces `options.where`, or a `beforeFindAfterOptions` hook that adds includes) is refused.
|
|
144
|
+
- A query whose options name a tenant model or instance but that did not go through these methods throws, also when it runs inside a model hook. This covers, for example, `queryInterface.select(Note, ...)`. `queryInterface` calls that take only a table name, such as `bulkInsert()`, `bulkUpdate()` and `bulkDelete()`, carry no model and are not scoped, like raw SQL.
|
|
145
|
+
- `hooks: false` does not skip the filter.
|
|
146
|
+
|
|
147
|
+
`upsert()` on a model with a `tenant_id` attribute is always refused inside the callback, because MySQL applies `ON DUPLICATE KEY UPDATE` on any unique key, so a conflict could update another tenant's row. Look the row up with `findOne()` and then `update()` or `create()` it instead.
|
|
148
|
+
|
|
149
|
+
The helper also sets `@stratum_tenant_id` on the transaction's connection and clears it in a `finally` block. Pass the transaction to queries that must run in it.
|
|
150
|
+
|
|
151
|
+
**Not scoped:** raw `sequelize.query()`, `queryInterface` calls by table name, models without a `tenant_id` attribute, the through (junction) model of a many-to-many include, and any code outside the callback. The helper throws when it is given something other than a Sequelize v6 instance.
|
|
152
|
+
|
|
131
153
|
## MySQL Views (not supported)
|
|
132
154
|
|
|
133
155
|
`createTenantView()` is deprecated and always throws. MySQL does not allow a view to read a session variable (`ER_VIEW_SELECT_VARIABLE`), so a view filtered on `@stratum_tenant_id` cannot be created. Use the shared-table adapter's scoped methods. `setTenantSession()` and `dropTenantView()` remain available.
|
|
@@ -3,14 +3,36 @@ export interface SequelizeLike {
|
|
|
3
3
|
transaction<T>(fn: (t: unknown) => Promise<T>): Promise<T>;
|
|
4
4
|
}
|
|
5
5
|
/**
|
|
6
|
-
* Runs fn
|
|
7
|
-
*
|
|
8
|
-
* finally block, even if fn throws.
|
|
6
|
+
* Runs fn with the tenant's rows scoped for Sequelize models, inside a
|
|
7
|
+
* transaction that also sets the @stratum_tenant_id session variable.
|
|
9
8
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
9
|
+
* Inside fn, for every model with a tenant_id attribute:
|
|
10
|
+
* - findAll, findOne, findByPk, findAndCountAll, count, sum, min, max and
|
|
11
|
+
* reload() see only the tenant's rows, and every include of a tenant model
|
|
12
|
+
* (including those that scopes and hooks add) is filtered in its join
|
|
13
|
+
* condition, so a non-required include keeps the parent;
|
|
14
|
+
* - update, destroy, restore and increment/decrement change only the tenant's
|
|
15
|
+
* rows; updates never write tenant_id (by attribute or column name), and
|
|
16
|
+
* increment/decrement of tenant_id is refused. update, destroy and increment
|
|
17
|
+
* without a where clause are still refused by Sequelize;
|
|
18
|
+
* - create, save of a new instance and bulkCreate write the tenant's tenant_id;
|
|
19
|
+
* - save, destroy and restore of an instance whose row belongs to another
|
|
20
|
+
* tenant, or of a model without a primary key, throw;
|
|
21
|
+
* - upsert, bulkCreate with updateOnDuplicate, truncate, and `or` / `right`
|
|
22
|
+
* on an include of a tenant model are refused, and a query that carries a
|
|
23
|
+
* tenant model but bypasses these methods throws, also inside model hooks;
|
|
24
|
+
* - right before each query the tenant condition is checked again, so a query
|
|
25
|
+
* whose condition a hook removed is refused.
|
|
26
|
+
* Hooks passed `hooks: false` do not skip the filter. Raw `sequelize.query()`
|
|
27
|
+
* and models without a tenant_id attribute are not scoped. Outside fn,
|
|
28
|
+
* Sequelize behaves as usual.
|
|
29
|
+
*
|
|
30
|
+
* The session variable exists only on the transaction's connection. fn
|
|
31
|
+
* receives that transaction as its second argument; pass it to queries that
|
|
32
|
+
* must run in the transaction.
|
|
33
|
+
*
|
|
34
|
+
* @throws Error when sequelize is not a Sequelize v6 instance, because the
|
|
35
|
+
* tenant scope could not be applied.
|
|
14
36
|
*/
|
|
15
37
|
export declare function withMysqlTenantScope<T>(sequelize: SequelizeLike, tenantId: string, fn: (sequelize: SequelizeLike, transaction: unknown) => Promise<T>): Promise<T>;
|
|
16
38
|
//# sourceMappingURL=sequelize.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sequelize.d.ts","sourceRoot":"","sources":["../../src/integrations/sequelize.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"sequelize.d.ts","sourceRoot":"","sources":["../../src/integrations/sequelize.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,aAAa;IAC5B,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACxD,WAAW,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;CAC5D;AA+CD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAsB,oBAAoB,CAAC,CAAC,EAC1C,SAAS,EAAE,aAAa,EACxB,QAAQ,EAAE,MAAM,EAChB,EAAE,EAAE,CAAC,SAAS,EAAE,aAAa,EAAE,WAAW,EAAE,OAAO,KAAK,OAAO,CAAC,CAAC,CAAC,GACjE,OAAO,CAAC,CAAC,CAAC,CAcZ"}
|
|
@@ -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
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
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
|
|
11
|
-
* transaction as its second argument
|
|
12
|
-
*
|
|
13
|
-
*
|
|
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,6 +39,7 @@ 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 {
|
|
@@ -32,12 +61,20 @@ export interface EntitySubscriberInterface {
|
|
|
32
61
|
export interface TypeOrmDataSourceLike {
|
|
33
62
|
readonly isInitialized: boolean;
|
|
34
63
|
readonly subscribers: unknown[];
|
|
35
|
-
/**
|
|
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
|
+
*/
|
|
36
68
|
createQueryBuilder(): {
|
|
69
|
+
insert(): object;
|
|
37
70
|
update(): object;
|
|
38
71
|
delete(): object;
|
|
39
72
|
softDelete(): object;
|
|
40
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
|
+
}[];
|
|
41
78
|
}
|
|
42
79
|
/**
|
|
43
80
|
* TypeORM subscriber that injects tenant_id into inserted entities via ALS and
|
|
@@ -51,13 +88,35 @@ export interface TypeOrmDataSourceLike {
|
|
|
51
88
|
* softDelete(), restore()) to the current tenant: `tenant_id = <tenant>` is
|
|
52
89
|
* ANDed to their WHERE clause, and they are refused outside a tenant context.
|
|
53
90
|
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
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.
|
|
57
106
|
*/
|
|
58
107
|
export declare class StratumTypeOrmSubscriber implements EntitySubscriberInterface {
|
|
59
|
-
/**
|
|
60
|
-
|
|
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>;
|
|
61
120
|
/**
|
|
62
121
|
* Keeps tenant_id out of an update. On save() the loaded value is restored,
|
|
63
122
|
* so the column is unchanged; on a repository or query builder update the
|
|
@@ -94,8 +153,8 @@ export declare class StratumTypeOrmSubscriber implements EntitySubscriberInterfa
|
|
|
94
153
|
* A second call returns the subscriber that the first call added, so the
|
|
95
154
|
* data source never runs the subscriber twice.
|
|
96
155
|
*
|
|
97
|
-
* It also scopes TypeORM's update, delete and soft-delete query
|
|
98
|
-
* current tenant (see StratumTypeOrmSubscriber). The subscriber refuses an
|
|
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
|
|
99
158
|
* UPDATE or DELETE on a data source that was not registered this way.
|
|
100
159
|
*
|
|
101
160
|
* @param dataSource - An initialized TypeORM `DataSource`.
|
|
@@ -103,4 +162,5 @@ export declare class StratumTypeOrmSubscriber implements EntitySubscriberInterfa
|
|
|
103
162
|
* the subscriber list, so an earlier registration has no effect.
|
|
104
163
|
*/
|
|
105
164
|
export declare function registerStratumSubscriber(dataSource: TypeOrmDataSourceLike): StratumTypeOrmSubscriber;
|
|
165
|
+
export {};
|
|
106
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":"
|
|
1
|
+
{"version":3,"file":"typeorm-subscriber.d.ts","sourceRoot":"","sources":["../../src/integrations/typeorm-subscriber.ts"],"names":[],"mappings":"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"}
|
|
@@ -2,6 +2,7 @@
|
|
|
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
|
|
@@ -17,6 +18,19 @@ const TENANT_COLUMN_SQL = "SELECT COUNT(*) AS n FROM information_schema.COLUMNS
|
|
|
17
18
|
"WHERE TABLE_SCHEMA = COALESCE(?, DATABASE()) AND TABLE_NAME = ? AND LOWER(COLUMN_NAME) = 'tenant_id'";
|
|
18
19
|
const TENANT_PARAMETER = "stratumTenantId";
|
|
19
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();
|
|
20
34
|
/** Data sources whose update and delete query builders are tenant-scoped. */
|
|
21
35
|
const scopedDataSources = new WeakSet();
|
|
22
36
|
const UNIQUE_KEYS_SQL = "SELECT INDEX_NAME AS index_name, COLUMN_NAME AS column_name " +
|
|
@@ -34,16 +48,46 @@ const UNIQUE_KEYS_SQL = "SELECT INDEX_NAME AS index_name, COLUMN_NAME AS column_
|
|
|
34
48
|
* softDelete(), restore()) to the current tenant: `tenant_id = <tenant>` is
|
|
35
49
|
* ANDed to their WHERE clause, and they are refused outside a tenant context.
|
|
36
50
|
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
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.
|
|
40
66
|
*/
|
|
41
67
|
class StratumTypeOrmSubscriber {
|
|
42
|
-
/**
|
|
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
|
+
*/
|
|
43
79
|
beforeInsert(event) {
|
|
44
80
|
const context = (0, sdk_1.getTenantContext)();
|
|
45
81
|
(0, utils_js_1.assertTenantId)(context.tenant_id);
|
|
46
|
-
|
|
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
|
+
}
|
|
47
91
|
}
|
|
48
92
|
/**
|
|
49
93
|
* Keeps tenant_id out of an update. On save() the loaded value is restored,
|
|
@@ -82,6 +126,11 @@ class StratumTypeOrmSubscriber {
|
|
|
82
126
|
tenantProps.add(column.propertyName);
|
|
83
127
|
}
|
|
84
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
|
+
}
|
|
85
134
|
for (const prop of tenantProps) {
|
|
86
135
|
const loaded = event.databaseEntity;
|
|
87
136
|
if (loaded && prop in loaded) {
|
|
@@ -149,18 +198,44 @@ function addTenantCondition(builder) {
|
|
|
149
198
|
}
|
|
150
199
|
const context = (0, sdk_1.getTenantContext)();
|
|
151
200
|
(0, utils_js_1.assertTenantId)(context.tenant_id);
|
|
201
|
+
stripTenantFromUpdateValues(builder);
|
|
152
202
|
const qualified = builder.expressionMap.aliasNamePrefixingEnabled && alias
|
|
153
203
|
? `${builder.escape(alias.name)}.${builder.escape(column)}`
|
|
154
204
|
: builder.escape(column);
|
|
155
205
|
const condition = `${qualified} = :${TENANT_PARAMETER}`;
|
|
156
206
|
const existing = builder.expressionMap.extraAppendedAndWhereCondition;
|
|
157
|
-
//
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
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
|
+
}
|
|
162
216
|
builder.setParameter(TENANT_PARAMETER, context.tenant_id);
|
|
163
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
|
+
}
|
|
164
239
|
/** Wraps execute() of TypeORM's update, delete and soft-delete query builder classes, once. */
|
|
165
240
|
function scopeWriteBuilders(dataSource) {
|
|
166
241
|
const builders = [
|
|
@@ -181,6 +256,237 @@ function scopeWriteBuilders(dataSource) {
|
|
|
181
256
|
}
|
|
182
257
|
scopedDataSources.add(dataSource);
|
|
183
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
|
+
}
|
|
184
490
|
/**
|
|
185
491
|
* Refuses a TRUNCATE (Repository.clear(), QueryRunner.clearTable()) of a table
|
|
186
492
|
* with a tenant_id column, because it would empty the table for every tenant.
|
|
@@ -227,8 +533,8 @@ async function assertUniqueKeysIncludeTenant(event) {
|
|
|
227
533
|
* A second call returns the subscriber that the first call added, so the
|
|
228
534
|
* data source never runs the subscriber twice.
|
|
229
535
|
*
|
|
230
|
-
* It also scopes TypeORM's update, delete and soft-delete query
|
|
231
|
-
* current tenant (see StratumTypeOrmSubscriber). The subscriber refuses an
|
|
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
|
|
232
538
|
* UPDATE or DELETE on a data source that was not registered this way.
|
|
233
539
|
*
|
|
234
540
|
* @param dataSource - An initialized TypeORM `DataSource`.
|
|
@@ -240,7 +546,10 @@ function registerStratumSubscriber(dataSource) {
|
|
|
240
546
|
throw new Error("Stratum: call registerStratumSubscriber() after dataSource.initialize(). " +
|
|
241
547
|
"initialize() replaces the subscriber list.");
|
|
242
548
|
}
|
|
549
|
+
scopeSelectBuilder(dataSource);
|
|
243
550
|
scopeWriteBuilders(dataSource);
|
|
551
|
+
scopeInsertBuilder(dataSource);
|
|
552
|
+
exemptViewExpressions(dataSource);
|
|
244
553
|
const existing = dataSource.subscribers.find((subscriber) => subscriber instanceof StratumTypeOrmSubscriber);
|
|
245
554
|
if (existing)
|
|
246
555
|
return existing;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"typeorm-subscriber.js","sourceRoot":"","sources":["../../src/integrations/typeorm-subscriber.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"typeorm-subscriber.js","sourceRoot":"","sources":["../../src/integrations/typeorm-subscriber.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stratum-hq/mysql",
|
|
3
|
-
"version": "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",
|
|
@@ -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"
|