Handle restricted objects #1 refactor permissions map + return object permissions from gql (#12313)

## 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:
Weiko
2025-05-27 17:42:26 +02:00
committed by GitHub
parent 651ad38e79
commit 8051646567
14 changed files with 143 additions and 93 deletions

View File

@ -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'];

View File

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

View File

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

View File

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

View File

@ -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}
/>
))
) : (

View File

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