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,
|
||||
|
||||
@ -72,7 +72,6 @@ export class AccessTokenService {
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace<WorkspaceMemberWorkspaceEntity>(
|
||||
workspaceId,
|
||||
'workspaceMember',
|
||||
false,
|
||||
);
|
||||
|
||||
const workspaceMember = await workspaceMemberRepository.findOne({
|
||||
|
||||
@ -5,6 +5,7 @@ import { Repository } from 'typeorm';
|
||||
|
||||
import { TypeORMService } from 'src/database/typeorm/typeorm.service';
|
||||
import { DatabaseEventAction } from 'src/engine/api/graphql/graphql-query-runner/enums/database-event-action';
|
||||
import { USER_SIGNUP_EVENT_NAME } from 'src/engine/api/graphql/workspace-query-runner/constants/user-signup-event-name.constants';
|
||||
import { AuthException } from 'src/engine/core-modules/auth/auth.exception';
|
||||
import { DomainManagerService } from 'src/engine/core-modules/domain-manager/services/domain-manager.service';
|
||||
import { UserWorkspace } from 'src/engine/core-modules/user-workspace/user-workspace.entity';
|
||||
@ -12,6 +13,7 @@ import { UserWorkspaceService } from 'src/engine/core-modules/user-workspace/use
|
||||
import { User } from 'src/engine/core-modules/user/user.entity';
|
||||
import { WorkspaceInvitationService } from 'src/engine/core-modules/workspace-invitation/services/workspace-invitation.service';
|
||||
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
import { DataSourceEntity } from 'src/engine/metadata-modules/data-source/data-source.entity';
|
||||
import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service';
|
||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { PermissionsException } from 'src/engine/metadata-modules/permissions/permissions.exception';
|
||||
@ -19,8 +21,6 @@ import { UserRoleService } from 'src/engine/metadata-modules/user-role/user-role
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
import { WorkspaceEventEmitter } from 'src/engine/workspace-event-emitter/workspace-event-emitter';
|
||||
import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/standard-objects/workspace-member.workspace-entity';
|
||||
import { DataSourceEntity } from 'src/engine/metadata-modules/data-source/data-source.entity';
|
||||
import { USER_SIGNUP_EVENT_NAME } from 'src/engine/api/graphql/workspace-query-runner/constants/user-signup-event-name.constants';
|
||||
|
||||
describe('UserWorkspaceService', () => {
|
||||
let service: UserWorkspaceService;
|
||||
|
||||
@ -20,6 +20,7 @@ import { User } from 'src/engine/core-modules/user/user.entity';
|
||||
import { userValidator } from 'src/engine/core-modules/user/user.validate';
|
||||
import { WorkspaceInvitationService } from 'src/engine/core-modules/workspace-invitation/services/workspace-invitation.service';
|
||||
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
import { workspaceValidator } from 'src/engine/core-modules/workspace/workspace.validate';
|
||||
import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service';
|
||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import {
|
||||
@ -32,7 +33,6 @@ import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.
|
||||
import { WorkspaceEventEmitter } from 'src/engine/workspace-event-emitter/workspace-event-emitter';
|
||||
import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/standard-objects/workspace-member.workspace-entity';
|
||||
import { assert } from 'src/utils/assert';
|
||||
import { workspaceValidator } from 'src/engine/core-modules/workspace/workspace.validate';
|
||||
|
||||
export class UserWorkspaceService extends TypeOrmQueryService<UserWorkspace> {
|
||||
constructor(
|
||||
|
||||
@ -31,6 +31,7 @@ import { RemoteTableRelationsModule } from 'src/engine/metadata-modules/remote-s
|
||||
import { SearchVectorModule } from 'src/engine/metadata-modules/search-vector/search-vector.module';
|
||||
import { WorkspaceMetadataVersionModule } from 'src/engine/metadata-modules/workspace-metadata-version/workspace-metadata-version.module';
|
||||
import { WorkspaceMigrationModule } from 'src/engine/metadata-modules/workspace-migration/workspace-migration.module';
|
||||
import { WorkspacePermissionsCacheModule } from 'src/engine/metadata-modules/workspace-permissions-cache/workspace-permissions-cache.module';
|
||||
import { WorkspaceMigrationRunnerModule } from 'src/engine/workspace-manager/workspace-migration-runner/workspace-migration-runner.module';
|
||||
|
||||
import { ObjectMetadataEntity } from './object-metadata.entity';
|
||||
@ -59,6 +60,7 @@ import { UpdateObjectPayload } from './dtos/update-object.input';
|
||||
IndexMetadataModule,
|
||||
FeatureFlagModule,
|
||||
PermissionsModule,
|
||||
WorkspacePermissionsCacheModule,
|
||||
],
|
||||
services: [
|
||||
ObjectMetadataService,
|
||||
|
||||
@ -40,6 +40,7 @@ import { RemoteTableRelationsService } from 'src/engine/metadata-modules/remote-
|
||||
import { SearchVectorService } from 'src/engine/metadata-modules/search-vector/search-vector.service';
|
||||
import { validateNameAndLabelAreSyncOrThrow } from 'src/engine/metadata-modules/utils/validate-name-and-label-are-sync-or-throw.util';
|
||||
import { WorkspaceMetadataVersionService } from 'src/engine/metadata-modules/workspace-metadata-version/services/workspace-metadata-version.service';
|
||||
import { WorkspacePermissionsCacheService } from 'src/engine/metadata-modules/workspace-permissions-cache/workspace-permissions-cache.service';
|
||||
import { computeObjectTargetTable } from 'src/engine/utils/compute-object-target-table.util';
|
||||
import { WorkspaceMigrationRunnerService } from 'src/engine/workspace-manager/workspace-migration-runner/workspace-migration-runner.service';
|
||||
import { CUSTOM_OBJECT_STANDARD_FIELD_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids';
|
||||
@ -68,6 +69,7 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
|
||||
private readonly objectMetadataMigrationService: ObjectMetadataMigrationService,
|
||||
private readonly objectMetadataRelatedRecordsService: ObjectMetadataRelatedRecordsService,
|
||||
private readonly indexMetadataService: IndexMetadataService,
|
||||
private readonly workspacePermissionsCacheService: WorkspacePermissionsCacheService,
|
||||
private readonly featureFlagService: FeatureFlagService,
|
||||
) {
|
||||
super(objectMetadataRepository);
|
||||
@ -236,6 +238,10 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
|
||||
objectMetadataInput.workspaceId,
|
||||
);
|
||||
|
||||
await this.workspacePermissionsCacheService.recomputeRolesPermissionsCache({
|
||||
workspaceId: objectMetadataInput.workspaceId,
|
||||
});
|
||||
|
||||
return createdObjectMetadata;
|
||||
}
|
||||
|
||||
@ -441,6 +447,10 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
|
||||
workspaceId,
|
||||
);
|
||||
|
||||
await this.workspacePermissionsCacheService.recomputeRolesPermissionsCache({
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
return objectMetadata;
|
||||
}
|
||||
|
||||
|
||||
@ -5,6 +5,7 @@ import {
|
||||
ObjectRecordsPermissions,
|
||||
ObjectRecordsPermissionsByRoleId,
|
||||
} from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { In, Repository } from 'typeorm';
|
||||
|
||||
import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum';
|
||||
@ -51,19 +52,15 @@ export class WorkspacePermissionsCacheService {
|
||||
ignoreLock?: boolean;
|
||||
roleIds?: string[];
|
||||
}): Promise<void> {
|
||||
const isPermissionsV2Enabled =
|
||||
await this.featureFlagService.isFeatureEnabled(
|
||||
FeatureFlagKey.IsPermissionsV2Enabled,
|
||||
workspaceId,
|
||||
);
|
||||
if (!ignoreLock) {
|
||||
const isAlreadyCaching =
|
||||
await this.workspacePermissionsCacheStorageService.getRolesPermissionsOngoingCachingLock(
|
||||
workspaceId,
|
||||
);
|
||||
|
||||
const isAlreadyCaching =
|
||||
await this.workspacePermissionsCacheStorageService.getRolesPermissionsOngoingCachingLock(
|
||||
workspaceId,
|
||||
);
|
||||
|
||||
if (!ignoreLock && isAlreadyCaching) {
|
||||
return;
|
||||
if (isAlreadyCaching) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
await this.workspacePermissionsCacheStorageService.addRolesPermissionsOngoingCachingLock(
|
||||
@ -80,6 +77,12 @@ export class WorkspacePermissionsCacheService {
|
||||
);
|
||||
}
|
||||
|
||||
const isPermissionsV2Enabled =
|
||||
await this.featureFlagService.isFeatureEnabled(
|
||||
FeatureFlagKey.IsPermissionsV2Enabled,
|
||||
workspaceId,
|
||||
);
|
||||
|
||||
const recomputedRolesPermissions =
|
||||
await this.getObjectRecordPermissionsForRoles({
|
||||
workspaceId,
|
||||
@ -109,13 +112,15 @@ export class WorkspacePermissionsCacheService {
|
||||
workspaceId: string;
|
||||
ignoreLock?: boolean;
|
||||
}): Promise<void> {
|
||||
const isAlreadyCaching =
|
||||
await this.workspacePermissionsCacheStorageService.getUserWorkspaceRoleMapOngoingCachingLock(
|
||||
workspaceId,
|
||||
);
|
||||
if (!ignoreLock) {
|
||||
const isAlreadyCaching =
|
||||
await this.workspacePermissionsCacheStorageService.getUserWorkspaceRoleMapOngoingCachingLock(
|
||||
workspaceId,
|
||||
);
|
||||
|
||||
if (!ignoreLock && isAlreadyCaching) {
|
||||
return;
|
||||
if (isAlreadyCaching) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
await this.workspacePermissionsCacheStorageService.addUserWorkspaceRoleMapOngoingCachingLock(
|
||||
@ -183,6 +188,24 @@ export class WorkspacePermissionsCacheService {
|
||||
});
|
||||
}
|
||||
|
||||
async getRoleIdFromUserWorkspaceId({
|
||||
workspaceId,
|
||||
userWorkspaceId,
|
||||
}: {
|
||||
workspaceId: string;
|
||||
userWorkspaceId?: string;
|
||||
}): Promise<string | undefined> {
|
||||
if (!isDefined(userWorkspaceId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const userWorkspaceRoleMap = await this.getUserWorkspaceRoleMapFromCache({
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
return userWorkspaceRoleMap[userWorkspaceId];
|
||||
}
|
||||
|
||||
private async getObjectRecordPermissionsForRoles({
|
||||
workspaceId,
|
||||
isPermissionsV2Enabled,
|
||||
|
||||
@ -41,10 +41,19 @@ export class WorkspaceDataSource extends DataSource {
|
||||
|
||||
override getRepository<Entity extends ObjectLiteral>(
|
||||
target: EntityTarget<Entity>,
|
||||
shouldBypassPermissionChecks = false,
|
||||
roleId?: string,
|
||||
): WorkspaceRepository<Entity> {
|
||||
if (shouldBypassPermissionChecks === true) {
|
||||
return this.manager.getRepository(target, shouldBypassPermissionChecks);
|
||||
}
|
||||
|
||||
if (roleId) {
|
||||
return this.manager.getRepository(target, roleId);
|
||||
return this.manager.getRepository(
|
||||
target,
|
||||
shouldBypassPermissionChecks,
|
||||
roleId,
|
||||
);
|
||||
}
|
||||
|
||||
return this.manager.getRepository(target);
|
||||
@ -64,11 +73,11 @@ export class WorkspaceDataSource extends DataSource {
|
||||
this.permissionsPerRoleId = permissionsPerRoleId;
|
||||
}
|
||||
|
||||
setFeatureFlagsMap(featureFlagMap: FeatureFlagMap) {
|
||||
setFeatureFlagMap(featureFlagMap: FeatureFlagMap) {
|
||||
this.featureFlagMap = featureFlagMap;
|
||||
}
|
||||
|
||||
setFeatureFlagsMapVersion(featureFlagMapVersion: string) {
|
||||
setFeatureFlagMapVersion(featureFlagMapVersion: string) {
|
||||
this.featureFlagMapVersion = featureFlagMapVersion;
|
||||
}
|
||||
}
|
||||
|
||||
@ -28,10 +28,17 @@ export class WorkspaceEntityManager extends EntityManager {
|
||||
|
||||
override getRepository<Entity extends ObjectLiteral>(
|
||||
target: EntityTarget<Entity>,
|
||||
shouldBypassPermissionChecks = false,
|
||||
roleId?: string,
|
||||
): WorkspaceRepository<Entity> {
|
||||
const dataSource = this.connection as WorkspaceDataSource;
|
||||
const repositoryKey = `${dataSource.getMetadata(target).name}_${roleId ?? 'default'}${dataSource.rolesPermissionsVersion ? `_${dataSource.rolesPermissionsVersion}` : ''}${dataSource.featureFlagMapVersion ? `_${dataSource.featureFlagMapVersion}` : ''}`;
|
||||
|
||||
const repositoryKey = this.getRepositoryKey({
|
||||
target,
|
||||
dataSource,
|
||||
roleId,
|
||||
shouldBypassPermissionChecks,
|
||||
});
|
||||
const repoFromMap = this.repositories.get(repositoryKey);
|
||||
|
||||
if (repoFromMap) {
|
||||
@ -53,10 +60,36 @@ export class WorkspaceEntityManager extends EntityManager {
|
||||
dataSource.featureFlagMap,
|
||||
this.queryRunner,
|
||||
objectPermissions,
|
||||
shouldBypassPermissionChecks,
|
||||
);
|
||||
|
||||
this.repositories.set(repositoryKey, newRepository);
|
||||
|
||||
return newRepository;
|
||||
}
|
||||
|
||||
private getRepositoryKey({
|
||||
target,
|
||||
dataSource,
|
||||
roleId,
|
||||
shouldBypassPermissionChecks,
|
||||
}: {
|
||||
target: EntityTarget<any>;
|
||||
dataSource: WorkspaceDataSource;
|
||||
shouldBypassPermissionChecks: boolean;
|
||||
roleId?: string;
|
||||
}) {
|
||||
const repositoryPrefix = dataSource.getMetadata(target).name;
|
||||
const roleIdSuffix = roleId ? `_${roleId}` : '';
|
||||
const rolesPermissionsVersionSuffix = dataSource.rolesPermissionsVersion
|
||||
? `_${dataSource.rolesPermissionsVersion}`
|
||||
: '';
|
||||
const featureFlagMapVersionSuffix = dataSource.featureFlagMapVersion
|
||||
? `_${dataSource.featureFlagMapVersion}`
|
||||
: '';
|
||||
|
||||
return shouldBypassPermissionChecks
|
||||
? `${repositoryPrefix}_bypass${featureFlagMapVersionSuffix}`
|
||||
: `${repositoryPrefix}${roleIdSuffix}${rolesPermissionsVersionSuffix}${featureFlagMapVersionSuffix}`;
|
||||
}
|
||||
}
|
||||
|
||||
@ -13,6 +13,7 @@ export class ScopedWorkspaceContextFactory {
|
||||
workspaceId: string | null;
|
||||
workspaceMetadataVersion: number | null;
|
||||
userWorkspaceId: string | null;
|
||||
isExecutedByApiKey: boolean;
|
||||
} {
|
||||
const workspaceId: string | undefined =
|
||||
this.request?.['req']?.['workspaceId'] ||
|
||||
@ -24,6 +25,7 @@ export class ScopedWorkspaceContextFactory {
|
||||
workspaceId: workspaceId ?? null,
|
||||
workspaceMetadataVersion: workspaceMetadataVersion ?? null,
|
||||
userWorkspaceId: this.request?.['req']?.['userWorkspaceId'] ?? null,
|
||||
isExecutedByApiKey: !!this.request?.['req']?.['apiKey'],
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@ -294,9 +294,9 @@ export class WorkspaceDatasourceFactory {
|
||||
currentVersion: workspaceDataSource.featureFlagMapVersion,
|
||||
newVersion: cachedFeatureFlagMapVersion,
|
||||
newData: cachedFeatureFlagMap,
|
||||
setData: (data) => workspaceDataSource.setFeatureFlagsMap(data),
|
||||
setData: (data) => workspaceDataSource.setFeatureFlagMap(data),
|
||||
setVersion: (version) =>
|
||||
workspaceDataSource.setFeatureFlagsMapVersion(version),
|
||||
workspaceDataSource.setFeatureFlagMapVersion(version),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -15,19 +15,31 @@ export class TwentyORMGlobalManager {
|
||||
async getRepositoryForWorkspace<T extends ObjectLiteral>(
|
||||
workspaceId: string,
|
||||
workspaceEntity: Type<T>,
|
||||
shouldFailIfMetadataNotFound?: boolean,
|
||||
options?: {
|
||||
shouldBypassPermissionChecks?: boolean;
|
||||
shouldFailIfMetadataNotFound?: boolean;
|
||||
},
|
||||
): Promise<WorkspaceRepository<T>>;
|
||||
|
||||
async getRepositoryForWorkspace<T extends ObjectLiteral>(
|
||||
workspaceId: string,
|
||||
objectMetadataName: string,
|
||||
shouldFailIfMetadataNotFound?: boolean,
|
||||
options?: {
|
||||
shouldBypassPermissionChecks?: boolean;
|
||||
shouldFailIfMetadataNotFound?: boolean;
|
||||
},
|
||||
): Promise<WorkspaceRepository<T>>;
|
||||
|
||||
async getRepositoryForWorkspace<T extends ObjectLiteral>(
|
||||
workspaceId: string,
|
||||
workspaceEntityOrobjectMetadataName: Type<T> | string,
|
||||
shouldFailIfMetadataNotFound = true,
|
||||
options: {
|
||||
shouldBypassPermissionChecks?: boolean;
|
||||
shouldFailIfMetadataNotFound?: boolean;
|
||||
} = {
|
||||
shouldBypassPermissionChecks: false,
|
||||
shouldFailIfMetadataNotFound: true,
|
||||
},
|
||||
): Promise<WorkspaceRepository<T>> {
|
||||
let objectMetadataName: string;
|
||||
|
||||
@ -42,10 +54,13 @@ export class TwentyORMGlobalManager {
|
||||
const workspaceDataSource = await this.workspaceDataSourceFactory.create(
|
||||
workspaceId,
|
||||
null,
|
||||
shouldFailIfMetadataNotFound,
|
||||
options.shouldFailIfMetadataNotFound,
|
||||
);
|
||||
|
||||
const repository = workspaceDataSource.getRepository<T>(objectMetadataName);
|
||||
const repository = workspaceDataSource.getRepository<T>(
|
||||
objectMetadataName,
|
||||
options.shouldBypassPermissionChecks,
|
||||
);
|
||||
|
||||
return repository;
|
||||
}
|
||||
|
||||
@ -30,8 +30,12 @@ export class TwentyORMManager {
|
||||
async getRepository<T extends ObjectLiteral>(
|
||||
workspaceEntityOrobjectMetadataName: Type<T> | string,
|
||||
): Promise<WorkspaceRepository<T>> {
|
||||
const { workspaceId, workspaceMetadataVersion, userWorkspaceId } =
|
||||
this.scopedWorkspaceContextFactory.create();
|
||||
const {
|
||||
workspaceId,
|
||||
workspaceMetadataVersion,
|
||||
userWorkspaceId,
|
||||
isExecutedByApiKey,
|
||||
} = this.scopedWorkspaceContextFactory.create();
|
||||
|
||||
let objectMetadataName: string;
|
||||
|
||||
@ -65,7 +69,13 @@ export class TwentyORMManager {
|
||||
roleId = userWorkspaceRole?.roleId;
|
||||
}
|
||||
|
||||
return workspaceDataSource.getRepository<T>(objectMetadataName, roleId);
|
||||
const shouldBypassPermissionChecks = !!isExecutedByApiKey;
|
||||
|
||||
return workspaceDataSource.getRepository<T>(
|
||||
objectMetadataName,
|
||||
shouldBypassPermissionChecks,
|
||||
roleId,
|
||||
);
|
||||
}
|
||||
|
||||
async getDatasource() {
|
||||
|
||||
Reference in New Issue
Block a user