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

@ -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,

View File

@ -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;
}

View File

@ -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,