[permission] Override query builders db-executing methods (#11714)
closes https://github.com/twentyhq/core-team-issues/issues/843
This commit is contained in:
@ -69,6 +69,7 @@ export const validateQueryIsPermittedOrThrow = (
|
||||
);
|
||||
}
|
||||
break;
|
||||
case 'restore':
|
||||
case 'soft-delete':
|
||||
if (!permissionsForEntity?.canSoftDelete) {
|
||||
throw new PermissionsException(
|
||||
|
||||
@ -0,0 +1,84 @@
|
||||
import { ObjectRecordsPermissions } from 'twenty-shared/types';
|
||||
import {
|
||||
DeleteQueryBuilder,
|
||||
DeleteResult,
|
||||
InsertQueryBuilder,
|
||||
ObjectLiteral,
|
||||
} from 'typeorm';
|
||||
import { QueryDeepPartialEntity } from 'typeorm/query-builder/QueryPartialEntity';
|
||||
|
||||
import { WorkspaceInternalContext } from 'src/engine/twenty-orm/interfaces/workspace-internal-context.interface';
|
||||
|
||||
import { validateQueryIsPermittedOrThrow } from 'src/engine/twenty-orm/repository/permissions.util';
|
||||
import { WorkspaceSelectQueryBuilder } from 'src/engine/twenty-orm/repository/workspace-select-query-builder';
|
||||
import { WorkspaceSoftDeleteQueryBuilder } from 'src/engine/twenty-orm/repository/workspace-soft-delete-query-builder';
|
||||
import { WorkspaceUpdateQueryBuilder } from 'src/engine/twenty-orm/repository/workspace-update-query-builder';
|
||||
|
||||
export class WorkspaceDeleteQueryBuilder<
|
||||
T extends ObjectLiteral,
|
||||
> extends DeleteQueryBuilder<T> {
|
||||
private objectRecordsPermissions: ObjectRecordsPermissions;
|
||||
private shouldBypassPermissionChecks: boolean;
|
||||
private internalContext: WorkspaceInternalContext;
|
||||
constructor(
|
||||
queryBuilder: DeleteQueryBuilder<T>,
|
||||
objectRecordsPermissions: ObjectRecordsPermissions,
|
||||
internalContext: WorkspaceInternalContext,
|
||||
shouldBypassPermissionChecks: boolean,
|
||||
) {
|
||||
super(queryBuilder);
|
||||
this.objectRecordsPermissions = objectRecordsPermissions;
|
||||
this.internalContext = internalContext;
|
||||
this.shouldBypassPermissionChecks = shouldBypassPermissionChecks;
|
||||
}
|
||||
|
||||
override clone(): this {
|
||||
const clonedQueryBuilder = super.clone();
|
||||
|
||||
return new WorkspaceDeleteQueryBuilder(
|
||||
clonedQueryBuilder,
|
||||
this.objectRecordsPermissions,
|
||||
this.internalContext,
|
||||
this.shouldBypassPermissionChecks,
|
||||
) as this;
|
||||
}
|
||||
|
||||
override execute(): Promise<DeleteResult> {
|
||||
validateQueryIsPermittedOrThrow(
|
||||
this.expressionMap,
|
||||
this.objectRecordsPermissions,
|
||||
this.internalContext.objectMetadataMaps,
|
||||
this.shouldBypassPermissionChecks,
|
||||
);
|
||||
|
||||
return super.execute();
|
||||
}
|
||||
|
||||
override select(): WorkspaceSelectQueryBuilder<T> {
|
||||
throw new Error('This builder cannot morph into a select builder');
|
||||
}
|
||||
|
||||
override update(): WorkspaceUpdateQueryBuilder<T>;
|
||||
|
||||
override update(
|
||||
updateSet: QueryDeepPartialEntity<T>,
|
||||
): WorkspaceUpdateQueryBuilder<T>;
|
||||
|
||||
override update(
|
||||
_updateSet?: QueryDeepPartialEntity<T>,
|
||||
): WorkspaceUpdateQueryBuilder<T> {
|
||||
throw new Error('This builder cannot morph into an update builder');
|
||||
}
|
||||
|
||||
override insert(): InsertQueryBuilder<T> {
|
||||
throw new Error('This builder cannot morph into an insert builder');
|
||||
}
|
||||
|
||||
override softDelete(): WorkspaceSoftDeleteQueryBuilder<T> {
|
||||
throw new Error('This builder cannot morph into a soft delete builder');
|
||||
}
|
||||
|
||||
override restore(): WorkspaceSoftDeleteQueryBuilder<T> {
|
||||
throw new Error('This builder cannot morph into a soft delete builder');
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,72 @@
|
||||
import { ObjectRecordsPermissions } from 'twenty-shared/types';
|
||||
import { InsertQueryBuilder, ObjectLiteral } from 'typeorm';
|
||||
|
||||
import { WorkspaceInternalContext } from 'src/engine/twenty-orm/interfaces/workspace-internal-context.interface';
|
||||
|
||||
import { validateQueryIsPermittedOrThrow } from 'src/engine/twenty-orm/repository/permissions.util';
|
||||
import { WorkspaceDeleteQueryBuilder } from 'src/engine/twenty-orm/repository/workspace-delete-query-builder';
|
||||
import { WorkspaceSelectQueryBuilder } from 'src/engine/twenty-orm/repository/workspace-select-query-builder';
|
||||
import { WorkspaceSoftDeleteQueryBuilder } from 'src/engine/twenty-orm/repository/workspace-soft-delete-query-builder';
|
||||
import { WorkspaceUpdateQueryBuilder } from 'src/engine/twenty-orm/repository/workspace-update-query-builder';
|
||||
|
||||
export class WorkspaceInsertQueryBuilder<
|
||||
T extends ObjectLiteral,
|
||||
> extends InsertQueryBuilder<T> {
|
||||
private objectRecordsPermissions: ObjectRecordsPermissions;
|
||||
private shouldBypassPermissionChecks: boolean;
|
||||
private internalContext: WorkspaceInternalContext;
|
||||
|
||||
constructor(
|
||||
queryBuilder: InsertQueryBuilder<T>,
|
||||
objectRecordsPermissions: ObjectRecordsPermissions,
|
||||
internalContext: WorkspaceInternalContext,
|
||||
shouldBypassPermissionChecks: boolean,
|
||||
) {
|
||||
super(queryBuilder);
|
||||
this.objectRecordsPermissions = objectRecordsPermissions;
|
||||
this.internalContext = internalContext;
|
||||
this.shouldBypassPermissionChecks = shouldBypassPermissionChecks;
|
||||
}
|
||||
|
||||
override clone(): this {
|
||||
const clonedQueryBuilder = super.clone();
|
||||
|
||||
return new WorkspaceInsertQueryBuilder(
|
||||
clonedQueryBuilder,
|
||||
this.objectRecordsPermissions,
|
||||
this.internalContext,
|
||||
this.shouldBypassPermissionChecks,
|
||||
) as this;
|
||||
}
|
||||
|
||||
override execute(): Promise<any> {
|
||||
validateQueryIsPermittedOrThrow(
|
||||
this.expressionMap,
|
||||
this.objectRecordsPermissions,
|
||||
this.internalContext.objectMetadataMaps,
|
||||
this.shouldBypassPermissionChecks,
|
||||
);
|
||||
|
||||
return super.execute();
|
||||
}
|
||||
|
||||
override select(): WorkspaceSelectQueryBuilder<T> {
|
||||
throw new Error('This builder cannot morph into a select builder');
|
||||
}
|
||||
|
||||
override update(): WorkspaceUpdateQueryBuilder<T> {
|
||||
throw new Error('This builder cannot morph into an update builder');
|
||||
}
|
||||
|
||||
override delete(): WorkspaceDeleteQueryBuilder<T> {
|
||||
throw new Error('This builder cannot morph into a delete builder');
|
||||
}
|
||||
|
||||
override softDelete(): WorkspaceSoftDeleteQueryBuilder<T> {
|
||||
throw new Error('This builder cannot morph into a soft delete builder');
|
||||
}
|
||||
|
||||
override restore(): WorkspaceSoftDeleteQueryBuilder<T> {
|
||||
throw new Error('This builder cannot morph into a soft delete builder');
|
||||
}
|
||||
}
|
||||
@ -1,35 +0,0 @@
|
||||
import { ObjectRecordsPermissions } from 'twenty-shared/types';
|
||||
import { ObjectLiteral, SelectQueryBuilder } from 'typeorm';
|
||||
|
||||
import { WorkspaceInternalContext } from 'src/engine/twenty-orm/interfaces/workspace-internal-context.interface';
|
||||
|
||||
import { WorkspaceSelectQueryBuilder } from 'src/engine/twenty-orm/repository/workspace-select-query-builder';
|
||||
|
||||
export class WorkspaceQueryBuilder<
|
||||
T extends ObjectLiteral,
|
||||
> extends WorkspaceSelectQueryBuilder<T> {
|
||||
constructor(
|
||||
queryBuilder: SelectQueryBuilder<T>,
|
||||
objectRecordsPermissions: ObjectRecordsPermissions,
|
||||
internalContext: WorkspaceInternalContext,
|
||||
shouldBypassPermissionChecks: boolean,
|
||||
) {
|
||||
super(
|
||||
queryBuilder,
|
||||
objectRecordsPermissions,
|
||||
internalContext,
|
||||
shouldBypassPermissionChecks,
|
||||
);
|
||||
}
|
||||
|
||||
override clone(): this {
|
||||
const clonedQueryBuilder = super.clone();
|
||||
|
||||
return new WorkspaceQueryBuilder(
|
||||
clonedQueryBuilder,
|
||||
this.objectRecordsPermissions,
|
||||
this.internalContext,
|
||||
this.shouldBypassPermissionChecks,
|
||||
) as this;
|
||||
}
|
||||
}
|
||||
@ -1,10 +1,12 @@
|
||||
import { ObjectRecordsPermissions } from 'twenty-shared/types';
|
||||
import { ObjectLiteral, SelectQueryBuilder, UpdateQueryBuilder } from 'typeorm';
|
||||
import { ObjectLiteral, SelectQueryBuilder } from 'typeorm';
|
||||
import { QueryDeepPartialEntity } from 'typeorm/query-builder/QueryPartialEntity';
|
||||
|
||||
import { WorkspaceInternalContext } from 'src/engine/twenty-orm/interfaces/workspace-internal-context.interface';
|
||||
|
||||
import { validateQueryIsPermittedOrThrow } from 'src/engine/twenty-orm/repository/permissions.util';
|
||||
import { WorkspaceDeleteQueryBuilder } from 'src/engine/twenty-orm/repository/workspace-delete-query-builder';
|
||||
import { WorkspaceSoftDeleteQueryBuilder } from 'src/engine/twenty-orm/repository/workspace-soft-delete-query-builder';
|
||||
import { WorkspaceUpdateQueryBuilder } from 'src/engine/twenty-orm/repository/workspace-update-query-builder';
|
||||
|
||||
export class WorkspaceSelectQueryBuilder<
|
||||
@ -25,6 +27,29 @@ export class WorkspaceSelectQueryBuilder<
|
||||
this.shouldBypassPermissionChecks = shouldBypassPermissionChecks;
|
||||
}
|
||||
|
||||
override clone(): this {
|
||||
const clonedQueryBuilder = super.clone();
|
||||
|
||||
return new WorkspaceSelectQueryBuilder(
|
||||
clonedQueryBuilder,
|
||||
this.objectRecordsPermissions,
|
||||
this.internalContext,
|
||||
this.shouldBypassPermissionChecks,
|
||||
) as this;
|
||||
}
|
||||
|
||||
override execute(): Promise<T[]> {
|
||||
this.validatePermissions();
|
||||
|
||||
return super.execute();
|
||||
}
|
||||
|
||||
override getMany(): Promise<T[]> {
|
||||
this.validatePermissions();
|
||||
|
||||
return super.getMany();
|
||||
}
|
||||
|
||||
override update(): WorkspaceUpdateQueryBuilder<T>;
|
||||
|
||||
override update(
|
||||
@ -33,7 +58,7 @@ export class WorkspaceSelectQueryBuilder<
|
||||
|
||||
override update(
|
||||
updateSet?: QueryDeepPartialEntity<T>,
|
||||
): UpdateQueryBuilder<T> {
|
||||
): WorkspaceUpdateQueryBuilder<T> {
|
||||
const updateQueryBuilder = updateSet
|
||||
? super.update(updateSet)
|
||||
: super.update();
|
||||
@ -46,25 +71,45 @@ export class WorkspaceSelectQueryBuilder<
|
||||
);
|
||||
}
|
||||
|
||||
override execute(): Promise<T[]> {
|
||||
validateQueryIsPermittedOrThrow(
|
||||
this.expressionMap,
|
||||
override delete(): WorkspaceDeleteQueryBuilder<T> {
|
||||
const deleteQueryBuilder = super.delete();
|
||||
|
||||
return new WorkspaceDeleteQueryBuilder<T>(
|
||||
deleteQueryBuilder,
|
||||
this.objectRecordsPermissions,
|
||||
this.internalContext.objectMetadataMaps,
|
||||
this.internalContext,
|
||||
this.shouldBypassPermissionChecks,
|
||||
);
|
||||
|
||||
return super.execute();
|
||||
}
|
||||
|
||||
override getMany(): Promise<T[]> {
|
||||
override softDelete(): WorkspaceSoftDeleteQueryBuilder<T> {
|
||||
const softDeleteQueryBuilder = super.softDelete();
|
||||
|
||||
return new WorkspaceSoftDeleteQueryBuilder<T>(
|
||||
softDeleteQueryBuilder,
|
||||
this.objectRecordsPermissions,
|
||||
this.internalContext,
|
||||
this.shouldBypassPermissionChecks,
|
||||
);
|
||||
}
|
||||
|
||||
override restore(): WorkspaceSoftDeleteQueryBuilder<T> {
|
||||
const restoreQueryBuilder = super.restore();
|
||||
|
||||
return new WorkspaceSoftDeleteQueryBuilder<T>(
|
||||
restoreQueryBuilder,
|
||||
this.objectRecordsPermissions,
|
||||
this.internalContext,
|
||||
this.shouldBypassPermissionChecks,
|
||||
);
|
||||
}
|
||||
|
||||
private validatePermissions(): void {
|
||||
validateQueryIsPermittedOrThrow(
|
||||
this.expressionMap,
|
||||
this.objectRecordsPermissions,
|
||||
this.internalContext.objectMetadataMaps,
|
||||
this.shouldBypassPermissionChecks,
|
||||
);
|
||||
|
||||
return super.getMany();
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,68 @@
|
||||
import { ObjectRecordsPermissions } from 'twenty-shared/types';
|
||||
import { InsertQueryBuilder, ObjectLiteral, UpdateResult } from 'typeorm';
|
||||
import { SoftDeleteQueryBuilder } from 'typeorm/query-builder/SoftDeleteQueryBuilder';
|
||||
|
||||
import { WorkspaceInternalContext } from 'src/engine/twenty-orm/interfaces/workspace-internal-context.interface';
|
||||
|
||||
import { validateQueryIsPermittedOrThrow } from 'src/engine/twenty-orm/repository/permissions.util';
|
||||
import { WorkspaceDeleteQueryBuilder } from 'src/engine/twenty-orm/repository/workspace-delete-query-builder';
|
||||
import { WorkspaceSelectQueryBuilder } from 'src/engine/twenty-orm/repository/workspace-select-query-builder';
|
||||
import { WorkspaceUpdateQueryBuilder } from 'src/engine/twenty-orm/repository/workspace-update-query-builder';
|
||||
|
||||
export class WorkspaceSoftDeleteQueryBuilder<
|
||||
T extends ObjectLiteral,
|
||||
> extends SoftDeleteQueryBuilder<T> {
|
||||
private objectRecordsPermissions: ObjectRecordsPermissions;
|
||||
private shouldBypassPermissionChecks: boolean;
|
||||
private internalContext: WorkspaceInternalContext;
|
||||
|
||||
constructor(
|
||||
queryBuilder: SoftDeleteQueryBuilder<T>,
|
||||
objectRecordsPermissions: ObjectRecordsPermissions,
|
||||
internalContext: WorkspaceInternalContext,
|
||||
shouldBypassPermissionChecks: boolean,
|
||||
) {
|
||||
super(queryBuilder);
|
||||
this.objectRecordsPermissions = objectRecordsPermissions;
|
||||
this.internalContext = internalContext;
|
||||
this.shouldBypassPermissionChecks = shouldBypassPermissionChecks;
|
||||
}
|
||||
|
||||
override clone(): this {
|
||||
const clonedQueryBuilder = super.clone();
|
||||
|
||||
return new WorkspaceSoftDeleteQueryBuilder(
|
||||
clonedQueryBuilder,
|
||||
this.objectRecordsPermissions,
|
||||
this.internalContext,
|
||||
this.shouldBypassPermissionChecks,
|
||||
) as this;
|
||||
}
|
||||
|
||||
override execute(): Promise<UpdateResult> {
|
||||
validateQueryIsPermittedOrThrow(
|
||||
this.expressionMap,
|
||||
this.objectRecordsPermissions,
|
||||
this.internalContext.objectMetadataMaps,
|
||||
this.shouldBypassPermissionChecks,
|
||||
);
|
||||
|
||||
return super.execute();
|
||||
}
|
||||
|
||||
override select(): WorkspaceSelectQueryBuilder<T> {
|
||||
throw new Error('This builder cannot morph into a select builder');
|
||||
}
|
||||
|
||||
override update(): WorkspaceUpdateQueryBuilder<T> {
|
||||
throw new Error('This builder cannot morph into an update builder');
|
||||
}
|
||||
|
||||
override insert(): InsertQueryBuilder<T> {
|
||||
throw new Error('This builder cannot morph into an insert builder');
|
||||
}
|
||||
|
||||
override delete(): WorkspaceDeleteQueryBuilder<T> {
|
||||
throw new Error('This builder cannot morph into a delete builder');
|
||||
}
|
||||
}
|
||||
@ -4,15 +4,18 @@ import { ObjectLiteral, UpdateQueryBuilder, UpdateResult } from 'typeorm';
|
||||
import { WorkspaceInternalContext } from 'src/engine/twenty-orm/interfaces/workspace-internal-context.interface';
|
||||
|
||||
import { validateQueryIsPermittedOrThrow } from 'src/engine/twenty-orm/repository/permissions.util';
|
||||
import { WorkspaceDeleteQueryBuilder } from 'src/engine/twenty-orm/repository/workspace-delete-query-builder';
|
||||
import { WorkspaceSelectQueryBuilder } from 'src/engine/twenty-orm/repository/workspace-select-query-builder';
|
||||
import { WorkspaceSoftDeleteQueryBuilder } from 'src/engine/twenty-orm/repository/workspace-soft-delete-query-builder';
|
||||
|
||||
export class WorkspaceUpdateQueryBuilder<
|
||||
Entity extends ObjectLiteral,
|
||||
> extends UpdateQueryBuilder<Entity> {
|
||||
T extends ObjectLiteral,
|
||||
> extends UpdateQueryBuilder<T> {
|
||||
private objectRecordsPermissions: ObjectRecordsPermissions;
|
||||
private shouldBypassPermissionChecks: boolean;
|
||||
private internalContext: WorkspaceInternalContext;
|
||||
constructor(
|
||||
queryBuilder: UpdateQueryBuilder<Entity>,
|
||||
queryBuilder: UpdateQueryBuilder<T>,
|
||||
objectRecordsPermissions: ObjectRecordsPermissions,
|
||||
internalContext: WorkspaceInternalContext,
|
||||
shouldBypassPermissionChecks: boolean,
|
||||
@ -23,6 +26,17 @@ export class WorkspaceUpdateQueryBuilder<
|
||||
this.shouldBypassPermissionChecks = shouldBypassPermissionChecks;
|
||||
}
|
||||
|
||||
override clone(): this {
|
||||
const clonedQueryBuilder = super.clone();
|
||||
|
||||
return new WorkspaceUpdateQueryBuilder(
|
||||
clonedQueryBuilder,
|
||||
this.objectRecordsPermissions,
|
||||
this.internalContext,
|
||||
this.shouldBypassPermissionChecks,
|
||||
) as this;
|
||||
}
|
||||
|
||||
override execute(): Promise<UpdateResult> {
|
||||
validateQueryIsPermittedOrThrow(
|
||||
this.expressionMap,
|
||||
@ -33,4 +47,20 @@ export class WorkspaceUpdateQueryBuilder<
|
||||
|
||||
return super.execute();
|
||||
}
|
||||
|
||||
override select(): WorkspaceSelectQueryBuilder<T> {
|
||||
throw new Error('This builder cannot morph into a select builder');
|
||||
}
|
||||
|
||||
override delete(): WorkspaceDeleteQueryBuilder<T> {
|
||||
throw new Error('This builder cannot morph into a delete builder');
|
||||
}
|
||||
|
||||
override softDelete(): WorkspaceSoftDeleteQueryBuilder<T> {
|
||||
throw new Error('This builder cannot morph into a soft delete builder');
|
||||
}
|
||||
|
||||
override restore(): WorkspaceSoftDeleteQueryBuilder<T> {
|
||||
throw new Error('This builder cannot morph into a soft delete builder');
|
||||
}
|
||||
}
|
||||
|
||||
@ -27,7 +27,7 @@ import { WorkspaceInternalContext } from 'src/engine/twenty-orm/interfaces/works
|
||||
import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum';
|
||||
import { ObjectMetadataItemWithFieldMaps } from 'src/engine/metadata-modules/types/object-metadata-item-with-field-maps';
|
||||
import { getObjectMetadataMapItemByNameSingular } from 'src/engine/metadata-modules/utils/get-object-metadata-map-item-by-name-singular.util';
|
||||
import { WorkspaceQueryBuilder } from 'src/engine/twenty-orm/repository/workspace-query-builder';
|
||||
import { WorkspaceSelectQueryBuilder } from 'src/engine/twenty-orm/repository/workspace-select-query-builder';
|
||||
import { WorkspaceEntitiesStorage } from 'src/engine/twenty-orm/storage/workspace-entities.storage';
|
||||
import { formatData } from 'src/engine/twenty-orm/utils/format-data.util';
|
||||
import { formatResult } from 'src/engine/twenty-orm/utils/format-result.util';
|
||||
@ -58,11 +58,11 @@ export class WorkspaceRepository<
|
||||
override createQueryBuilder<U extends T>(
|
||||
alias?: string,
|
||||
queryRunner?: QueryRunner,
|
||||
): WorkspaceQueryBuilder<U> {
|
||||
): WorkspaceSelectQueryBuilder<U> {
|
||||
const queryBuilder = super.createQueryBuilder(
|
||||
alias,
|
||||
queryRunner,
|
||||
) as unknown as WorkspaceQueryBuilder<U>;
|
||||
) as unknown as WorkspaceSelectQueryBuilder<U>;
|
||||
const isPermissionsV2Enabled =
|
||||
this.featureFlagMap[FeatureFlagKey.IsPermissionsV2Enabled];
|
||||
|
||||
@ -73,7 +73,7 @@ export class WorkspaceRepository<
|
||||
throw new Error('Object records permissions are required');
|
||||
}
|
||||
|
||||
return new WorkspaceQueryBuilder(
|
||||
return new WorkspaceSelectQueryBuilder(
|
||||
queryBuilder,
|
||||
this.objectRecordsPermissions,
|
||||
this.internalContext,
|
||||
|
||||
Reference in New Issue
Block a user