## Context - Introduced objectPermissions in currentUserWorkspace which uses role permissions from cache so we can fetch granular permissions from the API - Refactored cached role permissions to map permissions with object metadata id instead of object metadata name singular to be more flexible New Cache <img width="574" alt="Screenshot 2025-05-27 at 11 59 06" src="https://github.com/user-attachments/assets/1a090134-1b8a-4681-a630-29f1472178bd" /> GQL <img width="977" alt="Screenshot 2025-05-27 at 11 58 53" src="https://github.com/user-attachments/assets/3b9a82b0-6019-4a25-a6e2-a9e0fb4bb8a0" /> Next steps: Use the updated API in the FE to fetch granular permissions and update useHasObjectReadOnlyPermission hook
This commit is contained in:
@ -2,12 +2,6 @@ import { Field, ObjectType } from '@nestjs/graphql';
|
||||
|
||||
@ObjectType('ObjectPermission')
|
||||
export class ObjectPermissionDTO {
|
||||
@Field({ nullable: false })
|
||||
id: string;
|
||||
|
||||
@Field({ nullable: false })
|
||||
roleId: string;
|
||||
|
||||
@Field({ nullable: false })
|
||||
objectMetadataId: string;
|
||||
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { PermissionsOnAllObjectRecords } from 'twenty-shared/constants';
|
||||
import { ObjectRecordsPermissions } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import {
|
||||
@ -26,6 +27,64 @@ export class PermissionsService {
|
||||
private readonly featureFlagService: FeatureFlagService,
|
||||
) {}
|
||||
|
||||
public async getUserWorkspacePermissionsV2({
|
||||
userWorkspaceId,
|
||||
workspaceId,
|
||||
}: {
|
||||
userWorkspaceId: string;
|
||||
workspaceId: string;
|
||||
}): Promise<{
|
||||
settingsPermissions: Record<SettingPermissionType, boolean>;
|
||||
objectRecordsPermissions: ObjectRecordsPermissions;
|
||||
}> {
|
||||
const [roleOfUserWorkspace] = await this.userRoleService
|
||||
.getRolesByUserWorkspaces({
|
||||
userWorkspaceIds: [userWorkspaceId],
|
||||
workspaceId,
|
||||
})
|
||||
.then((roles) => roles?.get(userWorkspaceId) ?? []);
|
||||
|
||||
let hasPermissionOnSettingFeature = false;
|
||||
|
||||
if (!isDefined(roleOfUserWorkspace)) {
|
||||
throw new PermissionsException(
|
||||
PermissionsExceptionMessage.NO_ROLE_FOUND_FOR_USER_WORKSPACE,
|
||||
PermissionsExceptionCode.NO_ROLE_FOUND_FOR_USER_WORKSPACE,
|
||||
);
|
||||
}
|
||||
|
||||
if (roleOfUserWorkspace.canUpdateAllSettings === true) {
|
||||
hasPermissionOnSettingFeature = true;
|
||||
}
|
||||
|
||||
const settingPermissions = roleOfUserWorkspace.settingPermissions ?? [];
|
||||
|
||||
const settingsPermissionsMap = Object.keys(SettingPermissionType).reduce(
|
||||
(acc, feature) => ({
|
||||
...acc,
|
||||
[feature]:
|
||||
hasPermissionOnSettingFeature ||
|
||||
settingPermissions.some(
|
||||
(settingPermission) => settingPermission.setting === feature,
|
||||
),
|
||||
}),
|
||||
{} as Record<SettingPermissionType, boolean>,
|
||||
);
|
||||
|
||||
const { data: rolesPermissions } =
|
||||
await this.workspacePermissionsCacheService.getRolesPermissionsFromCache({
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
const objectRecordsPermissions =
|
||||
rolesPermissions[roleOfUserWorkspace.id] ?? {};
|
||||
|
||||
return {
|
||||
settingsPermissions: settingsPermissionsMap,
|
||||
objectRecordsPermissions: objectRecordsPermissions,
|
||||
};
|
||||
}
|
||||
|
||||
public async getUserWorkspacePermissions({
|
||||
userWorkspaceId,
|
||||
workspaceId,
|
||||
|
||||
@ -247,17 +247,15 @@ export class WorkspacePermissionsCacheService {
|
||||
relations: ['objectPermissions'],
|
||||
});
|
||||
|
||||
const workspaceObjectMetadataNameIdMap =
|
||||
await this.getWorkspaceObjectMetadataNameIdMap(workspaceId);
|
||||
const workspaceObjectMetadataIds =
|
||||
await this.getWorkspaceObjectMetadataIds(workspaceId);
|
||||
|
||||
const permissionsByRoleId: ObjectRecordsPermissionsByRoleId = {};
|
||||
|
||||
for (const role of roles) {
|
||||
const objectRecordsPermissions: ObjectRecordsPermissions = {};
|
||||
|
||||
for (const objectMetadataNameSingular of Object.keys(
|
||||
workspaceObjectMetadataNameIdMap,
|
||||
)) {
|
||||
for (const objectMetadataId of workspaceObjectMetadataIds) {
|
||||
let canRead = role.canReadAllObjectRecords;
|
||||
let canUpdate = role.canUpdateAllObjectRecords;
|
||||
let canSoftDelete = role.canSoftDeleteAllObjectRecords;
|
||||
@ -266,8 +264,7 @@ export class WorkspacePermissionsCacheService {
|
||||
if (isPermissionsV2Enabled) {
|
||||
const objectRecordPermissionsOverride = role.objectPermissions.find(
|
||||
(objectPermission) =>
|
||||
objectPermission.objectMetadataId ===
|
||||
workspaceObjectMetadataNameIdMap[objectMetadataNameSingular],
|
||||
objectPermission.objectMetadataId === objectMetadataId,
|
||||
);
|
||||
|
||||
canRead =
|
||||
@ -283,7 +280,7 @@ export class WorkspacePermissionsCacheService {
|
||||
canDestroy;
|
||||
}
|
||||
|
||||
objectRecordsPermissions[objectMetadataNameSingular] = {
|
||||
objectRecordsPermissions[objectMetadataId] = {
|
||||
canRead,
|
||||
canUpdate,
|
||||
canSoftDelete,
|
||||
@ -297,53 +294,17 @@ export class WorkspacePermissionsCacheService {
|
||||
return permissionsByRoleId;
|
||||
}
|
||||
|
||||
private async getWorkspaceObjectMetadataNameIdMap(
|
||||
private async getWorkspaceObjectMetadataIds(
|
||||
workspaceId: string,
|
||||
): Promise<Record<string, string>> {
|
||||
let workspaceObjectMetadataMap: Record<string, string> = {};
|
||||
const metadataVersion =
|
||||
await this.workspaceCacheStorageService.getMetadataVersion(workspaceId);
|
||||
): Promise<string[]> {
|
||||
const workspaceObjectMetadata = await this.objectMetadataRepository.find({
|
||||
where: {
|
||||
workspaceId,
|
||||
},
|
||||
select: ['id'],
|
||||
});
|
||||
|
||||
if (metadataVersion) {
|
||||
const objectMetadataMaps =
|
||||
await this.workspaceCacheStorageService.getObjectMetadataMaps(
|
||||
workspaceId,
|
||||
metadataVersion,
|
||||
);
|
||||
|
||||
workspaceObjectMetadataMap = Object.values(
|
||||
objectMetadataMaps?.byId ?? {},
|
||||
).reduce(
|
||||
(acc, objectMetadata) => {
|
||||
acc[objectMetadata.nameSingular] = objectMetadata.id;
|
||||
|
||||
return acc;
|
||||
},
|
||||
{} as Record<string, string>,
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
!metadataVersion ||
|
||||
Object.keys(workspaceObjectMetadataMap).length === 0
|
||||
) {
|
||||
const workspaceObjectMetadata = await this.objectMetadataRepository.find({
|
||||
where: {
|
||||
workspaceId,
|
||||
},
|
||||
});
|
||||
|
||||
workspaceObjectMetadataMap = workspaceObjectMetadata.reduce(
|
||||
(acc, objectMetadata) => {
|
||||
acc[objectMetadata.nameSingular] = objectMetadata.id;
|
||||
|
||||
return acc;
|
||||
},
|
||||
{} as Record<string, string>,
|
||||
);
|
||||
}
|
||||
|
||||
return workspaceObjectMetadataMap;
|
||||
return workspaceObjectMetadata.map((objectMetadata) => objectMetadata.id);
|
||||
}
|
||||
|
||||
private async getUserWorkspaceRoleMapFromDatabase({
|
||||
|
||||
Reference in New Issue
Block a user