[permissions V2] Upsert object and setting permissions (#11119)

Closes https://github.com/twentyhq/core-team-issues/issues/639
This commit is contained in:
Marie
2025-03-25 11:07:51 +01:00
committed by GitHub
parent 54e346a2aa
commit 4680bc740a
51 changed files with 985 additions and 205 deletions

View File

@ -5,7 +5,7 @@ import { SettingsProtectedRouteWrapper } from '@/settings/components/SettingsPro
import { SettingsSkeletonLoader } from '@/settings/components/SettingsSkeletonLoader';
import { SettingsPath } from '@/types/SettingsPath';
import { FeatureFlagKey } from '~/generated-metadata/graphql';
import { SettingsPermissions } from '~/generated/graphql';
import { SettingPermissionType } from '~/generated/graphql';
const SettingsApiKeys = lazy(() =>
import('~/pages/settings/developers/api-keys/SettingsApiKeys').then(
@ -334,7 +334,7 @@ export const SettingsRoutes = ({
<Route
element={
<SettingsProtectedRouteWrapper
settingsPermission={SettingsPermissions.WORKSPACE}
settingsPermission={SettingPermissionType.WORKSPACE}
/>
}
>
@ -345,7 +345,7 @@ export const SettingsRoutes = ({
<Route
element={
<SettingsProtectedRouteWrapper
settingsPermission={SettingsPermissions.WORKSPACE_MEMBERS}
settingsPermission={SettingPermissionType.WORKSPACE_MEMBERS}
/>
}
>
@ -357,7 +357,7 @@ export const SettingsRoutes = ({
<Route
element={
<SettingsProtectedRouteWrapper
settingsPermission={SettingsPermissions.DATA_MODEL}
settingsPermission={SettingPermissionType.DATA_MODEL}
/>
}
>
@ -387,7 +387,7 @@ export const SettingsRoutes = ({
<Route
element={
<SettingsProtectedRouteWrapper
settingsPermission={SettingsPermissions.ROLES}
settingsPermission={SettingPermissionType.ROLES}
requiredFeatureFlag={FeatureFlagKey.IsPermissionsEnabled}
/>
}
@ -398,7 +398,7 @@ export const SettingsRoutes = ({
<Route
element={
<SettingsProtectedRouteWrapper
settingsPermission={SettingsPermissions.API_KEYS_AND_WEBHOOKS}
settingsPermission={SettingPermissionType.API_KEYS_AND_WEBHOOKS}
/>
}
>
@ -465,7 +465,7 @@ export const SettingsRoutes = ({
<Route
element={
<SettingsProtectedRouteWrapper
settingsPermission={SettingsPermissions.SECURITY}
settingsPermission={SettingPermissionType.SECURITY}
/>
}
>
@ -496,7 +496,7 @@ export const SettingsRoutes = ({
<Route
element={
<SettingsProtectedRouteWrapper
settingsPermission={SettingsPermissions.WORKSPACE}
settingsPermission={SettingPermissionType.WORKSPACE}
/>
}
>

View File

@ -12,10 +12,10 @@ import { ViewType } from '@/views/types/ViewType';
import { useCallback, useContext } from 'react';
import { useLocation } from 'react-router-dom';
import { useSetRecoilState } from 'recoil';
import { IconEyeOff, IconSettings } from 'twenty-ui';
import { SettingsPermissions } from '~/generated/graphql';
import { useNavigateSettings } from '~/hooks/useNavigateSettings';
import { isDefined } from 'twenty-shared/utils';
import { IconEyeOff, IconSettings } from 'twenty-ui';
import { SettingPermissionType } from '~/generated/graphql';
import { useNavigateSettings } from '~/hooks/useNavigateSettings';
type UseRecordGroupActionsParams = {
viewType: ViewType;
@ -71,7 +71,7 @@ export const useRecordGroupActions = ({
]);
const hasAccessToDataModelSettings = useHasSettingsPermission(
SettingsPermissions.DATA_MODEL,
SettingPermissionType.DATA_MODEL,
);
const recordGroupActions: RecordGroupAction[] = [];

View File

@ -3,12 +3,12 @@ import { SettingsPath } from '@/types/SettingsPath';
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
import { ReactNode } from 'react';
import { Navigate, Outlet } from 'react-router-dom';
import { FeatureFlagKey, SettingsPermissions } from '~/generated/graphql';
import { FeatureFlagKey, SettingPermissionType } from '~/generated/graphql';
import { getSettingsPath } from '~/utils/navigation/getSettingsPath';
type SettingsProtectedRouteWrapperProps = {
children?: ReactNode;
settingsPermission?: SettingsPermissions;
settingsPermission?: SettingPermissionType;
requiredFeatureFlag?: FeatureFlagKey;
};

View File

@ -8,7 +8,7 @@ import {
Billing,
FeatureFlagKey,
OnboardingStatus,
SettingsPermissions,
SettingPermissionType,
} from '~/generated/graphql';
import { currentUserState } from '@/auth/states/currentUserState';
@ -61,12 +61,12 @@ jest.mock('@/workspace/hooks/useFeatureFlagsMap', () => ({
describe('useSettingsNavigationItems', () => {
it('should hide workspace settings when no permissions', () => {
(useSettingsPermissionMap as jest.Mock).mockImplementation(() => ({
[SettingsPermissions.WORKSPACE]: false,
[SettingsPermissions.WORKSPACE_MEMBERS]: false,
[SettingsPermissions.DATA_MODEL]: false,
[SettingsPermissions.API_KEYS_AND_WEBHOOKS]: false,
[SettingsPermissions.ROLES]: false,
[SettingsPermissions.SECURITY]: false,
[SettingPermissionType.WORKSPACE]: false,
[SettingPermissionType.WORKSPACE_MEMBERS]: false,
[SettingPermissionType.DATA_MODEL]: false,
[SettingPermissionType.API_KEYS_AND_WEBHOOKS]: false,
[SettingPermissionType.ROLES]: false,
[SettingPermissionType.SECURITY]: false,
}));
const { result } = renderHook(() => useSettingsNavigationItems(), {
@ -82,12 +82,12 @@ describe('useSettingsNavigationItems', () => {
it('should show workspace settings when has permissions', () => {
(useSettingsPermissionMap as jest.Mock).mockImplementation(() => ({
[SettingsPermissions.WORKSPACE]: true,
[SettingsPermissions.WORKSPACE_MEMBERS]: true,
[SettingsPermissions.DATA_MODEL]: true,
[SettingsPermissions.API_KEYS_AND_WEBHOOKS]: true,
[SettingsPermissions.ROLES]: true,
[SettingsPermissions.SECURITY]: true,
[SettingPermissionType.WORKSPACE]: true,
[SettingPermissionType.WORKSPACE_MEMBERS]: true,
[SettingPermissionType.DATA_MODEL]: true,
[SettingPermissionType.API_KEYS_AND_WEBHOOKS]: true,
[SettingPermissionType.ROLES]: true,
[SettingPermissionType.SECURITY]: true,
}));
const { result } = renderHook(() => useSettingsNavigationItems(), {

View File

@ -22,7 +22,6 @@ import {
import { SettingsPath } from '@/types/SettingsPath';
import { FeatureFlagKey } from '~/generated-metadata/graphql';
import { SettingsPermissions } from '~/generated/graphql';
import { currentUserState } from '@/auth/states/currentUserState';
import { billingState } from '@/client-config/states/billingState';
@ -32,6 +31,7 @@ import { NavigationDrawerItemIndentationLevel } from '@/ui/navigation/navigation
import { useFeatureFlagsMap } from '@/workspace/hooks/useFeatureFlagsMap';
import { t } from '@lingui/core/macro';
import { useRecoilValue } from 'recoil';
import { SettingPermissionType } from '~/generated/graphql';
export type SettingsNavigationSection = {
label: string;
@ -108,13 +108,13 @@ const useSettingsNavigationItems = (): SettingsNavigationSection[] => {
label: t`General`,
path: SettingsPath.Workspace,
Icon: IconSettings,
isHidden: !permissionMap[SettingsPermissions.WORKSPACE],
isHidden: !permissionMap[SettingPermissionType.WORKSPACE],
},
{
label: t`Members`,
path: SettingsPath.WorkspaceMembersPage,
Icon: IconUsers,
isHidden: !permissionMap[SettingsPermissions.WORKSPACE_MEMBERS],
isHidden: !permissionMap[SettingPermissionType.WORKSPACE_MEMBERS],
},
{
label: t`Roles`,
@ -122,33 +122,34 @@ const useSettingsNavigationItems = (): SettingsNavigationSection[] => {
Icon: IconLock,
isHidden:
!featureFlags[FeatureFlagKey.IsPermissionsEnabled] ||
!permissionMap[SettingsPermissions.ROLES],
!permissionMap[SettingPermissionType.ROLES],
},
{
label: t`Billing`,
path: SettingsPath.Billing,
Icon: IconCurrencyDollar,
isHidden:
!isBillingEnabled || !permissionMap[SettingsPermissions.WORKSPACE],
!isBillingEnabled ||
!permissionMap[SettingPermissionType.WORKSPACE],
},
{
label: t`Data model`,
path: SettingsPath.Objects,
Icon: IconHierarchy2,
isHidden: !permissionMap[SettingsPermissions.DATA_MODEL],
isHidden: !permissionMap[SettingPermissionType.DATA_MODEL],
},
{
label: t`Integrations`,
path: SettingsPath.Integrations,
Icon: IconApps,
isHidden: !permissionMap[SettingsPermissions.API_KEYS_AND_WEBHOOKS],
isHidden: !permissionMap[SettingPermissionType.API_KEYS_AND_WEBHOOKS],
},
{
label: t`Security`,
path: SettingsPath.Security,
Icon: IconKey,
isAdvanced: true,
isHidden: !permissionMap[SettingsPermissions.SECURITY],
isHidden: !permissionMap[SettingPermissionType.SECURITY],
},
],
},
@ -161,14 +162,14 @@ const useSettingsNavigationItems = (): SettingsNavigationSection[] => {
path: SettingsPath.APIs,
Icon: IconApi,
isAdvanced: true,
isHidden: !permissionMap[SettingsPermissions.API_KEYS_AND_WEBHOOKS],
isHidden: !permissionMap[SettingPermissionType.API_KEYS_AND_WEBHOOKS],
},
{
label: t`Webhooks`,
path: SettingsPath.Webhooks,
Icon: IconWebhook,
isAdvanced: true,
isHidden: !permissionMap[SettingsPermissions.API_KEYS_AND_WEBHOOKS],
isHidden: !permissionMap[SettingPermissionType.API_KEYS_AND_WEBHOOKS],
},
{
label: t`Functions`,
@ -194,7 +195,7 @@ const useSettingsNavigationItems = (): SettingsNavigationSection[] => {
Icon: IconFlask,
isHidden:
!labPublicFeatureFlags.length ||
!permissionMap[SettingsPermissions.WORKSPACE],
!permissionMap[SettingPermissionType.WORKSPACE],
},
{
label: t`Releases`,

View File

@ -1,11 +1,11 @@
import { currentUserWorkspaceState } from '@/auth/states/currentUserWorkspaceState';
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
import { useRecoilValue } from 'recoil';
import { SettingsPermissions } from '~/generated/graphql';
import { WorkspaceActivationStatus } from 'twenty-shared/workspace';
import { SettingPermissionType } from '~/generated/graphql';
export const useHasSettingsPermission = (
settingsPermission?: SettingsPermissions,
settingsPermission?: SettingPermissionType,
) => {
const currentWorkspace = useRecoilValue(currentWorkspaceState);
const currentUserWorkspace = useRecoilValue(currentUserWorkspaceState);
@ -15,19 +15,18 @@ export const useHasSettingsPermission = (
}
if (
settingsPermission === SettingsPermissions.WORKSPACE &&
settingsPermission === SettingPermissionType.WORKSPACE &&
currentWorkspace?.activationStatus ===
WorkspaceActivationStatus.PENDING_CREATION
) {
return true;
}
const currentUserWorkspaceSettingsPermissions =
currentUserWorkspace?.settingsPermissions;
const currentUserWorkspaceSetting = currentUserWorkspace?.settingsPermissions;
if (!currentUserWorkspaceSettingsPermissions) {
if (!currentUserWorkspaceSetting) {
return false;
}
return currentUserWorkspaceSettingsPermissions.includes(settingsPermission);
return currentUserWorkspaceSetting.includes(settingsPermission);
};

View File

@ -2,11 +2,11 @@ import { currentUserWorkspaceState } from '@/auth/states/currentUserWorkspaceSta
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
import { useRecoilValue } from 'recoil';
import { FeatureFlagKey } from '~/generated-metadata/graphql';
import { SettingsPermissions } from '~/generated/graphql';
import { SettingPermissionType } from '~/generated/graphql';
import { buildRecordFromKeysWithSameValue } from '~/utils/array/buildRecordFromKeysWithSameValue';
export const useSettingsPermissionMap = (): Record<
SettingsPermissions,
SettingPermissionType,
boolean
> => {
const currentUserWorkspace = useRecoilValue(currentUserWorkspaceState);
@ -19,7 +19,7 @@ export const useSettingsPermissionMap = (): Record<
currentUserWorkspace?.settingsPermissions;
const initialPermissions = buildRecordFromKeysWithSameValue(
Object.values(SettingsPermissions),
Object.values(SettingPermissionType),
!isPermissionEnabled,
);

View File

@ -21,7 +21,7 @@ import {
Section,
} from 'twenty-ui';
import { Role } from '~/generated-metadata/graphql';
import { SettingsPermissions } from '~/generated/graphql';
import { SettingPermissionType } from '~/generated/graphql';
import { RolePermissionsObjectsTableRow } from './RolePermissionsObjectsTableRow';
const StyledRolePermissionsContainer = styled.div`
@ -81,49 +81,49 @@ export const RolePermissions = ({ role }: RolePermissionsProps) => {
const settingsPermissionsConfig: RolePermissionsSettingPermission[] = [
{
key: SettingsPermissions.API_KEYS_AND_WEBHOOKS,
key: SettingPermissionType.API_KEYS_AND_WEBHOOKS,
name: 'API Keys & Webhooks',
description: 'Manage API keys and webhooks',
value: role.canUpdateAllSettings,
Icon: IconCode,
},
{
key: SettingsPermissions.WORKSPACE,
key: SettingPermissionType.WORKSPACE,
name: 'Workspace',
description: 'Set global workspace preferences',
value: role.canUpdateAllSettings,
Icon: IconSettings,
},
{
key: SettingsPermissions.WORKSPACE_MEMBERS,
key: SettingPermissionType.WORKSPACE_MEMBERS,
name: 'Users',
description: 'Add or remove users',
value: role.canUpdateAllSettings,
Icon: IconUsers,
},
{
key: SettingsPermissions.ROLES,
key: SettingPermissionType.ROLES,
name: 'Roles',
description: 'Define user roles and access levels',
value: role.canUpdateAllSettings,
Icon: IconLockOpen,
},
{
key: SettingsPermissions.DATA_MODEL,
key: SettingPermissionType.DATA_MODEL,
name: 'Data Model',
description: 'Edit CRM data structure and fields',
value: role.canUpdateAllSettings,
Icon: IconHierarchy,
},
{
key: SettingsPermissions.ADMIN_PANEL,
key: SettingPermissionType.ADMIN_PANEL,
name: 'Admin Panel',
description: 'Admin settings and system tools',
value: role.canUpdateAllSettings,
Icon: IconServer,
},
{
key: SettingsPermissions.SECURITY,
key: SettingPermissionType.SECURITY,
name: 'Security',
description: 'Manage security policies',
value: role.canUpdateAllSettings,