[permissions] Enable permissions V1 for all workspaces (#11172)

Closes https://github.com/twentyhq/core-team-issues/issues/526

(for reminder: 
1. Make defaultRoleId non-nullable for an active workspace
2. Remove permissions V1 feature flag
3. Set member role as default role for new workspaces

About 1.:
An active workspace's defaultRoleId should never be null.
We can't rely on a simple postgres NOT NULL constraint as defaultRoleId
will always be initially null when the workspace is first created since
the roles do not exist at that time.

Let's add a more complex rule to ensure that

About 3.:
In the first phase of our deploy of permissions, we chose to assign
admin role to all existing users, not to break any existing behavior
with the introduction of the feature (= existing users have less rights
than before).

As we deploy permissions to all existing and future workspaces, let's
set the member role as default role for future workspaces.
)
This commit is contained in:
Marie
2025-03-26 13:51:34 +01:00
committed by GitHub
parent 0f7adedc96
commit 72b4b26e2c
35 changed files with 103 additions and 562 deletions

View File

@ -4,7 +4,6 @@ import { Route, Routes } from 'react-router-dom';
import { SettingsProtectedRouteWrapper } from '@/settings/components/SettingsProtectedRouteWrapper';
import { SettingsSkeletonLoader } from '@/settings/components/SettingsSkeletonLoader';
import { SettingsPath } from '@/types/SettingsPath';
import { FeatureFlagKey } from '~/generated-metadata/graphql';
import { SettingPermissionType } from '~/generated/graphql';
const SettingsApiKeys = lazy(() =>
@ -388,7 +387,6 @@ export const SettingsRoutes = ({
element={
<SettingsProtectedRouteWrapper
settingsPermission={SettingPermissionType.ROLES}
requiredFeatureFlag={FeatureFlagKey.IsPermissionsEnabled}
/>
}
>

View File

@ -17,18 +17,12 @@ export const SettingsProtectedRouteWrapper = ({
settingsPermission,
requiredFeatureFlag,
}: SettingsProtectedRouteWrapperProps) => {
const isPermissionsEnabled = useIsFeatureEnabled(
FeatureFlagKey.IsPermissionsEnabled,
);
const hasPermission = useHasSettingsPermission(settingsPermission);
const requiredFeatureFlagEnabled = useIsFeatureEnabled(
requiredFeatureFlag || null,
);
if (
(requiredFeatureFlag && !requiredFeatureFlagEnabled) ||
(!hasPermission && isPermissionsEnabled)
) {
if ((requiredFeatureFlag && !requiredFeatureFlagEnabled) || !hasPermission) {
return <Navigate to={getSettingsPath(SettingsPath.ProfilePage)} replace />;
}

View File

@ -6,7 +6,6 @@ import { MemoryRouter } from 'react-router-dom';
import { MutableSnapshot, RecoilRoot } from 'recoil';
import {
Billing,
FeatureFlagKey,
OnboardingStatus,
SettingPermissionType,
} from '~/generated/graphql';
@ -52,12 +51,6 @@ jest.mock('@/settings/roles/hooks/useSettingsPermissionMap', () => ({
useSettingsPermissionMap: jest.fn(),
}));
jest.mock('@/workspace/hooks/useFeatureFlagsMap', () => ({
useFeatureFlagsMap: () => ({
[FeatureFlagKey.IsPermissionsEnabled]: true,
}),
}));
describe('useSettingsNavigationItems', () => {
it('should hide workspace settings when no permissions', () => {
(useSettingsPermissionMap as jest.Mock).mockImplementation(() => ({

View File

@ -21,14 +21,12 @@ import {
} from 'twenty-ui';
import { SettingsPath } from '@/types/SettingsPath';
import { FeatureFlagKey } from '~/generated-metadata/graphql';
import { currentUserState } from '@/auth/states/currentUserState';
import { billingState } from '@/client-config/states/billingState';
import { labPublicFeatureFlagsState } from '@/client-config/states/labPublicFeatureFlagsState';
import { useSettingsPermissionMap } from '@/settings/roles/hooks/useSettingsPermissionMap';
import { NavigationDrawerItemIndentationLevel } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerItem';
import { useFeatureFlagsMap } from '@/workspace/hooks/useFeatureFlagsMap';
import { t } from '@lingui/core/macro';
import { useRecoilValue } from 'recoil';
import { SettingPermissionType } from '~/generated/graphql';
@ -63,7 +61,6 @@ const useSettingsNavigationItems = (): SettingsNavigationSection[] => {
false;
const labPublicFeatureFlags = useRecoilValue(labPublicFeatureFlagsState);
const featureFlags = useFeatureFlagsMap();
const permissionMap = useSettingsPermissionMap();
return [
{
@ -120,9 +117,7 @@ const useSettingsNavigationItems = (): SettingsNavigationSection[] => {
label: t`Roles`,
path: SettingsPath.Roles,
Icon: IconLock,
isHidden:
!featureFlags[FeatureFlagKey.IsPermissionsEnabled] ||
!permissionMap[SettingPermissionType.ROLES],
isHidden: !permissionMap[SettingPermissionType.ROLES],
},
{
label: t`Billing`,

View File

@ -1,19 +1,10 @@
import { currentUserWorkspaceState } from '@/auth/states/currentUserWorkspaceState';
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
import { useRecoilValue } from 'recoil';
import { FeatureFlagKey } from '~/generated-metadata/graphql';
import { isDefined } from 'twenty-shared/utils';
import { PermissionsOnAllObjectRecords } from 'twenty-shared/constants';
import { isDefined } from 'twenty-shared/utils';
export const useHasObjectReadOnlyPermission = () => {
const currentUserWorkspace = useRecoilValue(currentUserWorkspaceState);
const isPermissionEnabled = useIsFeatureEnabled(
FeatureFlagKey.IsPermissionsEnabled,
);
if (!isPermissionEnabled) {
return false;
}
if (!isDefined(currentUserWorkspace?.objectRecordsPermissions)) {
return false;

View File

@ -1,7 +1,5 @@
import { currentUserWorkspaceState } from '@/auth/states/currentUserWorkspaceState';
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
import { useRecoilValue } from 'recoil';
import { FeatureFlagKey } from '~/generated-metadata/graphql';
import { SettingPermissionType } from '~/generated/graphql';
import { buildRecordFromKeysWithSameValue } from '~/utils/array/buildRecordFromKeysWithSameValue';
@ -11,16 +9,12 @@ export const useSettingsPermissionMap = (): Record<
> => {
const currentUserWorkspace = useRecoilValue(currentUserWorkspaceState);
const isPermissionEnabled = useIsFeatureEnabled(
FeatureFlagKey.IsPermissionsEnabled,
);
const currentUserWorkspaceSettingsPermissions =
currentUserWorkspace?.settingsPermissions;
const initialPermissions = buildRecordFromKeysWithSameValue(
Object.values(SettingPermissionType),
!isPermissionEnabled,
false,
);
if (!currentUserWorkspaceSettingsPermissions) {