Permission checks on twentyORM global manager (#11477)

In this PR we are handling permissions when using
twentyORMGlobalManager,
and handling permissions for rest api and api key
This commit is contained in:
Marie
2025-04-23 17:57:48 +02:00
committed by GitHub
parent 28a1354928
commit 4257f30f12
54 changed files with 547 additions and 116 deletions

View File

@ -6,6 +6,7 @@ import {
PermissionsExceptionCode,
PermissionsExceptionMessage,
} from 'src/engine/metadata-modules/permissions/permissions.exception';
import { ObjectMetadataMaps } from 'src/engine/metadata-modules/types/object-metadata-maps';
const getTargetEntityAndOperationType = (expressionMap: QueryExpressionMap) => {
const mainEntity = expressionMap.aliases[0].metadata.name;
@ -20,10 +21,26 @@ const getTargetEntityAndOperationType = (expressionMap: QueryExpressionMap) => {
export const validateQueryIsPermittedOrThrow = (
expressionMap: QueryExpressionMap,
objectRecordsPermissions: ObjectRecordsPermissions,
objectMetadataMaps: ObjectMetadataMaps,
shouldBypassPermissionChecks: boolean,
) => {
if (shouldBypassPermissionChecks) {
return;
}
const { mainEntity, operationType } =
getTargetEntityAndOperationType(expressionMap);
const objectMetadataIdForEntity =
objectMetadataMaps.idByNameSingular[mainEntity];
const objectMetadataIsSystem =
objectMetadataMaps.byId[objectMetadataIdForEntity]?.isSystem === true;
if (objectMetadataIsSystem) {
return;
}
const permissionsForEntity = objectRecordsPermissions[mainEntity];
switch (operationType) {

View File

@ -1,6 +1,8 @@
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<
@ -9,9 +11,15 @@ export class WorkspaceQueryBuilder<
constructor(
queryBuilder: SelectQueryBuilder<T>,
objectRecordsPermissions: ObjectRecordsPermissions,
internalContext: WorkspaceInternalContext,
shouldBypassPermissionChecks: boolean,
) {
super(queryBuilder, objectRecordsPermissions);
this.objectRecordsPermissions = objectRecordsPermissions;
super(
queryBuilder,
objectRecordsPermissions,
internalContext,
shouldBypassPermissionChecks,
);
}
override clone(): this {
@ -20,6 +28,8 @@ export class WorkspaceQueryBuilder<
return new WorkspaceQueryBuilder(
clonedQueryBuilder,
this.objectRecordsPermissions,
this.internalContext,
this.shouldBypassPermissionChecks,
) as this;
}
}

View File

@ -2,6 +2,8 @@ import { ObjectRecordsPermissions } from 'twenty-shared/types';
import { ObjectLiteral, SelectQueryBuilder, UpdateQueryBuilder } 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 { WorkspaceUpdateQueryBuilder } from 'src/engine/twenty-orm/repository/workspace-update-query-builder';
@ -9,12 +11,18 @@ export class WorkspaceSelectQueryBuilder<
T extends ObjectLiteral,
> extends SelectQueryBuilder<T> {
objectRecordsPermissions: ObjectRecordsPermissions;
shouldBypassPermissionChecks: boolean;
internalContext: WorkspaceInternalContext;
constructor(
queryBuilder: SelectQueryBuilder<T>,
objectRecordsPermissions: ObjectRecordsPermissions,
internalContext: WorkspaceInternalContext,
shouldBypassPermissionChecks: boolean,
) {
super(queryBuilder);
this.objectRecordsPermissions = objectRecordsPermissions;
this.internalContext = internalContext;
this.shouldBypassPermissionChecks = shouldBypassPermissionChecks;
}
override update(): WorkspaceUpdateQueryBuilder<T>;
@ -33,6 +41,8 @@ export class WorkspaceSelectQueryBuilder<
return new WorkspaceUpdateQueryBuilder<T>(
updateQueryBuilder,
this.objectRecordsPermissions,
this.internalContext,
this.shouldBypassPermissionChecks,
);
}
@ -40,6 +50,8 @@ export class WorkspaceSelectQueryBuilder<
validateQueryIsPermittedOrThrow(
this.expressionMap,
this.objectRecordsPermissions,
this.internalContext.objectMetadataMaps,
this.shouldBypassPermissionChecks,
);
return super.execute();
@ -49,6 +61,8 @@ export class WorkspaceSelectQueryBuilder<
validateQueryIsPermittedOrThrow(
this.expressionMap,
this.objectRecordsPermissions,
this.internalContext.objectMetadataMaps,
this.shouldBypassPermissionChecks,
);
return super.getMany();

View File

@ -1,24 +1,34 @@
import { ObjectRecordsPermissions } from 'twenty-shared/types';
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';
export class WorkspaceUpdateQueryBuilder<
Entity extends ObjectLiteral,
> extends UpdateQueryBuilder<Entity> {
private objectRecordsPermissions: ObjectRecordsPermissions;
private shouldBypassPermissionChecks: boolean;
private internalContext: WorkspaceInternalContext;
constructor(
queryBuilder: UpdateQueryBuilder<Entity>,
objectRecordsPermissions: ObjectRecordsPermissions,
internalContext: WorkspaceInternalContext,
shouldBypassPermissionChecks: boolean,
) {
super(queryBuilder);
this.objectRecordsPermissions = objectRecordsPermissions;
this.internalContext = internalContext;
this.shouldBypassPermissionChecks = shouldBypassPermissionChecks;
}
override execute(): Promise<UpdateResult> {
validateQueryIsPermittedOrThrow(
this.expressionMap,
this.objectRecordsPermissions,
this.internalContext.objectMetadataMaps,
this.shouldBypassPermissionChecks,
);
return super.execute();

View File

@ -36,9 +36,9 @@ export class WorkspaceRepository<
T extends ObjectLiteral,
> extends Repository<T> {
private readonly internalContext: WorkspaceInternalContext;
private shouldBypassPermissionChecks: boolean;
private featureFlagMap: FeatureFlagMap;
private objectRecordsPermissions?: ObjectRecordsPermissions;
constructor(
internalContext: WorkspaceInternalContext,
target: EntityTarget<T>,
@ -46,11 +46,13 @@ export class WorkspaceRepository<
featureFlagMap: FeatureFlagMap,
queryRunner?: QueryRunner,
objectRecordsPermissions?: ObjectRecordsPermissions,
shouldBypassPermissionChecks = false,
) {
super(target, manager, queryRunner);
this.internalContext = internalContext;
this.featureFlagMap = featureFlagMap;
this.objectRecordsPermissions = objectRecordsPermissions;
this.shouldBypassPermissionChecks = shouldBypassPermissionChecks;
}
override createQueryBuilder<U extends T>(
@ -74,6 +76,8 @@ export class WorkspaceRepository<
return new WorkspaceQueryBuilder(
queryBuilder,
this.objectRecordsPermissions,
this.internalContext,
this.shouldBypassPermissionChecks,
);
}
}