diff --git a/packages/twenty-front/src/modules/auth/states/currentUserWorkspaceState.ts b/packages/twenty-front/src/modules/auth/states/currentUserWorkspaceState.ts index 9beaa69130..341485659e 100644 --- a/packages/twenty-front/src/modules/auth/states/currentUserWorkspaceState.ts +++ b/packages/twenty-front/src/modules/auth/states/currentUserWorkspaceState.ts @@ -4,9 +4,7 @@ import { type UserWorkspace } from '~/generated/graphql'; export type CurrentUserWorkspace = Pick< UserWorkspace, - | 'permissionFlags' - | 'twoFactorAuthenticationMethodSummary' - | 'objectRecordsPermissions' + 'permissionFlags' | 'twoFactorAuthenticationMethodSummary' > & { objectPermissions: Array; }; diff --git a/packages/twenty-front/src/modules/object-record/record-field-list/record-detail-section/relation/components/RecordDetailRelationSectionDropdownToMany.tsx b/packages/twenty-front/src/modules/object-record/record-field-list/record-detail-section/relation/components/RecordDetailRelationSectionDropdownToMany.tsx index 3f2bb9456b..01cac9cfcb 100644 --- a/packages/twenty-front/src/modules/object-record/record-field-list/record-detail-section/relation/components/RecordDetailRelationSectionDropdownToMany.tsx +++ b/packages/twenty-front/src/modules/object-record/record-field-list/record-detail-section/relation/components/RecordDetailRelationSectionDropdownToMany.tsx @@ -146,6 +146,7 @@ export const RecordDetailRelationSectionDropdownToMany = () => { focusId={dropdownId} componentInstanceId={dropdownId} onCreate={handleCreateNew} + objectMetadataItemIdForCreate={relationObjectMetadataItem.id} onChange={updateRelation} onSubmit={() => { closeDropdown(dropdownId); diff --git a/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/input/components/RelationFromManyFieldInput.tsx b/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/input/components/RelationFromManyFieldInput.tsx index 8d15e04465..a0de03bef2 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/input/components/RelationFromManyFieldInput.tsx +++ b/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/input/components/RelationFromManyFieldInput.tsx @@ -153,6 +153,7 @@ export const RelationFromManyFieldInput = () => { } }} onCreate={canCreateNew ? handleCreateNew : undefined} + objectMetadataItemIdForCreate={relationObjectMetadataItem.id} onClickOutside={handleSubmit} layoutDirection={ layoutDirection === 'downward' diff --git a/packages/twenty-front/src/modules/object-record/record-picker/multiple-record-picker/components/MultipleRecordPicker.tsx b/packages/twenty-front/src/modules/object-record/record-picker/multiple-record-picker/components/MultipleRecordPicker.tsx index a31ead0107..803d6cd309 100644 --- a/packages/twenty-front/src/modules/object-record/record-picker/multiple-record-picker/components/MultipleRecordPicker.tsx +++ b/packages/twenty-front/src/modules/object-record/record-picker/multiple-record-picker/components/MultipleRecordPicker.tsx @@ -1,3 +1,4 @@ +import { useObjectPermissionsForObject } from '@/object-record/hooks/useObjectPermissionsForObject'; import { MultipleRecordPickerItemsDisplay } from '@/object-record/record-picker/multiple-record-picker/components/MultipleRecordPickerItemsDisplay'; import { MultipleRecordPickerOnClickOutsideEffect } from '@/object-record/record-picker/multiple-record-picker/components/MultipleRecordPickerOnClickOutsideEffect'; import { MultipleRecordPickerSearchInput } from '@/object-record/record-picker/multiple-record-picker/components/MultipleRecordPickerSearchInput'; @@ -7,7 +8,6 @@ import { multipleRecordPickerSearchFilterComponentState } from '@/object-record/ import { getMultipleRecordPickerSelectableListId } from '@/object-record/record-picker/multiple-record-picker/utils/getMultipleRecordPickerSelectableListId'; import { type RecordPickerLayoutDirection } from '@/object-record/record-picker/types/RecordPickerLayoutDirection'; import { type RecordPickerPickableMorphItem } from '@/object-record/record-picker/types/RecordPickerPickableMorphItem'; -import { useHasObjectReadOnlyPermission } from '@/settings/roles/hooks/useHasObjectReadOnlyPermission'; import { CreateNewButton } from '@/ui/input/relation-picker/components/CreateNewButton'; import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent'; import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; @@ -28,6 +28,7 @@ type MultipleRecordPickerProps = { componentInstanceId: string; onClickOutside: () => void; focusId: string; + objectMetadataItemIdForCreate?: string; }; export const MultipleRecordPicker = ({ @@ -38,6 +39,7 @@ export const MultipleRecordPicker = ({ layoutDirection = 'search-bar-on-bottom', componentInstanceId, focusId, + objectMetadataItemIdForCreate, }: MultipleRecordPickerProps) => { const selectableListComponentInstanceId = getMultipleRecordPickerSelectableListId(componentInstanceId); @@ -57,8 +59,6 @@ export const MultipleRecordPicker = ({ componentInstanceId, ); - const hasObjectReadOnlyPermission = useHasObjectReadOnlyPermission(); - const resetState = useRecoilCallback( ({ set }) => { return () => { @@ -106,8 +106,12 @@ export const MultipleRecordPicker = ({ [multipleRecordPickerSearchFilterState, onCreate], ); + const hasCreatePermissionOnObjectForCreate = useObjectPermissionsForObject( + objectMetadataItemIdForCreate ?? '', + ).canUpdateObjectRecords; + const createNewButtonSection = - isDefined(onCreate) && !hasObjectReadOnlyPermission ? ( + isDefined(onCreate) && hasCreatePermissionOnObjectForCreate ? ( { - const currentUserWorkspace = useRecoilValue(currentUserWorkspaceState); - - if (!isDefined(currentUserWorkspace?.objectRecordsPermissions)) { - return false; - } - - if (currentUserWorkspace?.objectRecordsPermissions.length === 0) { - return true; - } - - return ( - currentUserWorkspace?.objectRecordsPermissions.length === 1 && - currentUserWorkspace?.objectRecordsPermissions.includes( - PermissionsOnAllObjectRecords.READ_ALL_OBJECT_RECORDS, - ) - ); -}; diff --git a/packages/twenty-server/src/engine/metadata-modules/object-permission/field-permission/__tests__/field-permissions.service.spec.ts b/packages/twenty-server/src/engine/metadata-modules/object-permission/field-permission/__tests__/field-permissions.service.spec.ts index a578c8c606..53b8cf95b0 100644 --- a/packages/twenty-server/src/engine/metadata-modules/object-permission/field-permission/__tests__/field-permissions.service.spec.ts +++ b/packages/twenty-server/src/engine/metadata-modules/object-permission/field-permission/__tests__/field-permissions.service.spec.ts @@ -60,6 +60,10 @@ describe('FieldPermissionService', () => { canUpdate: true, canSoftDelete: false, canDestroy: false, + canReadObjectRecords: true, + canUpdateObjectRecords: true, + canSoftDeleteObjectRecords: false, + canDestroyObjectRecords: false, restrictedFields: {}, }, [fieldRelationMock.objectMetadataId]: { @@ -67,6 +71,10 @@ describe('FieldPermissionService', () => { canUpdate: true, canSoftDelete: false, canDestroy: false, + canReadObjectRecords: true, + canUpdateObjectRecords: true, + canSoftDeleteObjectRecords: false, + canDestroyObjectRecords: false, restrictedFields: {}, }, }, diff --git a/packages/twenty-server/src/engine/metadata-modules/permissions/permissions.service.ts b/packages/twenty-server/src/engine/metadata-modules/permissions/permissions.service.ts index 8867fccc42..8b1884eabb 100644 --- a/packages/twenty-server/src/engine/metadata-modules/permissions/permissions.service.ts +++ b/packages/twenty-server/src/engine/metadata-modules/permissions/permissions.service.ts @@ -86,6 +86,7 @@ export class PermissionsService { }); const objectPermissions = rolesPermissions[roleOfUserWorkspace.id] ?? {}; + const objectsPermissions = rolesPermissions[roleOfUserWorkspace.id] ?? {}; const objectRecordsPermissions: UserWorkspacePermissions['objectRecordsPermissions'] = { @@ -103,6 +104,7 @@ export class PermissionsService { permissionFlags, objectRecordsPermissions, objectPermissions, + objectsPermissions, }; } @@ -128,6 +130,7 @@ export class PermissionsService { [PermissionFlagType.EXPORT_CSV]: false, }, objectPermissions: {}, + objectsPermissions: {}, }) as const satisfies UserWorkspacePermissions; public async userHasWorkspaceSettingPermission({ diff --git a/packages/twenty-server/src/engine/metadata-modules/permissions/types/user-workspace-permissions.ts b/packages/twenty-server/src/engine/metadata-modules/permissions/types/user-workspace-permissions.ts index da462b24b1..7366c9ce08 100644 --- a/packages/twenty-server/src/engine/metadata-modules/permissions/types/user-workspace-permissions.ts +++ b/packages/twenty-server/src/engine/metadata-modules/permissions/types/user-workspace-permissions.ts @@ -1,5 +1,8 @@ import { type PermissionsOnAllObjectRecords } from 'twenty-shared/constants'; -import { type ObjectsPermissionsDeprecated } from 'twenty-shared/types'; +import { + type ObjectsPermissions, + type ObjectsPermissionsDeprecated, +} from 'twenty-shared/types'; import { type PermissionFlagType } from 'src/engine/metadata-modules/permissions/constants/permission-flag-type.constants'; @@ -7,4 +10,5 @@ export type UserWorkspacePermissions = { permissionFlags: Record; objectRecordsPermissions: Record; objectPermissions: ObjectsPermissionsDeprecated; + objectsPermissions: ObjectsPermissions; // replaces ObjectsPermissionsDeprecated - deprecation ongoing }; diff --git a/packages/twenty-server/src/engine/metadata-modules/workspace-permissions-cache/workspace-permissions-cache.service.ts b/packages/twenty-server/src/engine/metadata-modules/workspace-permissions-cache/workspace-permissions-cache.service.ts index f0a01348ff..26ccb42c43 100644 --- a/packages/twenty-server/src/engine/metadata-modules/workspace-permissions-cache/workspace-permissions-cache.service.ts +++ b/packages/twenty-server/src/engine/metadata-modules/workspace-permissions-cache/workspace-permissions-cache.service.ts @@ -2,6 +2,7 @@ import { Injectable, Logger } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { + ObjectsPermissions, type ObjectsPermissionsByRoleIdDeprecated, type ObjectsPermissionsDeprecated, type RestrictedFieldsPermissions, @@ -190,7 +191,8 @@ export class WorkspacePermissionsCacheService { const permissionsByRoleId: ObjectsPermissionsByRoleIdDeprecated = {}; for (const role of roles) { - const objectRecordsPermissions: ObjectsPermissionsDeprecated = {}; + const objectRecordsPermissions: ObjectsPermissionsDeprecated & + ObjectsPermissions = {}; for (const objectMetadata of workspaceObjectMetadataCollection) { const { id: objectMetadataId, isSystem, standardId } = objectMetadata; @@ -270,6 +272,10 @@ export class WorkspacePermissionsCacheService { canUpdate, canSoftDelete, canDestroy, + canReadObjectRecords: canRead, + canUpdateObjectRecords: canUpdate, + canSoftDeleteObjectRecords: canSoftDelete, + canDestroyObjectRecords: canDestroy, restrictedFields, }; diff --git a/packages/twenty-server/test/integration/metadata/suites/agent/agent-tool.service.integration-spec.ts b/packages/twenty-server/test/integration/metadata/suites/agent/agent-tool.service.integration-spec.ts index 669468e449..c33f683be2 100644 --- a/packages/twenty-server/test/integration/metadata/suites/agent/agent-tool.service.integration-spec.ts +++ b/packages/twenty-server/test/integration/metadata/suites/agent/agent-tool.service.integration-spec.ts @@ -47,6 +47,10 @@ describe('AgentToolGeneratorService Integration', () => { canUpdate: true, canSoftDelete: true, canDestroy: true, + canReadObjectRecords: true, + canUpdateObjectRecords: true, + canSoftDeleteObjectRecords: true, + canDestroyObjectRecords: true, restrictedFields: {}, }, }, @@ -93,6 +97,10 @@ describe('AgentToolGeneratorService Integration', () => { canUpdate: false, canSoftDelete: false, canDestroy: false, + canReadObjectRecords: true, + canUpdateObjectRecords: false, + canSoftDeleteObjectRecords: false, + canDestroyObjectRecords: false, restrictedFields: {}, }, }, @@ -158,6 +166,10 @@ describe('AgentToolGeneratorService Integration', () => { canUpdate: true, canSoftDelete: true, canDestroy: false, + canReadObjectRecords: true, + canUpdateObjectRecords: true, + canSoftDeleteObjectRecords: true, + canDestroyObjectRecords: false, restrictedFields: {}, }, }, @@ -766,6 +778,10 @@ describe('AgentToolGeneratorService Integration', () => { canUpdate: true, canSoftDelete: false, canDestroy: false, + canReadObjectRecords: true, + canUpdateObjectRecords: true, + canSoftDeleteObjectRecords: false, + canDestroyObjectRecords: false, restrictedFields: {}, }, [secondObjectMetadata.id]: { @@ -773,6 +789,10 @@ describe('AgentToolGeneratorService Integration', () => { canUpdate: false, canSoftDelete: true, canDestroy: false, + canReadObjectRecords: true, + canUpdateObjectRecords: false, + canSoftDeleteObjectRecords: true, + canDestroyObjectRecords: false, restrictedFields: {}, }, }, diff --git a/packages/twenty-server/test/integration/metadata/suites/agent/utils/agent-tool-test-utils.ts b/packages/twenty-server/test/integration/metadata/suites/agent/utils/agent-tool-test-utils.ts index 72231f83a8..4f7b266fd9 100644 --- a/packages/twenty-server/test/integration/metadata/suites/agent/utils/agent-tool-test-utils.ts +++ b/packages/twenty-server/test/integration/metadata/suites/agent/utils/agent-tool-test-utils.ts @@ -306,6 +306,10 @@ export const setupBasicPermissions = (context: AgentToolTestContext) => { canUpdate: true, canSoftDelete: true, canDestroy: false, + canReadObjectRecords: true, + canUpdateObjectRecords: true, + canSoftDeleteObjectRecords: true, + canDestroyObjectRecords: false, restrictedFields: {}, }, }, diff --git a/packages/twenty-shared/src/types/ObjectsPermissionsByRoleIdDeprecated.ts b/packages/twenty-shared/src/types/ObjectsPermissionsByRoleIdDeprecated.ts index 35e5a61603..f58805ee3b 100644 --- a/packages/twenty-shared/src/types/ObjectsPermissionsByRoleIdDeprecated.ts +++ b/packages/twenty-shared/src/types/ObjectsPermissionsByRoleIdDeprecated.ts @@ -1,3 +1,4 @@ +import { type ObjectsPermissions } from '@/types/ObjectsPermissions'; import { type ObjectsPermissionsDeprecated } from './ObjectsPermissionsDeprecated'; type RoleId = string; @@ -5,5 +6,5 @@ type RoleId = string; // TODO: DEPRECATE THIS export type ObjectsPermissionsByRoleIdDeprecated = Record< RoleId, - ObjectsPermissionsDeprecated + ObjectsPermissionsDeprecated & ObjectsPermissions >;