## 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:
@ -1525,9 +1525,7 @@ export type ObjectPermission = {
|
||||
canReadObjectRecords?: Maybe<Scalars['Boolean']['output']>;
|
||||
canSoftDeleteObjectRecords?: Maybe<Scalars['Boolean']['output']>;
|
||||
canUpdateObjectRecords?: Maybe<Scalars['Boolean']['output']>;
|
||||
id: Scalars['String']['output'];
|
||||
objectMetadataId: Scalars['String']['output'];
|
||||
roleId: Scalars['String']['output'];
|
||||
};
|
||||
|
||||
export type ObjectPermissionInput = {
|
||||
@ -2543,6 +2541,8 @@ export type UserWorkspace = {
|
||||
createdAt: Scalars['DateTime']['output'];
|
||||
deletedAt?: Maybe<Scalars['DateTime']['output']>;
|
||||
id: Scalars['UUID']['output'];
|
||||
objectPermissions?: Maybe<Array<ObjectPermission>>;
|
||||
/** @deprecated Use objectPermissions instead */
|
||||
objectRecordsPermissions?: Maybe<Array<PermissionsOnAllObjectRecords>>;
|
||||
settingsPermissions?: Maybe<Array<SettingPermissionType>>;
|
||||
updatedAt: Scalars['DateTime']['output'];
|
||||
|
||||
@ -1391,9 +1391,7 @@ export type ObjectPermission = {
|
||||
canReadObjectRecords?: Maybe<Scalars['Boolean']>;
|
||||
canSoftDeleteObjectRecords?: Maybe<Scalars['Boolean']>;
|
||||
canUpdateObjectRecords?: Maybe<Scalars['Boolean']>;
|
||||
id: Scalars['String'];
|
||||
objectMetadataId: Scalars['String'];
|
||||
roleId: Scalars['String'];
|
||||
};
|
||||
|
||||
export type ObjectPermissionInput = {
|
||||
@ -2320,6 +2318,8 @@ export type UserWorkspace = {
|
||||
createdAt: Scalars['DateTime'];
|
||||
deletedAt?: Maybe<Scalars['DateTime']>;
|
||||
id: Scalars['UUID'];
|
||||
objectPermissions?: Maybe<Array<ObjectPermission>>;
|
||||
/** @deprecated Use objectPermissions instead */
|
||||
objectRecordsPermissions?: Maybe<Array<PermissionsOnAllObjectRecords>>;
|
||||
settingsPermissions?: Maybe<Array<SettingPermissionType>>;
|
||||
updatedAt: Scalars['DateTime'];
|
||||
@ -2854,7 +2854,7 @@ export type UpdateLabPublicFeatureFlagMutationVariables = Exact<{
|
||||
|
||||
export type UpdateLabPublicFeatureFlagMutation = { __typename?: 'Mutation', updateLabPublicFeatureFlag: { __typename?: 'FeatureFlagDTO', key: FeatureFlagKey, value: boolean } };
|
||||
|
||||
export type ObjectPermissionFragmentFragment = { __typename?: 'ObjectPermission', id: string, objectMetadataId: string, roleId: string, canReadObjectRecords?: boolean | null, canUpdateObjectRecords?: boolean | null, canSoftDeleteObjectRecords?: boolean | null, canDestroyObjectRecords?: boolean | null };
|
||||
export type ObjectPermissionFragmentFragment = { __typename?: 'ObjectPermission', objectMetadataId: string, canReadObjectRecords?: boolean | null, canUpdateObjectRecords?: boolean | null, canSoftDeleteObjectRecords?: boolean | null, canDestroyObjectRecords?: boolean | null };
|
||||
|
||||
export type RoleFragmentFragment = { __typename?: 'Role', id: string, label: string, description?: string | null, icon?: string | null, canUpdateAllSettings: boolean, isEditable: boolean, canReadAllObjectRecords: boolean, canUpdateAllObjectRecords: boolean, canSoftDeleteAllObjectRecords: boolean, canDestroyAllObjectRecords: boolean };
|
||||
|
||||
@ -2887,7 +2887,7 @@ export type UpsertObjectPermissionsMutationVariables = Exact<{
|
||||
}>;
|
||||
|
||||
|
||||
export type UpsertObjectPermissionsMutation = { __typename?: 'Mutation', upsertObjectPermissions: Array<{ __typename?: 'ObjectPermission', id: string, objectMetadataId: string, roleId: string, canReadObjectRecords?: boolean | null, canUpdateObjectRecords?: boolean | null, canSoftDeleteObjectRecords?: boolean | null, canDestroyObjectRecords?: boolean | null }> };
|
||||
export type UpsertObjectPermissionsMutation = { __typename?: 'Mutation', upsertObjectPermissions: Array<{ __typename?: 'ObjectPermission', objectMetadataId: string, canReadObjectRecords?: boolean | null, canUpdateObjectRecords?: boolean | null, canSoftDeleteObjectRecords?: boolean | null, canDestroyObjectRecords?: boolean | null }> };
|
||||
|
||||
export type UpsertSettingPermissionsMutationVariables = Exact<{
|
||||
upsertSettingPermissionsInput: UpsertSettingPermissionsInput;
|
||||
@ -2899,7 +2899,7 @@ export type UpsertSettingPermissionsMutation = { __typename?: 'Mutation', upsert
|
||||
export type GetRolesQueryVariables = Exact<{ [key: string]: never; }>;
|
||||
|
||||
|
||||
export type GetRolesQuery = { __typename?: 'Query', getRoles: Array<{ __typename?: 'Role', id: string, label: string, description?: string | null, icon?: string | null, canUpdateAllSettings: boolean, isEditable: boolean, canReadAllObjectRecords: boolean, canUpdateAllObjectRecords: boolean, canSoftDeleteAllObjectRecords: boolean, canDestroyAllObjectRecords: boolean, workspaceMembers: Array<{ __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale?: string | null, userEmail: string, timeZone?: string | null, dateFormat?: WorkspaceMemberDateFormatEnum | null, timeFormat?: WorkspaceMemberTimeFormatEnum | null, name: { __typename?: 'FullName', firstName: string, lastName: string } }>, settingPermissions?: Array<{ __typename?: 'SettingPermission', id: string, setting: SettingPermissionType, roleId: string }> | null, objectPermissions?: Array<{ __typename?: 'ObjectPermission', id: string, objectMetadataId: string, roleId: string, canReadObjectRecords?: boolean | null, canUpdateObjectRecords?: boolean | null, canSoftDeleteObjectRecords?: boolean | null, canDestroyObjectRecords?: boolean | null }> | null }> };
|
||||
export type GetRolesQuery = { __typename?: 'Query', getRoles: Array<{ __typename?: 'Role', id: string, label: string, description?: string | null, icon?: string | null, canUpdateAllSettings: boolean, isEditable: boolean, canReadAllObjectRecords: boolean, canUpdateAllObjectRecords: boolean, canSoftDeleteAllObjectRecords: boolean, canDestroyAllObjectRecords: boolean, workspaceMembers: Array<{ __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale?: string | null, userEmail: string, timeZone?: string | null, dateFormat?: WorkspaceMemberDateFormatEnum | null, timeFormat?: WorkspaceMemberTimeFormatEnum | null, name: { __typename?: 'FullName', firstName: string, lastName: string } }>, settingPermissions?: Array<{ __typename?: 'SettingPermission', id: string, setting: SettingPermissionType, roleId: string }> | null, objectPermissions?: Array<{ __typename?: 'ObjectPermission', objectMetadataId: string, canReadObjectRecords?: boolean | null, canUpdateObjectRecords?: boolean | null, canSoftDeleteObjectRecords?: boolean | null, canDestroyObjectRecords?: boolean | null }> | null }> };
|
||||
|
||||
export type CreateApprovedAccessDomainMutationVariables = Exact<{
|
||||
input: CreateApprovedAccessDomainInput;
|
||||
@ -3225,9 +3225,7 @@ export const AvailableSsoIdentityProvidersFragmentFragmentDoc = gql`
|
||||
`;
|
||||
export const ObjectPermissionFragmentFragmentDoc = gql`
|
||||
fragment ObjectPermissionFragment on ObjectPermission {
|
||||
id
|
||||
objectMetadataId
|
||||
roleId
|
||||
canReadObjectRecords
|
||||
canUpdateObjectRecords
|
||||
canSoftDeleteObjectRecords
|
||||
|
||||
@ -2,9 +2,7 @@ import { gql } from '@apollo/client';
|
||||
|
||||
export const OBJECT_PERMISSION_FRAGMENT = gql`
|
||||
fragment ObjectPermissionFragment on ObjectPermission {
|
||||
id
|
||||
objectMetadataId
|
||||
roleId
|
||||
canReadObjectRecords
|
||||
canUpdateObjectRecords
|
||||
canSoftDeleteObjectRecords
|
||||
|
||||
@ -36,15 +36,17 @@ const StyledSettingsRolePermissionsObjectLevelOverrideCell = styled.div`
|
||||
|
||||
type SettingsRolePermissionsObjectLevelOverrideCellProps = {
|
||||
objectPermission: ObjectPermission;
|
||||
roleId: string;
|
||||
};
|
||||
|
||||
export const SettingsRolePermissionsObjectLevelOverrideCell = ({
|
||||
objectPermission,
|
||||
roleId,
|
||||
}: SettingsRolePermissionsObjectLevelOverrideCellProps) => {
|
||||
const theme = useTheme();
|
||||
|
||||
const settingsDraftRole = useRecoilValue(
|
||||
settingsDraftRoleFamilyState(objectPermission.roleId),
|
||||
settingsDraftRoleFamilyState(roleId),
|
||||
);
|
||||
|
||||
const permissionMappings =
|
||||
|
||||
@ -15,7 +15,6 @@ import { isDefined } from 'twenty-shared/utils';
|
||||
import { H2Title, IconPlus } from 'twenty-ui/display';
|
||||
import { Button } from 'twenty-ui/input';
|
||||
import { Section } from 'twenty-ui/layout';
|
||||
import { v4 } from 'uuid';
|
||||
import { useNavigateSettings } from '~/hooks/useNavigateSettings';
|
||||
|
||||
const StyledCreateObjectOverrideSection = styled(Section)`
|
||||
@ -81,11 +80,9 @@ export const SettingsRolePermissionsObjectLevelSection = ({
|
||||
...draftRole,
|
||||
objectPermissions: [
|
||||
...(draftRole.objectPermissions ?? []).filter(
|
||||
(permission) =>
|
||||
permission.objectMetadataId !== objectMetadataId ||
|
||||
permission.roleId !== roleId,
|
||||
(permission) => permission.objectMetadataId !== objectMetadataId,
|
||||
),
|
||||
{ objectMetadataId, roleId, id: v4() },
|
||||
{ objectMetadataId, roleId },
|
||||
],
|
||||
}));
|
||||
navigate(SettingsPath.RoleObjectLevel, {
|
||||
@ -107,11 +104,12 @@ export const SettingsRolePermissionsObjectLevelSection = ({
|
||||
filteredObjectPermissions?.length > 0 ? (
|
||||
filteredObjectPermissions?.map((objectPermission) => (
|
||||
<SettingsRolePermissionsObjectLevelTableRow
|
||||
key={objectPermission.id}
|
||||
key={objectPermission.objectMetadataId}
|
||||
objectPermission={objectPermission}
|
||||
objectMetadataItem={
|
||||
objectMetadataMap[objectPermission.objectMetadataId]
|
||||
}
|
||||
roleId={roleId}
|
||||
/>
|
||||
))
|
||||
) : (
|
||||
|
||||
@ -27,11 +27,13 @@ const StyledNameLabel = styled.div`
|
||||
type SettingsRolePermissionsObjectLevelTableRowProps = {
|
||||
objectPermission: ObjectPermission;
|
||||
objectMetadataItem: ObjectMetadataItem;
|
||||
roleId: string;
|
||||
};
|
||||
|
||||
export const SettingsRolePermissionsObjectLevelTableRow = ({
|
||||
objectPermission,
|
||||
objectMetadataItem,
|
||||
roleId,
|
||||
}: SettingsRolePermissionsObjectLevelTableRowProps) => {
|
||||
const { getIcon } = useIcons();
|
||||
const theme = useTheme();
|
||||
@ -45,7 +47,7 @@ export const SettingsRolePermissionsObjectLevelTableRow = ({
|
||||
return (
|
||||
<TableRow
|
||||
to={getSettingsPath(SettingsPath.RoleObjectLevel, {
|
||||
roleId: objectPermission.roleId,
|
||||
roleId: roleId,
|
||||
objectMetadataId: objectPermission.objectMetadataId,
|
||||
})}
|
||||
gridAutoColumns="180px 1fr 1fr"
|
||||
@ -65,6 +67,7 @@ export const SettingsRolePermissionsObjectLevelTableRow = ({
|
||||
<TableCell>
|
||||
<SettingsRolePermissionsObjectLevelOverrideCell
|
||||
objectPermission={objectPermission}
|
||||
roleId={roleId}
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell align={'right'}>
|
||||
|
||||
@ -20,6 +20,7 @@ import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/
|
||||
import { TwoFactorMethod } from 'src/engine/core-modules/two-factor-method/two-factor-method.entity';
|
||||
import { User } from 'src/engine/core-modules/user/user.entity';
|
||||
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
import { ObjectPermissionDTO } from 'src/engine/metadata-modules/object-permission/dtos/object-permission.dto';
|
||||
import { SettingPermissionType } from 'src/engine/metadata-modules/permissions/constants/setting-permission-type.constants';
|
||||
|
||||
registerEnumType(SettingPermissionType, {
|
||||
@ -84,6 +85,12 @@ export class UserWorkspace {
|
||||
@Field(() => [SettingPermissionType], { nullable: true })
|
||||
settingsPermissions?: SettingPermissionType[];
|
||||
|
||||
@Field(() => [PermissionsOnAllObjectRecords], { nullable: true })
|
||||
@Field(() => [PermissionsOnAllObjectRecords], {
|
||||
nullable: true,
|
||||
deprecationReason: 'Use objectPermissions instead',
|
||||
})
|
||||
objectRecordsPermissions?: PermissionsOnAllObjectRecords[];
|
||||
|
||||
@Field(() => [ObjectPermissionDTO], { nullable: true })
|
||||
objectPermissions?: ObjectPermissionDTO[];
|
||||
}
|
||||
|
||||
@ -26,8 +26,12 @@ import {
|
||||
AuthExceptionCode,
|
||||
} from 'src/engine/core-modules/auth/auth.exception';
|
||||
import { DomainManagerService } from 'src/engine/core-modules/domain-manager/services/domain-manager.service';
|
||||
import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum';
|
||||
import { FeatureFlagService } from 'src/engine/core-modules/feature-flag/services/feature-flag.service';
|
||||
import { SignedFileDTO } from 'src/engine/core-modules/file/file-upload/dtos/signed-file.dto';
|
||||
import { FileUploadService } from 'src/engine/core-modules/file/file-upload/services/file-upload.service';
|
||||
import { FileService } from 'src/engine/core-modules/file/services/file.service';
|
||||
import { extractFilenameFromPath } from 'src/engine/core-modules/file/utils/extract-file-id-from-path.utils';
|
||||
import { OnboardingStatus } from 'src/engine/core-modules/onboarding/enums/onboarding-status.enum';
|
||||
import {
|
||||
OnboardingService,
|
||||
@ -46,6 +50,7 @@ import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
import { AuthUser } from 'src/engine/decorators/auth/auth-user.decorator';
|
||||
import { AuthWorkspace } from 'src/engine/decorators/auth/auth-workspace.decorator';
|
||||
import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard';
|
||||
import { ObjectPermissionDTO } from 'src/engine/metadata-modules/object-permission/dtos/object-permission.dto';
|
||||
import { SettingPermissionType } from 'src/engine/metadata-modules/permissions/constants/setting-permission-type.constants';
|
||||
import { PermissionsService } from 'src/engine/metadata-modules/permissions/permissions.service';
|
||||
import { PermissionsGraphqlApiExceptionFilter } from 'src/engine/metadata-modules/permissions/utils/permissions-graphql-api-exception.filter';
|
||||
@ -53,8 +58,6 @@ import { RoleDTO } from 'src/engine/metadata-modules/role/dtos/role.dto';
|
||||
import { UserRoleService } from 'src/engine/metadata-modules/user-role/user-role.service';
|
||||
import { AccountsToReconnectKeys } from 'src/modules/connected-account/types/accounts-to-reconnect-key-value.type';
|
||||
import { streamToBuffer } from 'src/utils/stream-to-buffer';
|
||||
import { SignedFileDTO } from 'src/engine/core-modules/file/file-upload/dtos/signed-file.dto';
|
||||
import { extractFilenameFromPath } from 'src/engine/core-modules/file/utils/extract-file-id-from-path.utils';
|
||||
|
||||
const getHMACKey = (email?: string, key?: string | null) => {
|
||||
if (!email || !key) return null;
|
||||
@ -83,6 +86,7 @@ export class UserResolver {
|
||||
private readonly userRoleService: UserRoleService,
|
||||
private readonly permissionsService: PermissionsService,
|
||||
private readonly deletedWorkspaceMemberTranspiler: DeletedWorkspaceMemberTranspiler,
|
||||
private readonly featureFlagService: FeatureFlagService,
|
||||
) {}
|
||||
|
||||
@Query(() => User)
|
||||
@ -111,6 +115,7 @@ export class UserResolver {
|
||||
}
|
||||
let settingsPermissions = {};
|
||||
let objectRecordsPermissions = {};
|
||||
let objectPermissions: ObjectPermissionDTO[] = [];
|
||||
|
||||
if (
|
||||
![
|
||||
@ -118,14 +123,40 @@ export class UserResolver {
|
||||
WorkspaceActivationStatus.ONGOING_CREATION,
|
||||
].includes(workspace.activationStatus)
|
||||
) {
|
||||
const permissions =
|
||||
await this.permissionsService.getUserWorkspacePermissions({
|
||||
userWorkspaceId: currentUserWorkspace.id,
|
||||
workspaceId: workspace.id,
|
||||
});
|
||||
const isPermissionsV2Enabled =
|
||||
await this.featureFlagService.isFeatureEnabled(
|
||||
FeatureFlagKey.IsPermissionsV2Enabled,
|
||||
workspace.id,
|
||||
);
|
||||
|
||||
settingsPermissions = permissions.settingsPermissions;
|
||||
objectRecordsPermissions = permissions.objectRecordsPermissions;
|
||||
if (isPermissionsV2Enabled) {
|
||||
const permissions =
|
||||
await this.permissionsService.getUserWorkspacePermissionsV2({
|
||||
userWorkspaceId: currentUserWorkspace.id,
|
||||
workspaceId: workspace.id,
|
||||
});
|
||||
|
||||
settingsPermissions = permissions.settingsPermissions;
|
||||
objectPermissions = Object.entries(
|
||||
permissions.objectRecordsPermissions,
|
||||
).map(([objectMetadataId, permissions]) => ({
|
||||
objectMetadataId,
|
||||
canReadObjectRecords: permissions.canRead,
|
||||
canUpdateObjectRecords: permissions.canUpdate,
|
||||
canSoftDeleteObjectRecords: permissions.canSoftDelete,
|
||||
canDestroyObjectRecords: permissions.canDestroy,
|
||||
}));
|
||||
objectRecordsPermissions = permissions.objectRecordsPermissions;
|
||||
} else {
|
||||
const permissions =
|
||||
await this.permissionsService.getUserWorkspacePermissions({
|
||||
userWorkspaceId: currentUserWorkspace.id,
|
||||
workspaceId: workspace.id,
|
||||
});
|
||||
|
||||
settingsPermissions = permissions.settingsPermissions;
|
||||
objectRecordsPermissions = permissions.objectRecordsPermissions;
|
||||
}
|
||||
}
|
||||
|
||||
const grantedSettingsPermissions: SettingPermissionType[] = (
|
||||
@ -143,6 +174,7 @@ export class UserResolver {
|
||||
currentUserWorkspace.settingsPermissions = grantedSettingsPermissions;
|
||||
currentUserWorkspace.objectRecordsPermissions =
|
||||
grantedObjectRecordsPermissions;
|
||||
currentUserWorkspace.objectPermissions = objectPermissions;
|
||||
user.currentUserWorkspace = currentUserWorkspace;
|
||||
|
||||
return {
|
||||
|
||||
@ -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({
|
||||
|
||||
@ -47,7 +47,8 @@ export const validateOperationIsPermittedOrThrow = ({
|
||||
return;
|
||||
}
|
||||
|
||||
const permissionsForEntity = objectRecordsPermissions[entityName];
|
||||
const permissionsForEntity =
|
||||
objectRecordsPermissions[objectMetadataIdForEntity];
|
||||
|
||||
switch (operationType) {
|
||||
case 'select':
|
||||
|
||||
@ -478,8 +478,6 @@ describe('roles permissions', () => {
|
||||
}) => `
|
||||
mutation UpsertObjectPermissions {
|
||||
upsertObjectPermissions(upsertObjectPermissionsInput: { roleId: "${roleId}", objectPermissions: [{objectMetadataId: "${objectMetadataId}", canUpdateObjectRecords: true}]}) {
|
||||
id
|
||||
roleId
|
||||
objectMetadataId
|
||||
canUpdateObjectRecords
|
||||
}
|
||||
@ -541,7 +539,6 @@ describe('roles permissions', () => {
|
||||
expect(res.body.data.upsertObjectPermissions).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
roleId: createdEditableRoleId,
|
||||
objectMetadataId: listingObjectId,
|
||||
canUpdateObjectRecords: true,
|
||||
}),
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
export type ObjectRecordsPermissions = {
|
||||
[objectName: string]: {
|
||||
[objectMetadataId: string]: {
|
||||
canRead: boolean;
|
||||
canUpdate: boolean;
|
||||
canSoftDelete: boolean;
|
||||
|
||||
Reference in New Issue
Block a user