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:
@ -42,6 +42,7 @@ export class ProcessNestedRelationsV2Helper {
|
||||
authContext,
|
||||
dataSource,
|
||||
roleId,
|
||||
shouldBypassPermissionChecks,
|
||||
}: {
|
||||
objectMetadataMaps: ObjectMetadataMaps;
|
||||
parentObjectMetadataItem: ObjectMetadataItemWithFieldMaps;
|
||||
@ -52,6 +53,7 @@ export class ProcessNestedRelationsV2Helper {
|
||||
limit: number;
|
||||
authContext: AuthContext;
|
||||
dataSource: WorkspaceDataSource;
|
||||
shouldBypassPermissionChecks: boolean;
|
||||
roleId?: string;
|
||||
}): Promise<void> {
|
||||
const processRelationTasks = Object.entries(relations).map(
|
||||
@ -67,6 +69,7 @@ export class ProcessNestedRelationsV2Helper {
|
||||
limit,
|
||||
authContext,
|
||||
dataSource,
|
||||
shouldBypassPermissionChecks,
|
||||
roleId,
|
||||
}),
|
||||
);
|
||||
@ -85,6 +88,7 @@ export class ProcessNestedRelationsV2Helper {
|
||||
limit,
|
||||
authContext,
|
||||
dataSource,
|
||||
shouldBypassPermissionChecks,
|
||||
roleId,
|
||||
}: {
|
||||
objectMetadataMaps: ObjectMetadataMaps;
|
||||
@ -97,6 +101,7 @@ export class ProcessNestedRelationsV2Helper {
|
||||
limit: number;
|
||||
authContext: AuthContext;
|
||||
dataSource: WorkspaceDataSource;
|
||||
shouldBypassPermissionChecks: boolean;
|
||||
roleId?: string;
|
||||
}): Promise<void> {
|
||||
const sourceFieldMetadata =
|
||||
@ -129,6 +134,7 @@ export class ProcessNestedRelationsV2Helper {
|
||||
|
||||
const targetObjectRepository = dataSource.getRepository(
|
||||
targetObjectMetadata.nameSingular,
|
||||
shouldBypassPermissionChecks,
|
||||
roleId,
|
||||
);
|
||||
|
||||
@ -199,6 +205,8 @@ export class ProcessNestedRelationsV2Helper {
|
||||
limit,
|
||||
authContext,
|
||||
dataSource,
|
||||
shouldBypassPermissionChecks,
|
||||
roleId,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -46,6 +46,7 @@ export class ProcessNestedRelationsHelper {
|
||||
authContext,
|
||||
dataSource,
|
||||
isNewRelationEnabled,
|
||||
shouldBypassPermissionChecks,
|
||||
roleId,
|
||||
}: {
|
||||
objectMetadataMaps: ObjectMetadataMaps;
|
||||
@ -58,6 +59,7 @@ export class ProcessNestedRelationsHelper {
|
||||
authContext: AuthContext;
|
||||
dataSource: WorkspaceDataSource;
|
||||
isNewRelationEnabled: boolean;
|
||||
shouldBypassPermissionChecks: boolean;
|
||||
roleId?: string;
|
||||
}): Promise<void> {
|
||||
if (isNewRelationEnabled) {
|
||||
@ -71,6 +73,7 @@ export class ProcessNestedRelationsHelper {
|
||||
limit,
|
||||
authContext,
|
||||
dataSource,
|
||||
shouldBypassPermissionChecks,
|
||||
roleId,
|
||||
});
|
||||
}
|
||||
@ -89,6 +92,7 @@ export class ProcessNestedRelationsHelper {
|
||||
authContext,
|
||||
dataSource,
|
||||
isNewRelationEnabled,
|
||||
shouldBypassPermissionChecks,
|
||||
roleId,
|
||||
}),
|
||||
);
|
||||
@ -108,6 +112,7 @@ export class ProcessNestedRelationsHelper {
|
||||
authContext,
|
||||
dataSource,
|
||||
isNewRelationEnabled,
|
||||
shouldBypassPermissionChecks,
|
||||
roleId,
|
||||
}: {
|
||||
objectMetadataMaps: ObjectMetadataMaps;
|
||||
@ -118,9 +123,10 @@ export class ProcessNestedRelationsHelper {
|
||||
nestedRelations: any;
|
||||
aggregate: Record<string, AggregationField>;
|
||||
limit: number;
|
||||
authContext: any;
|
||||
authContext: AuthContext;
|
||||
dataSource: DataSource;
|
||||
isNewRelationEnabled: boolean;
|
||||
shouldBypassPermissionChecks: boolean;
|
||||
roleId?: string;
|
||||
}): Promise<void> {
|
||||
const relationFieldMetadata =
|
||||
@ -148,6 +154,7 @@ export class ProcessNestedRelationsHelper {
|
||||
authContext,
|
||||
dataSource,
|
||||
isNewRelationEnabled,
|
||||
shouldBypassPermissionChecks,
|
||||
roleId,
|
||||
});
|
||||
}
|
||||
@ -164,6 +171,7 @@ export class ProcessNestedRelationsHelper {
|
||||
authContext,
|
||||
dataSource,
|
||||
isNewRelationEnabled,
|
||||
shouldBypassPermissionChecks,
|
||||
roleId,
|
||||
}: {
|
||||
objectMetadataMaps: ObjectMetadataMaps;
|
||||
@ -177,6 +185,7 @@ export class ProcessNestedRelationsHelper {
|
||||
authContext: AuthContext;
|
||||
dataSource: WorkspaceDataSource;
|
||||
isNewRelationEnabled: boolean;
|
||||
shouldBypassPermissionChecks: boolean;
|
||||
roleId?: string;
|
||||
}): Promise<void> {
|
||||
const { inverseRelationName, referenceObjectMetadata } =
|
||||
@ -188,6 +197,7 @@ export class ProcessNestedRelationsHelper {
|
||||
|
||||
const relationRepository = dataSource.getRepository(
|
||||
referenceObjectMetadata.nameSingular,
|
||||
shouldBypassPermissionChecks,
|
||||
roleId,
|
||||
);
|
||||
|
||||
@ -248,6 +258,8 @@ export class ProcessNestedRelationsHelper {
|
||||
authContext,
|
||||
dataSource,
|
||||
isNewRelationEnabled,
|
||||
shouldBypassPermissionChecks,
|
||||
roleId,
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -264,6 +276,7 @@ export class ProcessNestedRelationsHelper {
|
||||
authContext,
|
||||
dataSource,
|
||||
isNewRelationEnabled,
|
||||
shouldBypassPermissionChecks,
|
||||
roleId,
|
||||
}: {
|
||||
objectMetadataMaps: ObjectMetadataMaps;
|
||||
@ -277,6 +290,7 @@ export class ProcessNestedRelationsHelper {
|
||||
authContext: any;
|
||||
dataSource: WorkspaceDataSource;
|
||||
isNewRelationEnabled: boolean;
|
||||
shouldBypassPermissionChecks: boolean;
|
||||
roleId?: string;
|
||||
}): Promise<void> {
|
||||
const { referenceObjectMetadata } = this.getRelationMetadata({
|
||||
@ -287,6 +301,7 @@ export class ProcessNestedRelationsHelper {
|
||||
|
||||
const relationRepository = dataSource.getRepository(
|
||||
referenceObjectMetadata.nameSingular,
|
||||
shouldBypassPermissionChecks,
|
||||
roleId,
|
||||
);
|
||||
|
||||
@ -346,6 +361,8 @@ export class ProcessNestedRelationsHelper {
|
||||
authContext,
|
||||
dataSource,
|
||||
isNewRelationEnabled,
|
||||
shouldBypassPermissionChecks,
|
||||
roleId,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -45,6 +45,7 @@ export type GraphqlQueryResolverExecutionArgs<Input extends ResolverArgs> = {
|
||||
repository: WorkspaceRepository<ObjectLiteral>;
|
||||
graphqlQueryParser: GraphqlQueryParser;
|
||||
graphqlQuerySelectedFieldsResult: GraphqlQuerySelectedFieldsResult;
|
||||
isExecutedByApiKey: boolean;
|
||||
roleId?: string;
|
||||
};
|
||||
|
||||
@ -123,8 +124,12 @@ export abstract class GraphqlQueryBaseResolverService<
|
||||
workspaceId: authContext.workspace.id,
|
||||
});
|
||||
|
||||
const executedByApiKey = isDefined(authContext.apiKey);
|
||||
const shouldBypassPermissionChecks = executedByApiKey;
|
||||
|
||||
const repository = dataSource.getRepository(
|
||||
objectMetadataItemWithFieldMaps.nameSingular,
|
||||
shouldBypassPermissionChecks,
|
||||
roleId,
|
||||
);
|
||||
|
||||
@ -150,6 +155,7 @@ export abstract class GraphqlQueryBaseResolverService<
|
||||
repository,
|
||||
graphqlQueryParser,
|
||||
graphqlQuerySelectedFieldsResult,
|
||||
isExecutedByApiKey: executedByApiKey,
|
||||
roleId,
|
||||
};
|
||||
|
||||
|
||||
@ -53,12 +53,15 @@ export class GraphqlQueryCreateManyResolverService extends GraphqlQueryBaseResol
|
||||
objectMetadataItemWithFieldMaps,
|
||||
);
|
||||
|
||||
const shouldBypassPermissionChecks = executionArgs.isExecutedByApiKey;
|
||||
|
||||
await this.processNestedRelationsIfNeeded(
|
||||
executionArgs,
|
||||
upsertedRecords,
|
||||
objectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps,
|
||||
featureFlagsMap,
|
||||
shouldBypassPermissionChecks,
|
||||
roleId,
|
||||
);
|
||||
|
||||
@ -329,6 +332,7 @@ export class GraphqlQueryCreateManyResolverService extends GraphqlQueryBaseResol
|
||||
objectMetadataItemWithFieldMaps: ObjectMetadataItemWithFieldMaps,
|
||||
objectMetadataMaps: ObjectMetadataMaps,
|
||||
featureFlagsMap: Record<FeatureFlagKey, boolean>,
|
||||
shouldBypassPermissionChecks: boolean,
|
||||
roleId?: string,
|
||||
): Promise<void> {
|
||||
if (!executionArgs.graphqlQuerySelectedFieldsResult.relations) {
|
||||
@ -346,6 +350,7 @@ export class GraphqlQueryCreateManyResolverService extends GraphqlQueryBaseResol
|
||||
isNewRelationEnabled:
|
||||
featureFlagsMap[FeatureFlagKey.IsNewRelationEnabled],
|
||||
roleId,
|
||||
shouldBypassPermissionChecks,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -74,6 +74,7 @@ export class GraphqlQueryCreateOneResolverService extends GraphqlQueryBaseResolv
|
||||
isNewRelationEnabled:
|
||||
featureFlagsMap[FeatureFlagKey.IsNewRelationEnabled],
|
||||
roleId,
|
||||
shouldBypassPermissionChecks: executionArgs.isExecutedByApiKey,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -75,6 +75,7 @@ export class GraphqlQueryDeleteManyResolverService extends GraphqlQueryBaseResol
|
||||
isNewRelationEnabled:
|
||||
featureFlagsMap[FeatureFlagKey.IsNewRelationEnabled],
|
||||
roleId,
|
||||
shouldBypassPermissionChecks: executionArgs.isExecutedByApiKey,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -77,6 +77,7 @@ export class GraphqlQueryDeleteOneResolverService extends GraphqlQueryBaseResolv
|
||||
isNewRelationEnabled:
|
||||
featureFlagsMap[FeatureFlagKey.IsNewRelationEnabled],
|
||||
roleId,
|
||||
shouldBypassPermissionChecks: executionArgs.isExecutedByApiKey,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -73,6 +73,7 @@ export class GraphqlQueryDestroyManyResolverService extends GraphqlQueryBaseReso
|
||||
isNewRelationEnabled:
|
||||
featureFlagsMap[FeatureFlagKey.IsNewRelationEnabled],
|
||||
roleId,
|
||||
shouldBypassPermissionChecks: executionArgs.isExecutedByApiKey,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -73,6 +73,7 @@ export class GraphqlQueryDestroyOneResolverService extends GraphqlQueryBaseResol
|
||||
isNewRelationEnabled:
|
||||
featureFlagsMap[FeatureFlagKey.IsNewRelationEnabled],
|
||||
roleId,
|
||||
shouldBypassPermissionChecks: executionArgs.isExecutedByApiKey,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -160,6 +160,7 @@ export class GraphqlQueryFindManyResolverService extends GraphqlQueryBaseResolve
|
||||
isNewRelationEnabled:
|
||||
featureFlagsMap[FeatureFlagKey.IsNewRelationEnabled],
|
||||
roleId,
|
||||
shouldBypassPermissionChecks: executionArgs.isExecutedByApiKey,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -83,6 +83,7 @@ export class GraphqlQueryFindOneResolverService extends GraphqlQueryBaseResolver
|
||||
isNewRelationEnabled:
|
||||
featureFlagsMap[FeatureFlagKey.IsNewRelationEnabled],
|
||||
roleId,
|
||||
shouldBypassPermissionChecks: executionArgs.isExecutedByApiKey,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -75,6 +75,7 @@ export class GraphqlQueryRestoreManyResolverService extends GraphqlQueryBaseReso
|
||||
isNewRelationEnabled:
|
||||
featureFlagsMap[FeatureFlagKey.IsNewRelationEnabled],
|
||||
roleId,
|
||||
shouldBypassPermissionChecks: executionArgs.isExecutedByApiKey,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -77,6 +77,7 @@ export class GraphqlQueryRestoreOneResolverService extends GraphqlQueryBaseResol
|
||||
isNewRelationEnabled:
|
||||
featureFlagsMap[FeatureFlagKey.IsNewRelationEnabled],
|
||||
roleId,
|
||||
shouldBypassPermissionChecks: executionArgs.isExecutedByApiKey,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -113,6 +113,7 @@ export class GraphqlQueryUpdateManyResolverService extends GraphqlQueryBaseResol
|
||||
isNewRelationEnabled:
|
||||
featureFlagsMap[FeatureFlagKey.IsNewRelationEnabled],
|
||||
roleId,
|
||||
shouldBypassPermissionChecks: executionArgs.isExecutedByApiKey,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -107,6 +107,7 @@ export class GraphqlQueryUpdateOneResolverService extends GraphqlQueryBaseResolv
|
||||
isNewRelationEnabled:
|
||||
featureFlagsMap[FeatureFlagKey.IsNewRelationEnabled],
|
||||
roleId,
|
||||
shouldBypassPermissionChecks: executionArgs.isExecutedByApiKey,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -1,16 +1,17 @@
|
||||
import { BadRequestException, Injectable } from '@nestjs/common';
|
||||
|
||||
import { capitalize, isDefined } from 'twenty-shared/utils';
|
||||
import { Request } from 'express';
|
||||
import { capitalize, isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { ObjectRecord } from 'src/engine/api/graphql/workspace-query-builder/interfaces/object-record.interface';
|
||||
|
||||
import { ApiEventEmitterService } from 'src/engine/api/graphql/graphql-query-runner/services/api-event-emitter.service';
|
||||
import { CoreQueryBuilderFactory } from 'src/engine/api/rest/core/query-builder/core-query-builder.factory';
|
||||
import { parseCorePath } from 'src/engine/api/rest/core/query-builder/utils/path-parsers/parse-core-path.utils';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
import { RecordInputTransformerService } from 'src/engine/core-modules/record-transformer/services/record-input-transformer.service';
|
||||
import { ApiEventEmitterService } from 'src/engine/api/graphql/graphql-query-runner/services/api-event-emitter.service';
|
||||
import { AuthContext } from 'src/engine/core-modules/auth/types/auth-context.type';
|
||||
import { RecordInputTransformerService } from 'src/engine/core-modules/record-transformer/services/record-input-transformer.service';
|
||||
import { WorkspacePermissionsCacheService } from 'src/engine/metadata-modules/workspace-permissions-cache/workspace-permissions-cache.service';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
|
||||
@Injectable()
|
||||
export class RestApiCoreServiceV2 {
|
||||
@ -19,6 +20,7 @@ export class RestApiCoreServiceV2 {
|
||||
private readonly twentyORMGlobalManager: TwentyORMGlobalManager,
|
||||
private readonly recordInputTransformerService: RecordInputTransformerService,
|
||||
protected readonly apiEventEmitterService: ApiEventEmitterService,
|
||||
private readonly workspacePermissionsCacheService: WorkspacePermissionsCacheService,
|
||||
) {}
|
||||
|
||||
async delete(request: Request) {
|
||||
@ -137,7 +139,7 @@ export class RestApiCoreServiceV2 {
|
||||
}
|
||||
|
||||
private async getRepositoryAndMetadataOrFail(request: Request) {
|
||||
const { workspace } = request;
|
||||
const { workspace, apiKey, userWorkspaceId } = request;
|
||||
const { object: parsedObject } = parseCorePath(request);
|
||||
|
||||
const objectMetadata = await this.coreQueryBuilderFactory.getObjectMetadata(
|
||||
@ -153,13 +155,25 @@ export class RestApiCoreServiceV2 {
|
||||
throw new BadRequestException('Workspace not found');
|
||||
}
|
||||
|
||||
const dataSource =
|
||||
await this.twentyORMGlobalManager.getDataSourceForWorkspace(workspace.id);
|
||||
|
||||
const objectMetadataNameSingular =
|
||||
objectMetadata.objectMetadataMapItem.nameSingular;
|
||||
const repository =
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace<ObjectRecord>(
|
||||
workspace.id,
|
||||
objectMetadataNameSingular,
|
||||
);
|
||||
|
||||
const shouldBypassPermissionChecks = !!apiKey;
|
||||
|
||||
const roleId =
|
||||
await this.workspacePermissionsCacheService.getRoleIdFromUserWorkspaceId({
|
||||
workspaceId: workspace.id,
|
||||
userWorkspaceId,
|
||||
});
|
||||
|
||||
const repository = dataSource.getRepository<ObjectRecord>(
|
||||
objectMetadataNameSingular,
|
||||
shouldBypassPermissionChecks,
|
||||
roleId,
|
||||
);
|
||||
|
||||
return {
|
||||
objectMetadataNameSingular,
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { HttpModule } from '@nestjs/axios';
|
||||
import { Module } from '@nestjs/common';
|
||||
|
||||
import { ApiEventEmitterService } from 'src/engine/api/graphql/graphql-query-runner/services/api-event-emitter.service';
|
||||
import { RestApiCoreBatchController } from 'src/engine/api/rest/core/controllers/rest-api-core-batch.controller';
|
||||
import { RestApiCoreController } from 'src/engine/api/rest/core/controllers/rest-api-core.controller';
|
||||
import { CoreQueryBuilderModule } from 'src/engine/api/rest/core/query-builder/core-query-builder.module';
|
||||
@ -15,9 +16,9 @@ import { RestApiMetadataService } from 'src/engine/api/rest/metadata/rest-api-me
|
||||
import { RestApiService } from 'src/engine/api/rest/rest-api.service';
|
||||
import { AuthModule } from 'src/engine/core-modules/auth/auth.module';
|
||||
import { RecordTransformerModule } from 'src/engine/core-modules/record-transformer/record-transformer.module';
|
||||
import { WorkspacePermissionsCacheModule } from 'src/engine/metadata-modules/workspace-permissions-cache/workspace-permissions-cache.module';
|
||||
import { TwentyORMModule } from 'src/engine/twenty-orm/twenty-orm.module';
|
||||
import { WorkspaceCacheStorageModule } from 'src/engine/workspace-cache-storage/workspace-cache-storage.module';
|
||||
import { ApiEventEmitterService } from 'src/engine/api/graphql/graphql-query-runner/services/api-event-emitter.service';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
@ -28,6 +29,7 @@ import { ApiEventEmitterService } from 'src/engine/api/graphql/graphql-query-run
|
||||
HttpModule,
|
||||
TwentyORMModule,
|
||||
RecordTransformerModule,
|
||||
WorkspacePermissionsCacheModule,
|
||||
],
|
||||
controllers: [
|
||||
RestApiMetadataController,
|
||||
|
||||
Reference in New Issue
Block a user