@mikro-orm/core 7.2.0-dev.14 → 7.2.0-dev.15
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/EntityManager.d.ts +30 -2
- package/EntityManager.js +192 -40
- package/MikroORM.js +3 -0
- package/connections/Connection.d.ts +3 -1
- package/entity/Collection.js +4 -2
- package/entity/EntityLoader.js +1 -1
- package/entity/defineEntity.d.ts +3 -0
- package/enums.d.ts +3 -1
- package/errors.d.ts +31 -0
- package/errors.js +72 -0
- package/exceptions.d.ts +5 -0
- package/exceptions.js +5 -0
- package/index.d.ts +1 -1
- package/metadata/MetadataDiscovery.d.ts +1 -0
- package/metadata/MetadataDiscovery.js +31 -0
- package/metadata/types.d.ts +5 -1
- package/package.json +1 -1
- package/platforms/Platform.d.ts +9 -0
- package/platforms/Platform.js +40 -0
- package/typings.d.ts +36 -0
- package/typings.js +1 -0
- package/unit-of-work/UnitOfWork.js +1 -0
- package/utils/Configuration.d.ts +15 -1
- package/utils/Configuration.js +11 -1
- package/utils/QueryHelper.d.ts +12 -0
- package/utils/QueryHelper.js +63 -0
- package/utils/TransactionManager.js +1 -1
- package/utils/Utils.d.ts +2 -0
- package/utils/Utils.js +5 -1
- package/utils/env-vars.js +1 -0
- package/utils/index.d.ts +1 -0
- package/utils/index.js +1 -0
- package/utils/rls-utils.d.ts +35 -0
- package/utils/rls-utils.js +97 -0
package/errors.js
CHANGED
|
@@ -94,6 +94,30 @@ export class ValidationError extends Error {
|
|
|
94
94
|
static cannotUseGlobalContext() {
|
|
95
95
|
return new ValidationError("Using global EntityManager instance methods for context specific actions is disallowed. If you need to work with the global instance's identity map, use `allowGlobalContext` configuration option or `fork()` instead.");
|
|
96
96
|
}
|
|
97
|
+
static sessionContextNotSupported() {
|
|
98
|
+
return new ValidationError('Database session context (row level security) is only supported by the PostgreSQL driver. To test a PostgreSQL app without a server, use the `@mikro-orm/pglite` driver instead of sqlite.');
|
|
99
|
+
}
|
|
100
|
+
static sessionContextRequiresImplicitTransactions() {
|
|
101
|
+
return new ValidationError("Cannot set a database session context (row level security) with the 'transaction' strategy while 'implicitTransactions' is disabled. The context is applied on transaction begin, but writes would run without a transaction and silently bypass the policies. Enable 'implicitTransactions', wrap the work in 'em.transactional()', or use the 'connection' session context strategy.");
|
|
102
|
+
}
|
|
103
|
+
static sessionContextWithDisabledTransactions() {
|
|
104
|
+
return new ValidationError("Cannot set a database session context (row level security) with the 'transaction' strategy while transactions are disabled via 'disableTransactions'. The context is applied on transaction begin, so flushes would run without it and silently bypass the policies. Enable transactions, or use the 'connection' session context strategy.");
|
|
105
|
+
}
|
|
106
|
+
static sessionContextInsideTransaction(action = 'set') {
|
|
107
|
+
const advice = action === 'set'
|
|
108
|
+
? "Set the session context before starting the transaction (e.g. via 'em.fork({ session })')."
|
|
109
|
+
: 'Clear the session context outside the transaction.';
|
|
110
|
+
return new ValidationError(`Cannot ${action} a database session context (row level security) inside an active transaction. The context is applied when the transaction begins or the connection is reserved, so the change would never reach an already-open transaction (with the 'connection' strategy the pinned connection was reserved with the previous context). ${advice}`);
|
|
111
|
+
}
|
|
112
|
+
static cannotStageNonScalarSessionVariable(filterName, argName) {
|
|
113
|
+
return new ValidationError(`Cannot stage the '${argName}' argument of filter '${filterName}' as a session variable (row level security) — only scalar values (string, number, boolean, Date) can be mirrored to the database policy backing the filter.`);
|
|
114
|
+
}
|
|
115
|
+
static sessionContextStreamRequiresTransaction() {
|
|
116
|
+
return new ValidationError("Cannot stream under a database session context (row level security) with the 'transaction' strategy outside a transaction. Streaming never opens the implicit transaction that applies the context, so the streamed rows would not be scoped by it (other tenants' rows would leak). Wrap the stream in 'em.transactional()', or use the 'connection' session context strategy.");
|
|
117
|
+
}
|
|
118
|
+
static connectionSessionContextNotSupported() {
|
|
119
|
+
return new ValidationError("The 'connection' session context strategy requires a driver that supports per-acquire connection hooks (the `postgresql` driver). Use the default 'transaction' strategy instead.");
|
|
120
|
+
}
|
|
97
121
|
static cannotUseOperatorsInsideEmbeddables(entityName, propName, payload) {
|
|
98
122
|
return new ValidationError(`Using operators inside embeddables is not allowed, move the operator above. (property: ${Utils.className(entityName)}.${propName}, payload: ${inspect(payload)})`);
|
|
99
123
|
}
|
|
@@ -250,6 +274,54 @@ export class MetadataError extends ValidationError {
|
|
|
250
274
|
static triggersNotSupportedByDriver(meta) {
|
|
251
275
|
return new MetadataError(`Entity ${meta.className} defines database triggers which are not supported by the current driver. Triggers are only available with SQL drivers.`);
|
|
252
276
|
}
|
|
277
|
+
/** Thrown when row level security is declared on an entity using a driver that does not support it. */
|
|
278
|
+
static rowLevelSecurityNotSupportedByDriver(meta) {
|
|
279
|
+
return new MetadataError(`Entity ${meta.className} declares row level security which is not supported by the current driver. Row level security is only available with the PostgreSQL driver. To test a PostgreSQL app without a server, use the \`@mikro-orm/pglite\` driver instead of sqlite.`);
|
|
280
|
+
}
|
|
281
|
+
/** Thrown when row level security is declared on a non-root entity of an STI hierarchy. */
|
|
282
|
+
static rowLevelSecurityOnNonRootStiEntity(meta) {
|
|
283
|
+
return new MetadataError(`Entity ${meta.className} declares row level security, but it is part of a single table inheritance hierarchy. Declare policies on the root entity ${meta.root.className} instead, as the whole hierarchy shares a single table.`);
|
|
284
|
+
}
|
|
285
|
+
/** Thrown when two policies on the same entity are given the same explicit name. */
|
|
286
|
+
static duplicatePolicyName(meta, name) {
|
|
287
|
+
return new MetadataError(`Entity ${meta.className} declares multiple row level security policies named '${name}'. Policy names must be unique per table; rename one of them or omit the name to use an auto-generated one.`);
|
|
288
|
+
}
|
|
289
|
+
/** Thrown when a filter flagged with `rls` is declared on a driver that does not support row level security. */
|
|
290
|
+
static rlsFilterNotSupportedByDriver(meta, filterName) {
|
|
291
|
+
return new MetadataError(`Filter '${filterName}' on entity ${meta.className} is flagged with 'rls', which is only supported by the PostgreSQL driver. To test a PostgreSQL app without a server, use the \`@mikro-orm/pglite\` driver instead of sqlite.`);
|
|
292
|
+
}
|
|
293
|
+
/** Thrown when a filter flagged with `rls` is declared on a non-root entity of an STI hierarchy. */
|
|
294
|
+
static rlsFilterOnNonRootStiEntity(meta, filterName) {
|
|
295
|
+
return new MetadataError(`Filter '${filterName}' on entity ${meta.className} is flagged with 'rls', but the entity is part of a single table inheritance hierarchy. Declare the filter on the root entity ${meta.root.className} instead, as the whole hierarchy shares a single table and the policy would never be created otherwise.`);
|
|
296
|
+
}
|
|
297
|
+
/** Thrown when a global (config or EM registered) filter is flagged with `rls`; RLS filters must be entity scoped. */
|
|
298
|
+
static rlsFilterMustBeEntityScoped(filterName) {
|
|
299
|
+
return new MetadataError(`Filter '${filterName}' is a global filter and cannot be flagged with 'rls'. RLS filters must be declared on an entity via @Filter() so a policy can be attached to its table.`);
|
|
300
|
+
}
|
|
301
|
+
/** Thrown when an entity-scoped `rls` filter is registered at runtime via `em.addFilter()` instead of in metadata. */
|
|
302
|
+
static rlsFilterCannotBeRegisteredAtRuntime(filterName) {
|
|
303
|
+
return new MetadataError(`Filter '${filterName}' cannot be flagged with 'rls' when registered at runtime via 'em.addFilter()'. RLS filters must be declared in entity metadata via the @Filter() decorator (or the entity 'filters' option) so a policy can be attached to the table.`);
|
|
304
|
+
}
|
|
305
|
+
/** Thrown when a filter's custom `setting` is used with more than one argument. */
|
|
306
|
+
static rlsFilterMultiArgSetting(filterName, args) {
|
|
307
|
+
return new MetadataError(`Filter '${filterName}' sets a custom 'setting' but references multiple arguments (${args.join(', ')}). A custom 'setting' is only allowed for single-argument RLS filters; remove it to use the default 'mikro.${filterName}.<arg>' names.`);
|
|
308
|
+
}
|
|
309
|
+
/** Thrown when an `rls` filter's condition depends on runtime state and cannot be compiled to a static policy. */
|
|
310
|
+
static rlsFilterDependsOnRuntimeState(filterName) {
|
|
311
|
+
return new MetadataError(`Filter '${filterName}' cannot be compiled to an RLS policy because its condition depends on runtime state (it accesses 'em', 'type', 'options' or 'entityName', or resolves asynchronously). Declare an explicit policy instead.`);
|
|
312
|
+
}
|
|
313
|
+
/** Thrown when an `rls` filter compares against a column whose type has no automatic session-variable cast. */
|
|
314
|
+
static rlsFilterUncastableType(filterName, columnType) {
|
|
315
|
+
return new MetadataError(`Filter '${filterName}' cannot be compiled to an RLS policy because the column type '${columnType}' has no automatic session-variable cast. Declare an explicit policy instead.`);
|
|
316
|
+
}
|
|
317
|
+
/** Thrown when an `rls` filter references an argument outside of a direct comparison, which cannot be compiled. */
|
|
318
|
+
static rlsFilterUnsupportedCond(filterName) {
|
|
319
|
+
return new MetadataError(`Filter '${filterName}' cannot be compiled to an RLS policy because it references an argument outside of a direct comparison (only expressions like { prop: args.x } are supported). Declare an explicit policy instead.`);
|
|
320
|
+
}
|
|
321
|
+
/** Thrown when an `rls` filter compares against a column the schema generator does not manage. */
|
|
322
|
+
static rlsFilterUnmanagedColumn(filterName, column) {
|
|
323
|
+
return new MetadataError(`Filter '${filterName}' cannot be compiled to an RLS policy because column '${column}' is not part of the managed schema (e.g. the property is marked 'persist: false' or the column is excluded via 'skipColumns'). Declare an explicit policy instead.`);
|
|
324
|
+
}
|
|
253
325
|
static fromMessage(meta, prop, message) {
|
|
254
326
|
return new MetadataError(`${meta.className}.${prop.name} ${message}`);
|
|
255
327
|
}
|
package/exceptions.d.ts
CHANGED
|
@@ -102,3 +102,8 @@ export declare class TableNotFoundException extends DatabaseObjectNotFoundExcept
|
|
|
102
102
|
*/
|
|
103
103
|
export declare class UniqueConstraintViolationException extends ConstraintViolationException {
|
|
104
104
|
}
|
|
105
|
+
/**
|
|
106
|
+
* Exception for a row-level security policy violation (failed `WITH CHECK`) detected in the driver.
|
|
107
|
+
*/
|
|
108
|
+
export declare class RowLevelSecurityViolationException extends ConstraintViolationException {
|
|
109
|
+
}
|
package/exceptions.js
CHANGED
|
@@ -115,3 +115,8 @@ export class TableNotFoundException extends DatabaseObjectNotFoundException {
|
|
|
115
115
|
*/
|
|
116
116
|
export class UniqueConstraintViolationException extends ConstraintViolationException {
|
|
117
117
|
}
|
|
118
|
+
/**
|
|
119
|
+
* Exception for a row-level security policy violation (failed `WITH CHECK`) detected in the driver.
|
|
120
|
+
*/
|
|
121
|
+
export class RowLevelSecurityViolationException extends ConstraintViolationException {
|
|
122
|
+
}
|
package/index.d.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* @module core
|
|
4
4
|
*/
|
|
5
5
|
export { EntityMetadata, PrimaryKeyProp, EntityRepositoryType, OptionalProps, EagerProps, HiddenProps, Config, EntityName, IndexHints, } from './typings.js';
|
|
6
|
-
export type { CompiledFunctions, Constructor, ConnectionType, Dictionary, Primary, IPrimaryKey, ObjectQuery, FilterQuery, IWrappedEntity, InferEntityName, EntityData, Highlighter, MaybePromise, AnyEntity, EntityClass, EntityProperty, PopulateOptions, Populate, Loaded, New, LoadedReference, LoadedCollection, IMigrator, IMigrationGenerator, MigratorEvent, GetRepository, MigrationObject, DeepPartial, PrimaryProperty, Cast, IsUnknown, EntityDictionary, EntityDTO, EntityDTOFlat, EntityDTOProp, SerializeDTO, MigrationDiff, GenerateOptions, FilterObject, IndexFilterQuery, ExtractIndexHints, ExtractDefineEntityProperties, IndexName, IndexColumns, WithUsingOptions, IMigrationRunner, IEntityGenerator, ISeedManager, SeederObject, IMigratorStorage, RequiredEntityData, CheckCallback, TriggerCallback, IndexCallback, FormulaCallback, FormulaColumns, FormulaTable, SchemaTable, SchemaColumns, SchemaColumnRef, EntityDataPropValue, SimpleColumnMeta, Rel, Ref, LazyRef, ScalarRef, EntityRef, ISchemaGenerator, MigrationInfo, MigrateOptions, MigrationResult, MigrationRow, EntityKey, EntityValue, EntityDataValue, FilterKey, EntityType, FromEntityType, Selected, IsSubset, EntityProps, ExpandProperty, ExpandScalar, FilterItemValue, ExpandQuery, Scalar, ExpandHint, FilterValue, MergeLoaded, MergeSelected, TypeConfig, AnyString, ClearDatabaseOptions, CreateSchemaOptions, EnsureDatabaseOptions, UpdateSchemaOptions, DropSchemaOptions, RefreshDatabaseOptions, AutoPath, UnboxArray, MetadataProcessor, ImportsResolver, RequiredNullable, DefineConfig, Opt, Hidden, EntitySchemaWithMeta, InferEntity, CheckConstraint, TriggerDef, RoutineReturns, RoutineBodyCallback, RoutineJsBody, RoutineIgnoreField, RoutineParamConfig, RoutineConfig, RoutineRuntimeType, RoutineArgs, RoutineReturn, GeneratedColumnCallback, FilterDef, EntityCtor, Subquery, PopulateHintOptions, Prefixes, } from './typings.js';
|
|
6
|
+
export type { CompiledFunctions, Constructor, ConnectionType, Dictionary, Primary, IPrimaryKey, ObjectQuery, FilterQuery, IWrappedEntity, InferEntityName, EntityData, Highlighter, MaybePromise, AnyEntity, EntityClass, EntityProperty, PopulateOptions, Populate, Loaded, New, LoadedReference, LoadedCollection, IMigrator, IMigrationGenerator, MigratorEvent, GetRepository, MigrationObject, DeepPartial, PrimaryProperty, Cast, IsUnknown, EntityDictionary, EntityDTO, EntityDTOFlat, EntityDTOProp, SerializeDTO, MigrationDiff, GenerateOptions, FilterObject, IndexFilterQuery, ExtractIndexHints, ExtractDefineEntityProperties, IndexName, IndexColumns, WithUsingOptions, IMigrationRunner, IEntityGenerator, ISeedManager, SeederObject, IMigratorStorage, RequiredEntityData, CheckCallback, TriggerCallback, IndexCallback, FormulaCallback, FormulaColumns, FormulaTable, SchemaTable, SchemaColumns, SchemaColumnRef, EntityDataPropValue, SimpleColumnMeta, Rel, Ref, LazyRef, ScalarRef, EntityRef, ISchemaGenerator, MigrationInfo, MigrateOptions, MigrationResult, MigrationRow, EntityKey, EntityValue, EntityDataValue, FilterKey, EntityType, FromEntityType, Selected, IsSubset, EntityProps, ExpandProperty, ExpandScalar, FilterItemValue, ExpandQuery, Scalar, ExpandHint, FilterValue, MergeLoaded, MergeSelected, TypeConfig, AnyString, ClearDatabaseOptions, CreateSchemaOptions, EnsureDatabaseOptions, UpdateSchemaOptions, DropSchemaOptions, RefreshDatabaseOptions, AutoPath, UnboxArray, MetadataProcessor, ImportsResolver, RequiredNullable, DefineConfig, Opt, Hidden, EntitySchemaWithMeta, InferEntity, CheckConstraint, TriggerDef, PolicyDef, PolicyCallback, SessionContext, RoutineReturns, RoutineBodyCallback, RoutineJsBody, RoutineIgnoreField, RoutineParamConfig, RoutineConfig, RoutineRuntimeType, RoutineArgs, RoutineReturn, GeneratedColumnCallback, FilterDef, EntityCtor, Subquery, PopulateHintOptions, Prefixes, } from './typings.js';
|
|
7
7
|
export * from './enums.js';
|
|
8
8
|
export * from './errors.js';
|
|
9
9
|
export * from './exceptions.js';
|
|
@@ -117,6 +117,7 @@ export declare class MetadataDiscovery {
|
|
|
117
117
|
private createSchemaTable;
|
|
118
118
|
private initCheckConstraints;
|
|
119
119
|
private initTriggers;
|
|
120
|
+
private initPolicies;
|
|
120
121
|
private initGeneratedColumn;
|
|
121
122
|
private getDefaultVersionValue;
|
|
122
123
|
private inferDefaultValue;
|
|
@@ -165,6 +165,9 @@ export class MetadataDiscovery {
|
|
|
165
165
|
filtered.forEach(meta => this.initAutoincrement(meta)); // once again after we init custom types
|
|
166
166
|
filtered.forEach(meta => this.initCheckConstraints(meta));
|
|
167
167
|
filtered.forEach(meta => this.initTriggers(meta));
|
|
168
|
+
// filter names are load-bearing for RLS (policy and session variable names), backfill from the dictionary key
|
|
169
|
+
filtered.forEach(meta => Object.entries(meta.filters).forEach(([key, filter]) => (filter.name ??= key)));
|
|
170
|
+
filtered.forEach(meta => this.initPolicies(meta));
|
|
168
171
|
forEachProp((_m, p) => {
|
|
169
172
|
this.initDefaultValue(p);
|
|
170
173
|
this.inferTypeFromDefault(p);
|
|
@@ -1076,6 +1079,12 @@ export class MetadataDiscovery {
|
|
|
1076
1079
|
meta.checks = Utils.unique([...base.checks, ...meta.checks], Utils.equals);
|
|
1077
1080
|
meta.triggers = Utils.unique([...base.triggers, ...meta.triggers], Utils.equals);
|
|
1078
1081
|
}
|
|
1082
|
+
// Policies pass down only from inlined abstract bases; STI children share the root table
|
|
1083
|
+
// and TPT children own their tables, so both declare policies directly on the root/child.
|
|
1084
|
+
if (base.abstract && base.inheritanceType !== 'sti' && (meta.inheritanceType !== 'tpt' || !meta.tptParent)) {
|
|
1085
|
+
meta.policies = Utils.unique([...base.policies, ...meta.policies]);
|
|
1086
|
+
meta.rowLevelSecurity ??= base.rowLevelSecurity;
|
|
1087
|
+
}
|
|
1079
1088
|
const pks = Object.values(meta.properties)
|
|
1080
1089
|
.filter(p => p.primary)
|
|
1081
1090
|
.map(p => p.name);
|
|
@@ -1743,6 +1752,28 @@ export class MetadataDiscovery {
|
|
|
1743
1752
|
}
|
|
1744
1753
|
meta.hasTriggers = true;
|
|
1745
1754
|
}
|
|
1755
|
+
initPolicies(meta) {
|
|
1756
|
+
if (meta.policies.length === 0) {
|
|
1757
|
+
return;
|
|
1758
|
+
}
|
|
1759
|
+
const columns = meta.createSchemaColumnMappingObject();
|
|
1760
|
+
const table = this.createSchemaTable(meta);
|
|
1761
|
+
// resolve callbacks into a copy — the defs can be shared with siblings via an inlined abstract base,
|
|
1762
|
+
// and resolving in place would bake the first child's table into every other child's policy
|
|
1763
|
+
meta.policies = meta.policies.map(policy => {
|
|
1764
|
+
if (!(policy.using instanceof Function) && !(policy.check instanceof Function)) {
|
|
1765
|
+
return policy;
|
|
1766
|
+
}
|
|
1767
|
+
const resolved = { ...policy };
|
|
1768
|
+
if (resolved.using instanceof Function) {
|
|
1769
|
+
resolved.using = resolved.using(columns, table);
|
|
1770
|
+
}
|
|
1771
|
+
if (resolved.check instanceof Function) {
|
|
1772
|
+
resolved.check = resolved.check(columns, table);
|
|
1773
|
+
}
|
|
1774
|
+
return resolved;
|
|
1775
|
+
});
|
|
1776
|
+
}
|
|
1746
1777
|
initGeneratedColumn(meta, prop) {
|
|
1747
1778
|
if (!prop.generated && prop.columnTypes) {
|
|
1748
1779
|
const match = /(.*) generated always as (.*)/i.exec(prop.columnTypes[0]);
|
package/metadata/types.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AnyEntity, Constructor, EntityName, AnyString, CheckCallback, GeneratedColumnCallback, FormulaCallback, FilterQuery, Dictionary, AutoPath, EntityClass, IndexCallback, ObjectQuery, Raw, SchemaColumns, TriggerDef } from '../typings.js';
|
|
1
|
+
import type { AnyEntity, Constructor, EntityName, AnyString, CheckCallback, GeneratedColumnCallback, FormulaCallback, FilterQuery, Dictionary, AutoPath, EntityClass, IndexCallback, ObjectQuery, Raw, SchemaColumns, TriggerDef, PolicyDef } from '../typings.js';
|
|
2
2
|
import type { Cascade, LoadStrategy, DeferMode, QueryOrderMap, EmbeddedPrefixMode } from '../enums.js';
|
|
3
3
|
import type { Type, types } from '../types/index.js';
|
|
4
4
|
import type { EntityManager } from '../EntityManager.js';
|
|
@@ -104,6 +104,10 @@ export type EntityOptions<T, E = T extends EntityClass<infer P> ? P : T> = {
|
|
|
104
104
|
hasTriggers?: boolean;
|
|
105
105
|
/** Database triggers to create for this entity's table. (SQL drivers only) */
|
|
106
106
|
triggers?: TriggerDef<E>[];
|
|
107
|
+
/** PostgreSQL row level security policies for this entity's table. Declaring policies implicitly enables RLS. */
|
|
108
|
+
policies?: PolicyDef<E>[];
|
|
109
|
+
/** Enables PostgreSQL row level security on this entity's table. `'force'` also enforces it for the table owner. Set to `false` to keep declared policies staged while leaving RLS disabled. */
|
|
110
|
+
rowLevelSecurity?: boolean | 'force';
|
|
107
111
|
/**
|
|
108
112
|
* PostgreSQL partitioning definition for this table.
|
|
109
113
|
*
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mikro-orm/core",
|
|
3
|
-
"version": "7.2.0-dev.
|
|
3
|
+
"version": "7.2.0-dev.15",
|
|
4
4
|
"description": "TypeScript ORM for Node.js based on Data Mapper, Unit of Work and Identity Map patterns. Supports MongoDB, MySQL, PostgreSQL and SQLite databases as well as usage with vanilla JavaScript.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"data-mapper",
|
package/platforms/Platform.d.ts
CHANGED
|
@@ -316,6 +316,15 @@ export declare abstract class Platform {
|
|
|
316
316
|
supportsDownMigrations(): boolean;
|
|
317
317
|
/** Whether the platform supports deferred unique constraints. */
|
|
318
318
|
supportsDeferredUniqueConstraints(): boolean;
|
|
319
|
+
/** Whether the platform supports row level security (PostgreSQL). */
|
|
320
|
+
supportsRowLevelSecurity(): boolean;
|
|
321
|
+
/** Whether the driver can apply the session context on every pooled connection acquire (`sessionContext: 'connection'`). */
|
|
322
|
+
supportsConnectionSessionContext(): boolean;
|
|
323
|
+
/**
|
|
324
|
+
* SQL cast suffix (e.g. `'::uuid'`, or `''` when none is needed) applied when an RLS filter reads a session
|
|
325
|
+
* variable via `current_setting()` as the given column type, or `null` if the type has no automatic cast.
|
|
326
|
+
*/
|
|
327
|
+
getCurrentSettingCast(mappedType: Type<unknown>): string | null;
|
|
319
328
|
/** Platform-specific validation of entity metadata. */
|
|
320
329
|
validateMetadata(meta: EntityMetadata): void;
|
|
321
330
|
/**
|
package/platforms/Platform.js
CHANGED
|
@@ -722,11 +722,51 @@ export class Platform {
|
|
|
722
722
|
supportsDeferredUniqueConstraints() {
|
|
723
723
|
return true;
|
|
724
724
|
}
|
|
725
|
+
/** Whether the platform supports row level security (PostgreSQL). */
|
|
726
|
+
supportsRowLevelSecurity() {
|
|
727
|
+
return false;
|
|
728
|
+
}
|
|
729
|
+
/** Whether the driver can apply the session context on every pooled connection acquire (`sessionContext: 'connection'`). */
|
|
730
|
+
supportsConnectionSessionContext() {
|
|
731
|
+
return false;
|
|
732
|
+
}
|
|
733
|
+
/**
|
|
734
|
+
* SQL cast suffix (e.g. `'::uuid'`, or `''` when none is needed) applied when an RLS filter reads a session
|
|
735
|
+
* variable via `current_setting()` as the given column type, or `null` if the type has no automatic cast.
|
|
736
|
+
*/
|
|
737
|
+
getCurrentSettingCast(mappedType) {
|
|
738
|
+
return null;
|
|
739
|
+
}
|
|
725
740
|
/** Platform-specific validation of entity metadata. */
|
|
726
741
|
validateMetadata(meta) {
|
|
727
742
|
if (meta.partitionBy && !this.supportsPartitionedTables()) {
|
|
728
743
|
throw new MetadataError(`Entity ${meta.className} uses partitionBy, but ${this.constructor.name} does not support partitioned tables`);
|
|
729
744
|
}
|
|
745
|
+
const declaresRls = meta.policies.length > 0 || !!meta.rowLevelSecurity;
|
|
746
|
+
if (declaresRls && !this.supportsRowLevelSecurity()) {
|
|
747
|
+
throw MetadataError.rowLevelSecurityNotSupportedByDriver(meta);
|
|
748
|
+
}
|
|
749
|
+
// STI hierarchies share a single table, so only the root may declare policies; `root` is optional-chained
|
|
750
|
+
// as `validateMetadata` is public API and tolerates partially populated metadata
|
|
751
|
+
if (declaresRls && meta.root?.inheritanceType === 'sti' && meta.root !== meta) {
|
|
752
|
+
throw MetadataError.rowLevelSecurityOnNonRootStiEntity(meta);
|
|
753
|
+
}
|
|
754
|
+
if (meta.root?.inheritanceType === 'sti' && meta.root !== meta) {
|
|
755
|
+
for (const filter of Object.values(meta.filters)) {
|
|
756
|
+
// inherited root filters share the def object; only defs declared on the child itself are a problem,
|
|
757
|
+
// as non-root STI metas never reach the schema generator and the policy would silently not exist
|
|
758
|
+
if (filter.rls && meta.root.filters[filter.name] !== filter) {
|
|
759
|
+
throw MetadataError.rlsFilterOnNonRootStiEntity(meta, filter.name);
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
if (!this.supportsRowLevelSecurity()) {
|
|
764
|
+
for (const filter of Object.values(meta.filters)) {
|
|
765
|
+
if (filter.rls) {
|
|
766
|
+
throw MetadataError.rlsFilterNotSupportedByDriver(meta, filter.name);
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
}
|
|
730
770
|
}
|
|
731
771
|
/**
|
|
732
772
|
* Generates a custom order by statement given a set of in order values, eg.
|
package/typings.d.ts
CHANGED
|
@@ -710,6 +710,8 @@ export type IndexCallback<T> = (columns: Record<PropertyName<T>, string>, table:
|
|
|
710
710
|
export type FormulaCallback<T> = (columns: FormulaColumns<T>, table: FormulaTable) => string | Raw;
|
|
711
711
|
/** Callback for CHECK constraint expressions. Receives column mappings and table info. */
|
|
712
712
|
export type CheckCallback<T> = (columns: SchemaColumns<T>, table: SchemaTable) => string | Raw;
|
|
713
|
+
/** Callback for row level security policy expressions. Receives column mappings and table info. */
|
|
714
|
+
export type PolicyCallback<T> = (columns: SchemaColumns<T>, table: SchemaTable) => string | Raw;
|
|
713
715
|
/** Callback for trigger body expressions. Receives column mappings and table info. */
|
|
714
716
|
export type TriggerCallback<T> = (columns: Record<PropertyName<T>, string>, table: SchemaTable) => string | Raw;
|
|
715
717
|
/**
|
|
@@ -724,6 +726,28 @@ export interface CheckConstraint<T = any> {
|
|
|
724
726
|
property?: string;
|
|
725
727
|
expression: string | Raw | CheckCallback<T>;
|
|
726
728
|
}
|
|
729
|
+
/** Definition of a PostgreSQL row level security policy on a table. */
|
|
730
|
+
export interface PolicyDef<T = any> {
|
|
731
|
+
/** Policy name. Auto-generated if omitted. */
|
|
732
|
+
name?: string;
|
|
733
|
+
/** DML command the policy applies to. Defaults to `'all'`. */
|
|
734
|
+
command?: 'select' | 'insert' | 'update' | 'delete' | 'all';
|
|
735
|
+
/** Whether the policy is permissive (OR-combined) or restrictive (AND-combined). Defaults to `'permissive'`. */
|
|
736
|
+
type?: 'permissive' | 'restrictive';
|
|
737
|
+
/** Database roles the policy applies to. Defaults to `PUBLIC`. */
|
|
738
|
+
roles?: string[];
|
|
739
|
+
/** `USING` expression filtering visible rows. Can be a string, Raw query, or callback receiving column name mappings. */
|
|
740
|
+
using?: string | Raw | PolicyCallback<T>;
|
|
741
|
+
/** `WITH CHECK` expression validating written rows. Can be a string, Raw query, or callback receiving column name mappings. */
|
|
742
|
+
check?: string | Raw | PolicyCallback<T>;
|
|
743
|
+
}
|
|
744
|
+
/** Per-context database session state applied for row level security (session variables and role). */
|
|
745
|
+
export interface SessionContext {
|
|
746
|
+
/** Session variables set via `set_config`, typically referenced by RLS policies through `current_setting()`. `Date` values are serialized to ISO 8601. */
|
|
747
|
+
variables?: Dictionary<string | number | boolean | Date>;
|
|
748
|
+
/** Database role to switch to for the duration of the context (`set local role` / `set role`). */
|
|
749
|
+
role?: string;
|
|
750
|
+
}
|
|
727
751
|
/** Definition of a database trigger on a table. */
|
|
728
752
|
export interface TriggerDef<T = any> {
|
|
729
753
|
/** Trigger name. Auto-generated if omitted. */
|
|
@@ -1171,6 +1195,9 @@ export interface EntityMetadata<Entity = any, Class extends EntityCtor<Entity> =
|
|
|
1171
1195
|
}[];
|
|
1172
1196
|
checks: CheckConstraint<Entity>[];
|
|
1173
1197
|
triggers: TriggerDef<Entity>[];
|
|
1198
|
+
policies: PolicyDef<Entity>[];
|
|
1199
|
+
/** Enables row level security on the table. `'force'` also enables it for the table owner. Implied by non-empty `policies`, unless set to `false`, which keeps the policies staged but RLS disabled. */
|
|
1200
|
+
rowLevelSecurity?: boolean | 'force';
|
|
1174
1201
|
repositoryClass?: string;
|
|
1175
1202
|
repository: () => EntityClass<EntityRepository<any>>;
|
|
1176
1203
|
hooks: {
|
|
@@ -1498,6 +1525,15 @@ type FilterDefResolved<T extends object = any> = {
|
|
|
1498
1525
|
entity?: EntityName<T> | EntityName<T>[];
|
|
1499
1526
|
args?: boolean;
|
|
1500
1527
|
strict?: boolean;
|
|
1528
|
+
/**
|
|
1529
|
+
* Also materializes this filter as a PostgreSQL row level security policy on the entity's table, and stages the
|
|
1530
|
+
* matching session variables when its params are enabled via `em.setFilterParams()`. The `cond` must be compilable
|
|
1531
|
+
* to a static expression (no access to `em`/`type`/`options`, not async). Each referenced argument maps to a session
|
|
1532
|
+
* variable named `mikro.<filterName>.<argName>`; pass `{ setting }` to override that name for a single-argument filter.
|
|
1533
|
+
*/
|
|
1534
|
+
rls?: boolean | {
|
|
1535
|
+
setting?: string;
|
|
1536
|
+
};
|
|
1501
1537
|
};
|
|
1502
1538
|
/** Definition of a query filter that can be registered globally or per-entity via `@Filter()`. */
|
|
1503
1539
|
export type FilterDef<T extends EntityName | readonly EntityName[] = any> = FilterDefResolved<EntityFromInput<T>> & {
|
package/typings.js
CHANGED
|
@@ -472,6 +472,7 @@ export class UnitOfWork {
|
|
|
472
472
|
const loggerContext = Utils.merge({ id: this.#em._id }, this.#em.getLoggerContext({ disableContextResolution: true }));
|
|
473
473
|
await this.#em.getConnection('write').transactional(trx => this.persistToDatabase(groups, trx), {
|
|
474
474
|
ctx: oldTx,
|
|
475
|
+
sessionContext: this.#em.getTransactionSessionContext(),
|
|
475
476
|
eventBroadcaster: new TransactionEventBroadcaster(this.#em),
|
|
476
477
|
loggerContext,
|
|
477
478
|
});
|
package/utils/Configuration.d.ts
CHANGED
|
@@ -439,7 +439,7 @@ export interface Options<Driver extends IDatabaseDriver = IDatabaseDriver, EM ex
|
|
|
439
439
|
*/
|
|
440
440
|
filters: Dictionary<{
|
|
441
441
|
name?: string;
|
|
442
|
-
} & Omit<FilterDef, 'name'>>;
|
|
442
|
+
} & Omit<FilterDef, 'name' | 'rls'>>;
|
|
443
443
|
/**
|
|
444
444
|
* Metadata discovery configuration options.
|
|
445
445
|
* Controls how entities are discovered and validated.
|
|
@@ -480,6 +480,12 @@ export interface Options<Driver extends IDatabaseDriver = IDatabaseDriver, EM ex
|
|
|
480
480
|
* @default false
|
|
481
481
|
*/
|
|
482
482
|
disableTransactions?: boolean;
|
|
483
|
+
/**
|
|
484
|
+
* How `em.setSessionContext()` session variables/role are applied for row level security.
|
|
485
|
+
* `'transaction'` (default) emits `set_config(..., true)` inside each transaction; `'connection'` applies them on every pooled connection acquire (PostgreSQL only).
|
|
486
|
+
* @default 'transaction'
|
|
487
|
+
*/
|
|
488
|
+
sessionContext?: 'transaction' | 'connection';
|
|
483
489
|
/**
|
|
484
490
|
* Enable verbose logging of internal operations.
|
|
485
491
|
* @default false
|
|
@@ -840,6 +846,14 @@ export interface Options<Driver extends IDatabaseDriver = IDatabaseDriver, EM ex
|
|
|
840
846
|
* @default false
|
|
841
847
|
*/
|
|
842
848
|
ignoreRoutines?: boolean;
|
|
849
|
+
/**
|
|
850
|
+
* Leave row level security policies unmanaged. Declared policies are still created and RLS is still enabled or
|
|
851
|
+
* forced based on the entity metadata, but existing policies are never dropped or altered and RLS is never
|
|
852
|
+
* disabled or unforced — use this to protect hand-written policies from being removed when they are not
|
|
853
|
+
* mirrored in the entity definitions.
|
|
854
|
+
* @default false
|
|
855
|
+
*/
|
|
856
|
+
ignorePolicies?: boolean;
|
|
843
857
|
/**
|
|
844
858
|
* Table names or patterns to skip during schema generation.
|
|
845
859
|
* @default []
|
package/utils/Configuration.js
CHANGED
|
@@ -7,7 +7,7 @@ import { Utils } from '../utils/Utils.js';
|
|
|
7
7
|
import { Routine } from '../metadata/Routine.js';
|
|
8
8
|
import { MetadataValidator } from '../metadata/MetadataValidator.js';
|
|
9
9
|
import { MetadataProvider } from '../metadata/MetadataProvider.js';
|
|
10
|
-
import { NotFoundError } from '../errors.js';
|
|
10
|
+
import { MetadataError, NotFoundError, ValidationError } from '../errors.js';
|
|
11
11
|
import { RequestContext } from './RequestContext.js';
|
|
12
12
|
import { DataloaderType, FlushMode, LoadStrategy, PopulateHint } from '../enums.js';
|
|
13
13
|
import { MemoryCacheAdapter } from '../cache/MemoryCacheAdapter.js';
|
|
@@ -71,6 +71,7 @@ const DEFAULTS = {
|
|
|
71
71
|
ensureDatabase: true,
|
|
72
72
|
ensureIndexes: false,
|
|
73
73
|
batchSize: 300,
|
|
74
|
+
sessionContext: 'transaction',
|
|
74
75
|
debug: false,
|
|
75
76
|
ignoreDeprecations: false,
|
|
76
77
|
verbose: false,
|
|
@@ -94,6 +95,7 @@ const DEFAULTS = {
|
|
|
94
95
|
ignoreSchema: [],
|
|
95
96
|
ignoreTriggers: false,
|
|
96
97
|
ignoreRoutines: false,
|
|
98
|
+
ignorePolicies: false,
|
|
97
99
|
skipTables: [],
|
|
98
100
|
skipViews: [],
|
|
99
101
|
skipColumns: {},
|
|
@@ -393,7 +395,15 @@ export class Configuration {
|
|
|
393
395
|
}
|
|
394
396
|
this.#options.schema ??= this.#platform.getDefaultSchemaName();
|
|
395
397
|
this.#options.charset ??= this.#platform.getDefaultCharset();
|
|
398
|
+
// fail closed instead of silently applying no session state on drivers without the reserve hook (e.g. pglite)
|
|
399
|
+
if (this.#options.sessionContext === 'connection' && !this.#platform.supportsConnectionSessionContext()) {
|
|
400
|
+
throw ValidationError.connectionSessionContextNotSupported();
|
|
401
|
+
}
|
|
396
402
|
Object.keys(this.#options.filters).forEach(key => {
|
|
403
|
+
// global filters have no entity to attach a policy to, so `rls` is only valid on entity-scoped filters
|
|
404
|
+
if (this.#options.filters[key].rls) {
|
|
405
|
+
throw MetadataError.rlsFilterMustBeEntityScoped(key);
|
|
406
|
+
}
|
|
397
407
|
this.#options.filters[key].default ??= true;
|
|
398
408
|
});
|
|
399
409
|
if (!this.#options.filtersOnRelations) {
|
package/utils/QueryHelper.d.ts
CHANGED
|
@@ -37,6 +37,18 @@ export declare class QueryHelper {
|
|
|
37
37
|
static inlinePrimaryKeyObjects<T extends object>(where: Dictionary, meta: EntityMetadata<T>, metadata: MetadataStorage, key?: string): boolean;
|
|
38
38
|
static processWhere<T extends object>(options: ProcessWhereOptions<T>): FilterQuery<T>;
|
|
39
39
|
static getActiveFilters<T>(meta: EntityMetadata<T>, options: FilterOptions | undefined, filters: Dictionary<FilterDef>): FilterDef[];
|
|
40
|
+
/** @internal Sentinel wrapping for arguments accessed while statically resolving an `rls` filter condition. */
|
|
41
|
+
static readonly RLS_SENTINEL_PREFIX = "__mikro_rls_arg__";
|
|
42
|
+
/** @internal */
|
|
43
|
+
static readonly RLS_SENTINEL_SUFFIX = "__";
|
|
44
|
+
/**
|
|
45
|
+
* Resolves an `rls` filter's condition to a static `FilterQuery`. Function conditions are called with a proxy `args`
|
|
46
|
+
* that yields a unique sentinel per accessed argument, real `type`/`entityName` strings (validated to not affect the
|
|
47
|
+
* result), and a poison proxy or `undefined` for the remaining runtime-only parameters.
|
|
48
|
+
*
|
|
49
|
+
* @internal
|
|
50
|
+
*/
|
|
51
|
+
static resolveRlsFilterCond(filter: FilterDef, accessed: Set<string>, entityName?: string): Dictionary;
|
|
40
52
|
static mergePropertyFilters(propFilters: FilterOptions | undefined, options: FilterOptions | undefined): FilterOptions | undefined;
|
|
41
53
|
static isFilterActive<T>(meta: EntityMetadata<T>, filterName: string, filter: FilterDef, options: Dictionary<boolean | Dictionary>): boolean;
|
|
42
54
|
static processCustomType<T extends object>(prop: EntityProperty<T>, cond: FilterQuery<T>, platform: Platform, key?: string, fromQuery?: boolean): FilterQuery<T>;
|
package/utils/QueryHelper.js
CHANGED
|
@@ -4,6 +4,7 @@ import { ARRAY_OPERATORS, GroupOperator, JSON_KEY_OPERATORS, ReferenceKind } fro
|
|
|
4
4
|
import { JsonType } from '../types/JsonType.js';
|
|
5
5
|
import { helper } from '../entity/wrap.js';
|
|
6
6
|
import { isRaw, Raw } from './RawQueryFragment.js';
|
|
7
|
+
import { MetadataError } from '../errors.js';
|
|
7
8
|
/** @internal */
|
|
8
9
|
export class QueryHelper {
|
|
9
10
|
static SUPPORTED_OPERATORS = ['>', '<', '<=', '>=', '!', '!='];
|
|
@@ -286,6 +287,68 @@ export class QueryHelper {
|
|
|
286
287
|
return filters[f];
|
|
287
288
|
});
|
|
288
289
|
}
|
|
290
|
+
/** @internal Sentinel wrapping for arguments accessed while statically resolving an `rls` filter condition. */
|
|
291
|
+
static RLS_SENTINEL_PREFIX = '__mikro_rls_arg__';
|
|
292
|
+
/** @internal */
|
|
293
|
+
static RLS_SENTINEL_SUFFIX = '__';
|
|
294
|
+
/**
|
|
295
|
+
* Resolves an `rls` filter's condition to a static `FilterQuery`. Function conditions are called with a proxy `args`
|
|
296
|
+
* that yields a unique sentinel per accessed argument, real `type`/`entityName` strings (validated to not affect the
|
|
297
|
+
* result), and a poison proxy or `undefined` for the remaining runtime-only parameters.
|
|
298
|
+
*
|
|
299
|
+
* @internal
|
|
300
|
+
*/
|
|
301
|
+
static resolveRlsFilterCond(filter, accessed, entityName) {
|
|
302
|
+
if (!(filter.cond instanceof Function)) {
|
|
303
|
+
return filter.cond;
|
|
304
|
+
}
|
|
305
|
+
const args = new Proxy({}, {
|
|
306
|
+
get: (_target, prop) => {
|
|
307
|
+
if (typeof prop === 'symbol') {
|
|
308
|
+
// e.g. coercing `args` itself in a template literal triggers a `Symbol.toPrimitive` lookup
|
|
309
|
+
throw MetadataError.rlsFilterUnsupportedCond(filter.name);
|
|
310
|
+
}
|
|
311
|
+
accessed.add(prop);
|
|
312
|
+
return `${this.RLS_SENTINEL_PREFIX}${prop}${this.RLS_SENTINEL_SUFFIX}`;
|
|
313
|
+
},
|
|
314
|
+
});
|
|
315
|
+
const poison = new Proxy({}, {
|
|
316
|
+
get: () => {
|
|
317
|
+
throw MetadataError.rlsFilterDependsOnRuntimeState(filter.name);
|
|
318
|
+
},
|
|
319
|
+
});
|
|
320
|
+
// property access on the poison proxies throws, but equality/truthiness checks (`type === 'read'`,
|
|
321
|
+
// `entityName === 'X'`, `options ? a : b`) cannot be trapped — vary all three across the evaluations and require
|
|
322
|
+
// identical results, so a command-, entity-, or options-dependent condition cannot silently compile one branch.
|
|
323
|
+
// `entityName` uses the real class name (plus two derived-distinct variants) so an `=== '<name>'` check diverges;
|
|
324
|
+
// `options` alternates the poison proxy and `undefined` so a truthiness check flips. `em` stays poison throughout.
|
|
325
|
+
const name = entityName ?? `${this.RLS_SENTINEL_PREFIX}entity${this.RLS_SENTINEL_SUFFIX}`;
|
|
326
|
+
const evaluate = (type, entity, options) => {
|
|
327
|
+
let result;
|
|
328
|
+
try {
|
|
329
|
+
result = filter.cond(args, type, poison, options, entity);
|
|
330
|
+
}
|
|
331
|
+
catch (e) {
|
|
332
|
+
// a raw TypeError from touching the `undefined` options/em must fail closed like the poison proxy does,
|
|
333
|
+
// but the descriptive MetadataErrors thrown above are already correct — let them surface unchanged
|
|
334
|
+
if (e instanceof MetadataError) {
|
|
335
|
+
throw e;
|
|
336
|
+
}
|
|
337
|
+
throw MetadataError.rlsFilterDependsOnRuntimeState(filter.name);
|
|
338
|
+
}
|
|
339
|
+
if (result instanceof Promise) {
|
|
340
|
+
throw MetadataError.rlsFilterDependsOnRuntimeState(filter.name);
|
|
341
|
+
}
|
|
342
|
+
return result;
|
|
343
|
+
};
|
|
344
|
+
const read = evaluate('read', name, poison);
|
|
345
|
+
const update = evaluate('update', `${name}\0a`, undefined);
|
|
346
|
+
const del = evaluate('delete', `${name}\0b`, poison);
|
|
347
|
+
if (JSON.stringify(read) !== JSON.stringify(update) || JSON.stringify(read) !== JSON.stringify(del)) {
|
|
348
|
+
throw MetadataError.rlsFilterDependsOnRuntimeState(filter.name);
|
|
349
|
+
}
|
|
350
|
+
return read;
|
|
351
|
+
}
|
|
289
352
|
static mergePropertyFilters(propFilters, options) {
|
|
290
353
|
if (!options || !propFilters || options === true || propFilters === true) {
|
|
291
354
|
return options ?? propFilters;
|
|
@@ -239,7 +239,7 @@ export class TransactionManager {
|
|
|
239
239
|
return TransactionContext.create(fork, () => fork.getConnection().transactional(async (trx) => {
|
|
240
240
|
fork.setTransactionContext(trx);
|
|
241
241
|
return this.executeTransactionFlow(fork, cb, propagateToUpperContext, em);
|
|
242
|
-
}, { ...options, eventBroadcaster }));
|
|
242
|
+
}, { sessionContext: fork.getTransactionSessionContext(), ...options, eventBroadcaster }));
|
|
243
243
|
}
|
|
244
244
|
/**
|
|
245
245
|
* Executes transaction workflow with entity synchronization.
|
package/utils/Utils.d.ts
CHANGED
|
@@ -33,6 +33,8 @@ export declare function parseJsonSafe<T = unknown>(value: unknown): T;
|
|
|
33
33
|
export declare class Utils {
|
|
34
34
|
#private;
|
|
35
35
|
static readonly PK_SEPARATOR = "~~~";
|
|
36
|
+
/** Default session variable name backing an RLS filter argument (`current_setting('mikro.<filter>.<arg>')`). */
|
|
37
|
+
static getRlsSettingName(filterName: string, argName: string): string;
|
|
36
38
|
/**
|
|
37
39
|
* Checks if the argument is instance of `Object`. Returns false for arrays.
|
|
38
40
|
*/
|
package/utils/Utils.js
CHANGED
|
@@ -153,7 +153,11 @@ export function parseJsonSafe(value) {
|
|
|
153
153
|
/** Collection of general-purpose utility methods used throughout the ORM. */
|
|
154
154
|
export class Utils {
|
|
155
155
|
static PK_SEPARATOR = '~~~';
|
|
156
|
-
static #ORM_VERSION = '7.2.0-dev.
|
|
156
|
+
static #ORM_VERSION = '7.2.0-dev.15';
|
|
157
|
+
/** Default session variable name backing an RLS filter argument (`current_setting('mikro.<filter>.<arg>')`). */
|
|
158
|
+
static getRlsSettingName(filterName, argName) {
|
|
159
|
+
return `mikro.${filterName}.${argName}`;
|
|
160
|
+
}
|
|
157
161
|
/**
|
|
158
162
|
* Checks if the argument is instance of `Object`. Returns false for arrays.
|
|
159
163
|
*/
|
package/utils/env-vars.js
CHANGED
|
@@ -88,6 +88,7 @@ export function loadEnvironmentVars() {
|
|
|
88
88
|
read3('createForeignKeyConstraints', bool);
|
|
89
89
|
read3('ignoreTriggers', bool);
|
|
90
90
|
read3('ignoreRoutines', bool);
|
|
91
|
+
read3('ignorePolicies', bool);
|
|
91
92
|
cleanup(ret, 'schemaGenerator');
|
|
92
93
|
ret.seeder = {};
|
|
93
94
|
const read4 = read.bind(null, ret.seeder, 'MIKRO_ORM_SEEDER_');
|
package/utils/index.d.ts
CHANGED