[permissions V2] Remove feature flag (#12790)

This commit is contained in:
Marie
2025-06-23 17:22:57 +02:00
committed by GitHub
parent b6787c6fcd
commit 4c94fc2803
22 changed files with 99 additions and 407 deletions

View File

@ -691,7 +691,6 @@ export enum FeatureFlagKey {
IS_AIRTABLE_INTEGRATION_ENABLED = 'IS_AIRTABLE_INTEGRATION_ENABLED', IS_AIRTABLE_INTEGRATION_ENABLED = 'IS_AIRTABLE_INTEGRATION_ENABLED',
IS_AI_ENABLED = 'IS_AI_ENABLED', IS_AI_ENABLED = 'IS_AI_ENABLED',
IS_JSON_FILTER_ENABLED = 'IS_JSON_FILTER_ENABLED', IS_JSON_FILTER_ENABLED = 'IS_JSON_FILTER_ENABLED',
IS_PERMISSIONS_V2_ENABLED = 'IS_PERMISSIONS_V2_ENABLED',
IS_POSTGRESQL_INTEGRATION_ENABLED = 'IS_POSTGRESQL_INTEGRATION_ENABLED', IS_POSTGRESQL_INTEGRATION_ENABLED = 'IS_POSTGRESQL_INTEGRATION_ENABLED',
IS_STRIPE_INTEGRATION_ENABLED = 'IS_STRIPE_INTEGRATION_ENABLED', IS_STRIPE_INTEGRATION_ENABLED = 'IS_STRIPE_INTEGRATION_ENABLED',
IS_UNIQUE_INDEXES_ENABLED = 'IS_UNIQUE_INDEXES_ENABLED' IS_UNIQUE_INDEXES_ENABLED = 'IS_UNIQUE_INDEXES_ENABLED'

View File

@ -647,7 +647,6 @@ export enum FeatureFlagKey {
IS_AIRTABLE_INTEGRATION_ENABLED = 'IS_AIRTABLE_INTEGRATION_ENABLED', IS_AIRTABLE_INTEGRATION_ENABLED = 'IS_AIRTABLE_INTEGRATION_ENABLED',
IS_AI_ENABLED = 'IS_AI_ENABLED', IS_AI_ENABLED = 'IS_AI_ENABLED',
IS_JSON_FILTER_ENABLED = 'IS_JSON_FILTER_ENABLED', IS_JSON_FILTER_ENABLED = 'IS_JSON_FILTER_ENABLED',
IS_PERMISSIONS_V2_ENABLED = 'IS_PERMISSIONS_V2_ENABLED',
IS_POSTGRESQL_INTEGRATION_ENABLED = 'IS_POSTGRESQL_INTEGRATION_ENABLED', IS_POSTGRESQL_INTEGRATION_ENABLED = 'IS_POSTGRESQL_INTEGRATION_ENABLED',
IS_STRIPE_INTEGRATION_ENABLED = 'IS_STRIPE_INTEGRATION_ENABLED', IS_STRIPE_INTEGRATION_ENABLED = 'IS_STRIPE_INTEGRATION_ENABLED',
IS_UNIQUE_INDEXES_ENABLED = 'IS_UNIQUE_INDEXES_ENABLED' IS_UNIQUE_INDEXES_ENABLED = 'IS_UNIQUE_INDEXES_ENABLED'

View File

@ -7,12 +7,10 @@ import { SettingsRolesTableRow } from '@/settings/roles/components/SettingsRoles
import { settingsAllRolesSelector } from '@/settings/roles/states/settingsAllRolesSelector'; import { settingsAllRolesSelector } from '@/settings/roles/states/settingsAllRolesSelector';
import { SettingsPath } from '@/types/SettingsPath'; import { SettingsPath } from '@/types/SettingsPath';
import { TableCell } from '@/ui/layout/table/components/TableCell'; import { TableCell } from '@/ui/layout/table/components/TableCell';
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
import { useRecoilValue } from 'recoil'; import { useRecoilValue } from 'recoil';
import { H2Title, IconPlus } from 'twenty-ui/display'; import { H2Title, IconPlus } from 'twenty-ui/display';
import { Button } from 'twenty-ui/input'; import { Button } from 'twenty-ui/input';
import { Section } from 'twenty-ui/layout'; import { Section } from 'twenty-ui/layout';
import { FeatureFlagKey } from '~/generated/graphql';
import { useNavigateSettings } from '~/hooks/useNavigateSettings'; import { useNavigateSettings } from '~/hooks/useNavigateSettings';
import { sortByAscString } from '~/utils/array/sortByAscString'; import { sortByAscString } from '~/utils/array/sortByAscString';
@ -35,9 +33,6 @@ const StyledNoRoles = styled(TableCell)`
export const SettingsRolesList = () => { export const SettingsRolesList = () => {
const navigateSettings = useNavigateSettings(); const navigateSettings = useNavigateSettings();
const isPermissionsV2Enabled = useIsFeatureEnabled(
FeatureFlagKey.IS_PERMISSIONS_V2_ENABLED,
);
const settingsAllRoles = useRecoilValue(settingsAllRolesSelector); const settingsAllRoles = useRecoilValue(settingsAllRolesSelector);
@ -69,8 +64,6 @@ export const SettingsRolesList = () => {
title={t`Create Role`} title={t`Create Role`}
variant="secondary" variant="secondary"
size="small" size="small"
soon={!isPermissionsV2Enabled}
disabled={!isPermissionsV2Enabled}
onClick={() => navigateSettings(SettingsPath.RoleCreate)} onClick={() => navigateSettings(SettingsPath.RoleCreate)}
/> />
</StyledCreateRoleSection> </StyledCreateRoleSection>

View File

@ -1,9 +1,7 @@
import { SettingsRolePermissionsObjectLevelSection } from '@/settings/roles/role-permissions/object-level-permissions/components/SettingsRolePermissionsObjectLevelSection'; import { SettingsRolePermissionsObjectLevelSection } from '@/settings/roles/role-permissions/object-level-permissions/components/SettingsRolePermissionsObjectLevelSection';
import { SettingsRolePermissionsObjectsSection } from '@/settings/roles/role-permissions/objects-permissions/components/SettingsRolePermissionsObjectsSection'; import { SettingsRolePermissionsObjectsSection } from '@/settings/roles/role-permissions/objects-permissions/components/SettingsRolePermissionsObjectsSection';
import { SettingsRolePermissionsSettingsSection } from '@/settings/roles/role-permissions/settings-permissions/components/SettingsRolePermissionsSettingsSection'; import { SettingsRolePermissionsSettingsSection } from '@/settings/roles/role-permissions/settings-permissions/components/SettingsRolePermissionsSettingsSection';
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
import styled from '@emotion/styled'; import styled from '@emotion/styled';
import { FeatureFlagKey } from '~/generated-metadata/graphql';
const StyledRolePermissionsContainer = styled.div` const StyledRolePermissionsContainer = styled.div`
display: flex; display: flex;
@ -22,17 +20,13 @@ export const SettingsRolePermissions = ({
isEditable, isEditable,
isCreateMode, isCreateMode,
}: SettingsRolePermissionsProps) => { }: SettingsRolePermissionsProps) => {
const isPermissionsV2Enabled = useIsFeatureEnabled(
FeatureFlagKey.IS_PERMISSIONS_V2_ENABLED,
);
return ( return (
<StyledRolePermissionsContainer> <StyledRolePermissionsContainer>
<SettingsRolePermissionsObjectsSection <SettingsRolePermissionsObjectsSection
roleId={roleId} roleId={roleId}
isEditable={isEditable} isEditable={isEditable}
/> />
{isPermissionsV2Enabled && !isCreateMode && ( {!isCreateMode && (
<SettingsRolePermissionsObjectLevelSection <SettingsRolePermissionsObjectLevelSection
roleId={roleId} roleId={roleId}
isEditable={isEditable} isEditable={isEditable}

View File

@ -3,7 +3,6 @@ import { SettingsRolePermissionsSettingsTableHeader } from '@/settings/roles/rol
import { SettingsRolePermissionsSettingsTableRow } from '@/settings/roles/role-permissions/settings-permissions/components/SettingsRolePermissionsSettingsTableRow'; import { SettingsRolePermissionsSettingsTableRow } from '@/settings/roles/role-permissions/settings-permissions/components/SettingsRolePermissionsSettingsTableRow';
import { SettingsRolePermissionsSettingPermission } from '@/settings/roles/role-permissions/settings-permissions/types/SettingsRolePermissionsSettingPermission'; import { SettingsRolePermissionsSettingPermission } from '@/settings/roles/role-permissions/settings-permissions/types/SettingsRolePermissionsSettingPermission';
import { settingsDraftRoleFamilyState } from '@/settings/roles/states/settingsDraftRoleFamilyState'; import { settingsDraftRoleFamilyState } from '@/settings/roles/states/settingsDraftRoleFamilyState';
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
import styled from '@emotion/styled'; import styled from '@emotion/styled';
import { t } from '@lingui/core/macro'; import { t } from '@lingui/core/macro';
import { useRecoilState } from 'recoil'; import { useRecoilState } from 'recoil';
@ -18,10 +17,7 @@ import {
IconUsers, IconUsers,
} from 'twenty-ui/display'; } from 'twenty-ui/display';
import { AnimatedExpandableContainer, Card, Section } from 'twenty-ui/layout'; import { AnimatedExpandableContainer, Card, Section } from 'twenty-ui/layout';
import { import { SettingPermissionType } from '~/generated-metadata/graphql';
FeatureFlagKey,
SettingPermissionType,
} from '~/generated-metadata/graphql';
const StyledTable = styled.div` const StyledTable = styled.div`
border-bottom: 1px solid ${({ theme }) => theme.border.color.light}; border-bottom: 1px solid ${({ theme }) => theme.border.color.light};
@ -45,10 +41,6 @@ export const SettingsRolePermissionsSettingsSection = ({
roleId, roleId,
isEditable, isEditable,
}: SettingsRolePermissionsSettingsSectionProps) => { }: SettingsRolePermissionsSettingsSectionProps) => {
const isPermissionsV2Enabled = useIsFeatureEnabled(
FeatureFlagKey.IS_PERMISSIONS_V2_ENABLED,
);
const [settingsDraftRole, setSettingsDraftRole] = useRecoilState( const [settingsDraftRole, setSettingsDraftRole] = useRecoilState(
settingsDraftRoleFamilyState(roleId), settingsDraftRoleFamilyState(roleId),
); );
@ -102,23 +94,21 @@ export const SettingsRolePermissionsSettingsSection = ({
return ( return (
<Section> <Section>
<H2Title title={t`Settings`} description={t`Settings permissions`} /> <H2Title title={t`Settings`} description={t`Settings permissions`} />
{isPermissionsV2Enabled && ( <StyledCard rounded>
<StyledCard rounded> <SettingsOptionCardContentToggle
<SettingsOptionCardContentToggle Icon={IconSettings}
Icon={IconSettings} title={t`Settings All Access`}
title={t`Settings All Access`} description={t`Ability to edit all settings`}
description={t`Ability to edit all settings`} checked={settingsDraftRole.canUpdateAllSettings}
checked={settingsDraftRole.canUpdateAllSettings} disabled={!isEditable}
disabled={!isEditable} onChange={() => {
onChange={() => { setSettingsDraftRole({
setSettingsDraftRole({ ...settingsDraftRole,
...settingsDraftRole, canUpdateAllSettings: !settingsDraftRole.canUpdateAllSettings,
canUpdateAllSettings: !settingsDraftRole.canUpdateAllSettings, });
}); }}
}} />
/> </StyledCard>
</StyledCard>
)}
<AnimatedExpandableContainer <AnimatedExpandableContainer
isExpanded={!settingsDraftRole.canUpdateAllSettings} isExpanded={!settingsDraftRole.canUpdateAllSettings}
dimension="height" dimension="height"

View File

@ -2,13 +2,11 @@ import { SettingsRolePermissionsSettingPermission } from '@/settings/roles/role-
import { settingsDraftRoleFamilyState } from '@/settings/roles/states/settingsDraftRoleFamilyState'; import { settingsDraftRoleFamilyState } from '@/settings/roles/states/settingsDraftRoleFamilyState';
import { TableCell } from '@/ui/layout/table/components/TableCell'; import { TableCell } from '@/ui/layout/table/components/TableCell';
import { TableRow } from '@/ui/layout/table/components/TableRow'; import { TableRow } from '@/ui/layout/table/components/TableRow';
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
import { useTheme } from '@emotion/react'; import { useTheme } from '@emotion/react';
import styled from '@emotion/styled'; import styled from '@emotion/styled';
import { useRecoilState } from 'recoil'; import { useRecoilState } from 'recoil';
import { Checkbox } from 'twenty-ui/input'; import { Checkbox } from 'twenty-ui/input';
import { v4 } from 'uuid'; import { v4 } from 'uuid';
import { FeatureFlagKey } from '~/generated-metadata/graphql';
const StyledTableRow = styled(TableRow)<{ isDisabled: boolean }>` const StyledTableRow = styled(TableRow)<{ isDisabled: boolean }>`
cursor: ${({ isDisabled }) => (isDisabled ? 'default' : 'pointer')}; cursor: ${({ isDisabled }) => (isDisabled ? 'default' : 'pointer')};
@ -57,9 +55,6 @@ export const SettingsRolePermissionsSettingsTableRow = ({
const [settingsDraftRole, setSettingsDraftRole] = useRecoilState( const [settingsDraftRole, setSettingsDraftRole] = useRecoilState(
settingsDraftRoleFamilyState(roleId), settingsDraftRoleFamilyState(roleId),
); );
const isPermissionsV2Enabled = useIsFeatureEnabled(
FeatureFlagKey.IS_PERMISSIONS_V2_ENABLED,
);
const canUpdateAllSettings = settingsDraftRole.canUpdateAllSettings; const canUpdateAllSettings = settingsDraftRole.canUpdateAllSettings;
const isSettingPermissionEnabled = const isSettingPermissionEnabled =
@ -68,8 +63,7 @@ export const SettingsRolePermissionsSettingsTableRow = ({
) ?? false; ) ?? false;
const isChecked = isSettingPermissionEnabled || canUpdateAllSettings; const isChecked = isSettingPermissionEnabled || canUpdateAllSettings;
const isDisabled = const isDisabled = !isEditable || canUpdateAllSettings;
!isEditable || canUpdateAllSettings || !isPermissionsV2Enabled;
const handleChange = (value: boolean) => { const handleChange = (value: boolean) => {
const currentPermissions = settingsDraftRole.settingPermissions ?? []; const currentPermissions = settingsDraftRole.settingPermissions ?? [];

View File

@ -18,7 +18,6 @@ import { SubMenuTopBarContainer } from '@/ui/layout/page/components/SubMenuTopBa
import { TabList } from '@/ui/layout/tab-list/components/TabList'; import { TabList } from '@/ui/layout/tab-list/components/TabList';
import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTabIdComponentState'; import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTabIdComponentState';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
import { getOperationName } from '@apollo/client/utilities'; import { getOperationName } from '@apollo/client/utilities';
import { t } from '@lingui/core/macro'; import { t } from '@lingui/core/macro';
import { useRecoilState, useRecoilValue } from 'recoil'; import { useRecoilState, useRecoilValue } from 'recoil';
@ -26,7 +25,6 @@ import { isDefined } from 'twenty-shared/utils';
import { IconLockOpen, IconSettings, IconUserPlus } from 'twenty-ui/display'; import { IconLockOpen, IconSettings, IconUserPlus } from 'twenty-ui/display';
import { v4 } from 'uuid'; import { v4 } from 'uuid';
import { import {
FeatureFlagKey,
Role, Role,
useCreateOneRoleMutation, useCreateOneRoleMutation,
useUpdateOneRoleMutation, useUpdateOneRoleMutation,
@ -60,10 +58,6 @@ export const SettingsRole = ({ roleId, isCreateMode }: SettingsRoleProps) => {
SETTINGS_ROLE_DETAIL_TABS.COMPONENT_INSTANCE_ID + '-' + roleId, SETTINGS_ROLE_DETAIL_TABS.COMPONENT_INSTANCE_ID + '-' + roleId,
); );
const isPermissionsV2Enabled = useIsFeatureEnabled(
FeatureFlagKey.IS_PERMISSIONS_V2_ENABLED,
);
const navigateSettings = useNavigateSettings(); const navigateSettings = useNavigateSettings();
const [createRole] = useCreateOneRoleMutation(); const [createRole] = useCreateOneRoleMutation();
@ -91,7 +85,7 @@ export const SettingsRole = ({ roleId, isCreateMode }: SettingsRoleProps) => {
return <></>; return <></>;
} }
const isRoleEditable = isPermissionsV2Enabled && settingsDraftRole.isEditable; const isRoleEditable = settingsDraftRole.isEditable;
const tabs = [ const tabs = [
{ {

View File

@ -26,6 +26,7 @@ import { workspaceQueryRunnerGraphqlApiExceptionHandler } from 'src/engine/api/g
import { WorkspaceQueryHookService } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/workspace-query-hook.service'; import { WorkspaceQueryHookService } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/workspace-query-hook.service';
import { RESOLVER_METHOD_NAMES } from 'src/engine/api/graphql/workspace-resolver-builder/constants/resolver-method-names'; import { RESOLVER_METHOD_NAMES } from 'src/engine/api/graphql/workspace-resolver-builder/constants/resolver-method-names';
import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum'; import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum';
import { workspaceValidator } from 'src/engine/core-modules/workspace/workspace.validate';
import { SettingPermissionType } from 'src/engine/metadata-modules/permissions/constants/setting-permission-type.constants'; import { SettingPermissionType } from 'src/engine/metadata-modules/permissions/constants/setting-permission-type.constants';
import { import {
PermissionsException, PermissionsException,
@ -37,7 +38,6 @@ import { UserRoleService } from 'src/engine/metadata-modules/user-role/user-role
import { WorkspaceDataSource } from 'src/engine/twenty-orm/datasource/workspace.datasource'; import { WorkspaceDataSource } from 'src/engine/twenty-orm/datasource/workspace.datasource';
import { WorkspaceRepository } from 'src/engine/twenty-orm/repository/workspace.repository'; import { WorkspaceRepository } from 'src/engine/twenty-orm/repository/workspace.repository';
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager'; import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
import { workspaceValidator } from 'src/engine/core-modules/workspace/workspace.validate';
export type GraphqlQueryResolverExecutionArgs<Input extends ResolverArgs> = { export type GraphqlQueryResolverExecutionArgs<Input extends ResolverArgs> = {
args: Input; args: Input;
@ -98,18 +98,8 @@ export abstract class GraphqlQueryBaseResolverService<
const featureFlagsMap = workspaceDataSource.featureFlagMap; const featureFlagsMap = workspaceDataSource.featureFlagMap;
const isPermissionsV2Enabled =
featureFlagsMap[FeatureFlagKey.IS_PERMISSIONS_V2_ENABLED];
if (objectMetadataItemWithFieldMaps.isSystem === true) { if (objectMetadataItemWithFieldMaps.isSystem === true) {
await this.validateSettingsPermissionsOnObjectOrThrow(options); await this.validateSettingsPermissionsOnObjectOrThrow(options);
} else {
if (!isPermissionsV2Enabled)
await this.validateObjectRecordPermissionsOrThrow({
objectMetadataId: objectMetadataItemWithFieldMaps.id,
operationName,
options,
});
} }
const hookedArgs = const hookedArgs =
@ -228,39 +218,6 @@ export abstract class GraphqlQueryBaseResolverService<
} }
} }
private async validateObjectRecordPermissionsOrThrow({
objectMetadataId,
operationName,
options,
}: {
objectMetadataId: string;
operationName: WorkspaceResolverBuilderMethodNames;
options: WorkspaceQueryRunnerOptions;
}) {
const requiredPermission =
this.getRequiredPermissionForMethod(operationName);
const workspace = options.authContext.workspace;
workspaceValidator.assertIsDefinedOrThrow(workspace);
const userHasPermission =
await this.permissionsService.userHasObjectRecordsPermission({
userWorkspaceId: options.authContext.userWorkspaceId,
requiredPermission,
workspaceId: workspace.id,
isExecutedByApiKey: isDefined(options.authContext.apiKey),
objectMetadataId,
});
if (!userHasPermission) {
throw new PermissionsException(
PermissionsExceptionMessage.PERMISSION_DENIED,
PermissionsExceptionCode.PERMISSION_DENIED,
);
}
}
private getRequiredPermissionForMethod( private getRequiredPermissionForMethod(
operationName: WorkspaceResolverBuilderMethodNames, operationName: WorkspaceResolverBuilderMethodNames,
) { ) {

View File

@ -4,6 +4,5 @@ export enum FeatureFlagKey {
IS_STRIPE_INTEGRATION_ENABLED = 'IS_STRIPE_INTEGRATION_ENABLED', IS_STRIPE_INTEGRATION_ENABLED = 'IS_STRIPE_INTEGRATION_ENABLED',
IS_UNIQUE_INDEXES_ENABLED = 'IS_UNIQUE_INDEXES_ENABLED', IS_UNIQUE_INDEXES_ENABLED = 'IS_UNIQUE_INDEXES_ENABLED',
IS_JSON_FILTER_ENABLED = 'IS_JSON_FILTER_ENABLED', IS_JSON_FILTER_ENABLED = 'IS_JSON_FILTER_ENABLED',
IS_PERMISSIONS_V2_ENABLED = 'IS_PERMISSIONS_V2_ENABLED',
IS_AI_ENABLED = 'IS_AI_ENABLED', IS_AI_ENABLED = 'IS_AI_ENABLED',
} }

View File

@ -73,12 +73,6 @@ export class FeatureFlagService {
}, },
); );
if (keys.includes(FeatureFlagKey.IS_PERMISSIONS_V2_ENABLED)) {
await this.workspacePermissionsCacheService.recomputeRolesPermissionsCache(
{ workspaceId, ignoreLock: true },
);
}
await this.workspaceFeatureFlagsMapCacheService.recomputeFeatureFlagsMapCache( await this.workspaceFeatureFlagsMapCacheService.recomputeFeatureFlagsMapCache(
{ {
workspaceId, workspaceId,
@ -144,12 +138,6 @@ export class FeatureFlagService {
}, },
); );
if (featureFlag === FeatureFlagKey.IS_PERMISSIONS_V2_ENABLED) {
await this.workspacePermissionsCacheService.recomputeRolesPermissionsCache(
{ workspaceId, ignoreLock: true },
);
}
return result; return result;
} }
} }

View File

@ -1,7 +1,6 @@
import { Module } from '@nestjs/common'; import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm'; import { TypeOrmModule } from '@nestjs/typeorm';
import { FeatureFlagModule } from 'src/engine/core-modules/feature-flag/feature-flag.module';
import { UserWorkspace } from 'src/engine/core-modules/user-workspace/user-workspace.entity'; import { UserWorkspace } from 'src/engine/core-modules/user-workspace/user-workspace.entity';
import { PermissionsService } from 'src/engine/metadata-modules/permissions/permissions.service'; import { PermissionsService } from 'src/engine/metadata-modules/permissions/permissions.service';
import { RoleEntity } from 'src/engine/metadata-modules/role/role.entity'; import { RoleEntity } from 'src/engine/metadata-modules/role/role.entity';
@ -12,7 +11,6 @@ import { WorkspacePermissionsCacheModule } from 'src/engine/metadata-modules/wor
@Module({ @Module({
imports: [ imports: [
TypeOrmModule.forFeature([RoleEntity, UserWorkspaceRoleEntity], 'core'), TypeOrmModule.forFeature([RoleEntity, UserWorkspaceRoleEntity], 'core'),
FeatureFlagModule,
TypeOrmModule.forFeature([UserWorkspace], 'core'), TypeOrmModule.forFeature([UserWorkspace], 'core'),
UserRoleModule, UserRoleModule,
WorkspacePermissionsCacheModule, WorkspacePermissionsCacheModule,

View File

@ -7,8 +7,6 @@ import {
AuthException, AuthException,
AuthExceptionCode, AuthExceptionCode,
} from 'src/engine/core-modules/auth/auth.exception'; } from 'src/engine/core-modules/auth/auth.exception';
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 { SettingPermissionType } from 'src/engine/metadata-modules/permissions/constants/setting-permission-type.constants'; import { SettingPermissionType } from 'src/engine/metadata-modules/permissions/constants/setting-permission-type.constants';
import { import {
PermissionsException, PermissionsException,
@ -24,7 +22,6 @@ export class PermissionsService {
constructor( constructor(
private readonly userRoleService: UserRoleService, private readonly userRoleService: UserRoleService,
private readonly workspacePermissionsCacheService: WorkspacePermissionsCacheService, private readonly workspacePermissionsCacheService: WorkspacePermissionsCacheService,
private readonly featureFlagService: FeatureFlagService,
) {} ) {}
public async getUserWorkspacePermissions({ public async getUserWorkspacePermissions({
@ -157,108 +154,10 @@ export class PermissionsService {
return true; return true;
} }
const isPermissionsV2Enabled = const settingPermissions = roleOfUserWorkspace.settingPermissions ?? [];
await this.featureFlagService.isFeatureEnabled(
FeatureFlagKey.IS_PERMISSIONS_V2_ENABLED,
workspaceId,
);
if (isPermissionsV2Enabled) { return settingPermissions.some(
const settingPermissions = roleOfUserWorkspace.settingPermissions ?? []; (settingPermission) => settingPermission.setting === setting,
);
return settingPermissions.some(
(settingPermission) => settingPermission.setting === setting,
);
} else {
return false;
}
}
public async userHasObjectRecordsPermission({
userWorkspaceId,
workspaceId,
requiredPermission,
isExecutedByApiKey,
objectMetadataId,
}: {
userWorkspaceId?: string;
workspaceId: string;
requiredPermission: PermissionsOnAllObjectRecords;
isExecutedByApiKey: boolean;
objectMetadataId: string;
}): Promise<boolean> {
const isPermissionsV2Enabled =
await this.featureFlagService.isFeatureEnabled(
FeatureFlagKey.IS_PERMISSIONS_V2_ENABLED,
workspaceId,
);
if (isPermissionsV2Enabled) {
throw new Error(
'This should not be called once Permissions V2 is enabled',
);
}
if (isExecutedByApiKey) {
return true;
}
if (!isDefined(userWorkspaceId)) {
throw new AuthException(
'Missing userWorkspaceId or apiKey in authContext',
AuthExceptionCode.USER_WORKSPACE_NOT_FOUND,
);
}
const roleIdOfUserWorkspace =
await this.userRoleService.getRoleIdForUserWorkspace({
userWorkspaceId,
workspaceId,
});
if (!isDefined(roleIdOfUserWorkspace)) {
throw new PermissionsException(
PermissionsExceptionMessage.NO_ROLE_FOUND_FOR_USER_WORKSPACE,
PermissionsExceptionCode.NO_ROLE_FOUND_FOR_USER_WORKSPACE,
);
}
const { data: rolesPermissions } =
await this.workspacePermissionsCacheService.getRolesPermissionsFromCache({
workspaceId,
});
const rolePermissionsForUserWorkspaceRole =
rolesPermissions[roleIdOfUserWorkspace];
const objectPermissionKey =
this.getObjectPermissionKeyForRequiredPermission(requiredPermission);
const objectPermissionValue =
rolePermissionsForUserWorkspaceRole[objectMetadataId]?.[
objectPermissionKey
];
return objectPermissionValue === true;
}
private getObjectPermissionKeyForRequiredPermission(
requiredPermission: PermissionsOnAllObjectRecords,
) {
switch (requiredPermission) {
case PermissionsOnAllObjectRecords.READ_ALL_OBJECT_RECORDS:
return 'canRead';
case PermissionsOnAllObjectRecords.UPDATE_ALL_OBJECT_RECORDS:
return 'canUpdate';
case PermissionsOnAllObjectRecords.SOFT_DELETE_ALL_OBJECT_RECORDS:
return 'canSoftDelete';
case PermissionsOnAllObjectRecords.DESTROY_ALL_OBJECT_RECORDS:
return 'canDestroy';
default:
throw new PermissionsException(
PermissionsExceptionMessage.UNKNOWN_REQUIRED_PERMISSION,
PermissionsExceptionCode.UNKNOWN_REQUIRED_PERMISSION,
);
}
} }
} }

View File

@ -1,7 +1,6 @@
import { Module } from '@nestjs/common'; import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm'; import { TypeOrmModule } from '@nestjs/typeorm';
import { FeatureFlagModule } from 'src/engine/core-modules/feature-flag/feature-flag.module';
import { FileModule } from 'src/engine/core-modules/file/file.module'; import { FileModule } from 'src/engine/core-modules/file/file.module';
import { UserWorkspace } from 'src/engine/core-modules/user-workspace/user-workspace.entity'; import { UserWorkspace } from 'src/engine/core-modules/user-workspace/user-workspace.entity';
import { UserWorkspaceModule } from 'src/engine/core-modules/user-workspace/user-workspace.module'; import { UserWorkspaceModule } from 'src/engine/core-modules/user-workspace/user-workspace.module';
@ -11,19 +10,17 @@ import { PermissionsModule } from 'src/engine/metadata-modules/permissions/permi
import { RoleEntity } from 'src/engine/metadata-modules/role/role.entity'; import { RoleEntity } from 'src/engine/metadata-modules/role/role.entity';
import { RoleResolver } from 'src/engine/metadata-modules/role/role.resolver'; import { RoleResolver } from 'src/engine/metadata-modules/role/role.resolver';
import { RoleService } from 'src/engine/metadata-modules/role/role.service'; import { RoleService } from 'src/engine/metadata-modules/role/role.service';
import { UserWorkspaceRoleEntity } from 'src/engine/metadata-modules/role/user-workspace-role.entity';
import { SettingPermissionModule } from 'src/engine/metadata-modules/setting-permission/setting-permission.module'; import { SettingPermissionModule } from 'src/engine/metadata-modules/setting-permission/setting-permission.module';
import { UserRoleModule } from 'src/engine/metadata-modules/user-role/user-role.module'; import { UserRoleModule } from 'src/engine/metadata-modules/user-role/user-role.module';
import { WorkspacePermissionsCacheModule } from 'src/engine/metadata-modules/workspace-permissions-cache/workspace-permissions-cache.module'; import { WorkspacePermissionsCacheModule } from 'src/engine/metadata-modules/workspace-permissions-cache/workspace-permissions-cache.module';
@Module({ @Module({
imports: [ imports: [
TypeOrmModule.forFeature([RoleEntity, UserWorkspaceRoleEntity], 'core'), TypeOrmModule.forFeature([RoleEntity], 'core'),
TypeOrmModule.forFeature([UserWorkspace, Workspace], 'core'), TypeOrmModule.forFeature([UserWorkspace, Workspace], 'core'),
UserRoleModule, UserRoleModule,
PermissionsModule, PermissionsModule,
UserWorkspaceModule, UserWorkspaceModule,
FeatureFlagModule,
ObjectPermissionModule, ObjectPermissionModule,
SettingPermissionModule, SettingPermissionModule,
WorkspacePermissionsCacheModule, WorkspacePermissionsCacheModule,

View File

@ -8,9 +8,6 @@ import {
Resolver, Resolver,
} from '@nestjs/graphql'; } from '@nestjs/graphql';
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 { FileService } from 'src/engine/core-modules/file/services/file.service';
import { PreventNestToAutoLogGraphqlErrorsFilter } from 'src/engine/core-modules/graphql/filters/prevent-nest-to-auto-log-graphql-errors.filter'; import { PreventNestToAutoLogGraphqlErrorsFilter } from 'src/engine/core-modules/graphql/filters/prevent-nest-to-auto-log-graphql-errors.filter';
import { ResolverValidationPipe } from 'src/engine/core-modules/graphql/pipes/resolver-validation.pipe'; import { ResolverValidationPipe } from 'src/engine/core-modules/graphql/pipes/resolver-validation.pipe';
import { UserWorkspaceService } from 'src/engine/core-modules/user-workspace/user-workspace.service'; import { UserWorkspaceService } from 'src/engine/core-modules/user-workspace/user-workspace.service';
@ -56,10 +53,8 @@ export class RoleResolver {
private readonly userRoleService: UserRoleService, private readonly userRoleService: UserRoleService,
private readonly roleService: RoleService, private readonly roleService: RoleService,
private readonly userWorkspaceService: UserWorkspaceService, private readonly userWorkspaceService: UserWorkspaceService,
private readonly featureFlagService: FeatureFlagService,
private readonly objectPermissionService: ObjectPermissionService, private readonly objectPermissionService: ObjectPermissionService,
private readonly settingPermissionService: SettingPermissionService, private readonly settingPermissionService: SettingPermissionService,
private readonly fileService: FileService,
) {} ) {}
@Query(() => [RoleDTO]) @Query(() => [RoleDTO])
@ -123,8 +118,6 @@ export class RoleResolver {
@AuthWorkspace() workspace: Workspace, @AuthWorkspace() workspace: Workspace,
@Args('createRoleInput') createRoleInput: CreateRoleInput, @Args('createRoleInput') createRoleInput: CreateRoleInput,
): Promise<RoleDTO> { ): Promise<RoleDTO> {
await this.validatePermissionsV2EnabledOrThrow(workspace);
return await this.roleService.createRole({ return await this.roleService.createRole({
workspaceId: workspace.id, workspaceId: workspace.id,
input: createRoleInput, input: createRoleInput,
@ -136,8 +129,6 @@ export class RoleResolver {
@AuthWorkspace() workspace: Workspace, @AuthWorkspace() workspace: Workspace,
@Args('updateRoleInput') updateRoleInput: UpdateRoleInput, @Args('updateRoleInput') updateRoleInput: UpdateRoleInput,
): Promise<RoleDTO> { ): Promise<RoleDTO> {
await this.validatePermissionsV2EnabledOrThrow(workspace);
const role = await this.roleService.updateRole({ const role = await this.roleService.updateRole({
input: updateRoleInput, input: updateRoleInput,
workspaceId: workspace.id, workspaceId: workspace.id,
@ -151,8 +142,6 @@ export class RoleResolver {
@AuthWorkspace() workspace: Workspace, @AuthWorkspace() workspace: Workspace,
@Args('roleId') roleId: string, @Args('roleId') roleId: string,
): Promise<string> { ): Promise<string> {
await this.validatePermissionsV2EnabledOrThrow(workspace);
const deletedRoleId = await this.roleService.deleteRole( const deletedRoleId = await this.roleService.deleteRole(
roleId, roleId,
workspace.id, workspace.id,
@ -167,8 +156,6 @@ export class RoleResolver {
@Args('upsertObjectPermissionsInput') @Args('upsertObjectPermissionsInput')
upsertObjectPermissionsInput: UpsertObjectPermissionsInput, upsertObjectPermissionsInput: UpsertObjectPermissionsInput,
): Promise<ObjectPermissionDTO[]> { ): Promise<ObjectPermissionDTO[]> {
await this.validatePermissionsV2EnabledOrThrow(workspace);
return this.objectPermissionService.upsertObjectPermissions({ return this.objectPermissionService.upsertObjectPermissions({
workspaceId: workspace.id, workspaceId: workspace.id,
input: upsertObjectPermissionsInput, input: upsertObjectPermissionsInput,
@ -181,8 +168,6 @@ export class RoleResolver {
@Args('upsertSettingPermissionsInput') @Args('upsertSettingPermissionsInput')
upsertSettingPermissionsInput: UpsertSettingPermissionsInput, upsertSettingPermissionsInput: UpsertSettingPermissionsInput,
): Promise<SettingPermissionDTO[]> { ): Promise<SettingPermissionDTO[]> {
await this.validatePermissionsV2EnabledOrThrow(workspace);
return this.settingPermissionService.upsertSettingPermissions({ return this.settingPermissionService.upsertSettingPermissions({
workspaceId: workspace.id, workspaceId: workspace.id,
input: upsertSettingPermissionsInput, input: upsertSettingPermissionsInput,
@ -202,19 +187,4 @@ export class RoleResolver {
return workspaceMembers; return workspaceMembers;
} }
private async validatePermissionsV2EnabledOrThrow(workspace: Workspace) {
const isPermissionsV2Enabled =
await this.featureFlagService.isFeatureEnabled(
FeatureFlagKey.IS_PERMISSIONS_V2_ENABLED,
workspace.id,
);
if (!isPermissionsV2Enabled) {
throw new PermissionsException(
PermissionsExceptionMessage.PERMISSIONS_V2_NOT_ENABLED,
PermissionsExceptionCode.PERMISSIONS_V2_NOT_ENABLED,
);
}
}
} }

View File

@ -17,7 +17,6 @@ import {
UpdateRolePayload, UpdateRolePayload,
} from 'src/engine/metadata-modules/role/dtos/update-role-input.dto'; } from 'src/engine/metadata-modules/role/dtos/update-role-input.dto';
import { RoleEntity } from 'src/engine/metadata-modules/role/role.entity'; import { RoleEntity } from 'src/engine/metadata-modules/role/role.entity';
import { UserWorkspaceRoleEntity } from 'src/engine/metadata-modules/role/user-workspace-role.entity';
import { UserRoleService } from 'src/engine/metadata-modules/user-role/user-role.service'; import { UserRoleService } from 'src/engine/metadata-modules/user-role/user-role.service';
import { isArgDefinedIfProvidedOrThrow } from 'src/engine/metadata-modules/utils/is-arg-defined-if-provided-or-throw.util'; import { isArgDefinedIfProvidedOrThrow } from 'src/engine/metadata-modules/utils/is-arg-defined-if-provided-or-throw.util';
import { WorkspacePermissionsCacheService } from 'src/engine/metadata-modules/workspace-permissions-cache/workspace-permissions-cache.service'; import { WorkspacePermissionsCacheService } from 'src/engine/metadata-modules/workspace-permissions-cache/workspace-permissions-cache.service';
@ -28,8 +27,6 @@ export class RoleService {
private readonly workspaceRepository: Repository<Workspace>, private readonly workspaceRepository: Repository<Workspace>,
@InjectRepository(RoleEntity, 'core') @InjectRepository(RoleEntity, 'core')
private readonly roleRepository: Repository<RoleEntity>, private readonly roleRepository: Repository<RoleEntity>,
@InjectRepository(UserWorkspaceRoleEntity, 'core')
private readonly userWorkspaceRoleRepository: Repository<UserWorkspaceRoleEntity>,
private readonly userRoleService: UserRoleService, private readonly userRoleService: UserRoleService,
private readonly workspacePermissionsCacheService: WorkspacePermissionsCacheService, private readonly workspacePermissionsCacheService: WorkspacePermissionsCacheService,
) {} ) {}

View File

@ -8,7 +8,6 @@ import {
import { isDefined } from 'twenty-shared/utils'; import { isDefined } from 'twenty-shared/utils';
import { In, Repository } from 'typeorm'; import { In, Repository } from 'typeorm';
import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum';
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity'; import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
import { SettingPermissionType } from 'src/engine/metadata-modules/permissions/constants/setting-permission-type.constants'; import { SettingPermissionType } from 'src/engine/metadata-modules/permissions/constants/setting-permission-type.constants';
import { RoleEntity } from 'src/engine/metadata-modules/role/role.entity'; import { RoleEntity } from 'src/engine/metadata-modules/role/role.entity';
@ -85,18 +84,9 @@ export class WorkspacePermissionsCacheService {
); );
} }
const workspaceFeatureFlagsMap =
await this.workspaceFeatureFlagsMapCacheService.getWorkspaceFeatureFlagsMap(
{ workspaceId },
);
const isPermissionsV2Enabled =
!!workspaceFeatureFlagsMap[FeatureFlagKey.IS_PERMISSIONS_V2_ENABLED];
const recomputedRolesPermissions = const recomputedRolesPermissions =
await this.getObjectRecordPermissionsForRoles({ await this.getObjectRecordPermissionsForRoles({
workspaceId, workspaceId,
isPermissionsV2Enabled,
roleIds, roleIds,
}); });
@ -232,11 +222,9 @@ export class WorkspacePermissionsCacheService {
private async getObjectRecordPermissionsForRoles({ private async getObjectRecordPermissionsForRoles({
workspaceId, workspaceId,
isPermissionsV2Enabled,
roleIds, roleIds,
}: { }: {
workspaceId: string; workspaceId: string;
isPermissionsV2Enabled: boolean;
roleIds?: string[]; roleIds?: string[];
}): Promise<ObjectRecordsPermissionsByRoleId> { }): Promise<ObjectRecordsPermissionsByRoleId> {
let roles: RoleEntity[] = []; let roles: RoleEntity[] = [];
@ -265,49 +253,47 @@ export class WorkspacePermissionsCacheService {
let canSoftDelete = role.canSoftDeleteAllObjectRecords; let canSoftDelete = role.canSoftDeleteAllObjectRecords;
let canDestroy = role.canDestroyAllObjectRecords; let canDestroy = role.canDestroyAllObjectRecords;
if (isPermissionsV2Enabled) { if (
if ( standardId &&
standardId && [
[ STANDARD_OBJECT_IDS.workflow,
STANDARD_OBJECT_IDS.workflow, STANDARD_OBJECT_IDS.workflowRun,
STANDARD_OBJECT_IDS.workflowRun, STANDARD_OBJECT_IDS.workflowVersion,
STANDARD_OBJECT_IDS.workflowVersion, ].includes(standardId)
].includes(standardId) ) {
) { const hasWorkflowsPermissions = this.hasWorkflowsPermissions(role);
const hasWorkflowsPermissions = this.hasWorkflowsPermissions(role);
canRead = hasWorkflowsPermissions; canRead = hasWorkflowsPermissions;
canUpdate = hasWorkflowsPermissions; canUpdate = hasWorkflowsPermissions;
canSoftDelete = hasWorkflowsPermissions; canSoftDelete = hasWorkflowsPermissions;
canDestroy = hasWorkflowsPermissions; canDestroy = hasWorkflowsPermissions;
} else { } else {
const objectRecordPermissionsOverride = role.objectPermissions.find( const objectRecordPermissionsOverride = role.objectPermissions.find(
(objectPermission) => (objectPermission) =>
objectPermission.objectMetadataId === objectMetadataId, objectPermission.objectMetadataId === objectMetadataId,
); );
const getPermissionValue = ( const getPermissionValue = (
overrideValue: boolean | undefined, overrideValue: boolean | undefined,
defaultValue: boolean, defaultValue: boolean,
) => (isSystem ? true : (overrideValue ?? defaultValue)); ) => (isSystem ? true : (overrideValue ?? defaultValue));
canRead = getPermissionValue( canRead = getPermissionValue(
objectRecordPermissionsOverride?.canReadObjectRecords, objectRecordPermissionsOverride?.canReadObjectRecords,
canRead, canRead,
); );
canUpdate = getPermissionValue( canUpdate = getPermissionValue(
objectRecordPermissionsOverride?.canUpdateObjectRecords, objectRecordPermissionsOverride?.canUpdateObjectRecords,
canUpdate, canUpdate,
); );
canSoftDelete = getPermissionValue( canSoftDelete = getPermissionValue(
objectRecordPermissionsOverride?.canSoftDeleteObjectRecords, objectRecordPermissionsOverride?.canSoftDeleteObjectRecords,
canSoftDelete, canSoftDelete,
); );
canDestroy = getPermissionValue( canDestroy = getPermissionValue(
objectRecordPermissionsOverride?.canDestroyObjectRecords, objectRecordPermissionsOverride?.canDestroyObjectRecords,
canDestroy, canDestroy,
); );
}
} }
objectRecordsPermissions[objectMetadataId] = { objectRecordsPermissions[objectMetadataId] = {

View File

@ -15,7 +15,6 @@ import { EntityManagerFactory } from 'typeorm/entity-manager/EntityManagerFactor
import { FeatureFlagMap } from 'src/engine/core-modules/feature-flag/interfaces/feature-flag-map.interface'; import { FeatureFlagMap } from 'src/engine/core-modules/feature-flag/interfaces/feature-flag-map.interface';
import { WorkspaceInternalContext } from 'src/engine/twenty-orm/interfaces/workspace-internal-context.interface'; import { WorkspaceInternalContext } from 'src/engine/twenty-orm/interfaces/workspace-internal-context.interface';
import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum';
import { import {
PermissionsException, PermissionsException,
PermissionsExceptionCode, PermissionsExceptionCode,
@ -168,28 +167,22 @@ export class WorkspaceDataSource extends DataSource {
): SelectQueryBuilder<any> { ): SelectQueryBuilder<any> {
let calledByWorkspaceEntityManager; let calledByWorkspaceEntityManager;
const isPermissionsV2Enabled =
this.featureFlagMap[FeatureFlagKey.IS_PERMISSIONS_V2_ENABLED];
const isCalledWithEntityTarget = const isCalledWithEntityTarget =
isDefined(aliasOrOptions) && typeof aliasOrOptions === 'string'; isDefined(aliasOrOptions) && typeof aliasOrOptions === 'string';
if (isPermissionsV2Enabled) { if (isCalledWithEntityTarget) {
if (isCalledWithEntityTarget) { calledByWorkspaceEntityManager = options?.calledByWorkspaceEntityManager;
calledByWorkspaceEntityManager = } else {
options?.calledByWorkspaceEntityManager; calledByWorkspaceEntityManager = (
} else { aliasOrOptions as CreateQueryBuilderOptions
calledByWorkspaceEntityManager = ( )?.calledByWorkspaceEntityManager;
aliasOrOptions as CreateQueryBuilderOptions }
)?.calledByWorkspaceEntityManager;
}
if (!(calledByWorkspaceEntityManager === true)) { if (!(calledByWorkspaceEntityManager === true)) {
throw new PermissionsException( throw new PermissionsException(
'Method not allowed because permissions are not implemented at datasource level.', 'Method not allowed because permissions are not implemented at datasource level.',
PermissionsExceptionCode.METHOD_NOT_ALLOWED, PermissionsExceptionCode.METHOD_NOT_ALLOWED,
); );
}
} }
if (isCalledWithEntityTarget) { if (isCalledWithEntityTarget) {

View File

@ -5,7 +5,6 @@ import { PlainObjectToDatabaseEntityTransformer } from 'typeorm/query-builder/tr
import { WorkspaceInternalContext } from 'src/engine/twenty-orm/interfaces/workspace-internal-context.interface'; import { WorkspaceInternalContext } from 'src/engine/twenty-orm/interfaces/workspace-internal-context.interface';
import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum';
import { WorkspaceDataSource } from 'src/engine/twenty-orm/datasource/workspace.datasource'; import { WorkspaceDataSource } from 'src/engine/twenty-orm/datasource/workspace.datasource';
import { validateOperationIsPermittedOrThrow } from 'src/engine/twenty-orm/repository/permissions.utils'; import { validateOperationIsPermittedOrThrow } from 'src/engine/twenty-orm/repository/permissions.utils';
@ -58,15 +57,10 @@ describe('WorkspaceEntityManager', () => {
objectMetadataMaps: { objectMetadataMaps: {
idByNameSingular: {}, idByNameSingular: {},
}, },
featureFlagsMap: {
[FeatureFlagKey.IS_PERMISSIONS_V2_ENABLED]: true,
},
} as WorkspaceInternalContext; } as WorkspaceInternalContext;
mockDataSource = { mockDataSource = {
featureFlagMap: { featureFlagMap: {},
[FeatureFlagKey.IS_PERMISSIONS_V2_ENABLED]: true,
},
permissionsPerRoleId: {}, permissionsPerRoleId: {},
} as WorkspaceDataSource; } as WorkspaceDataSource;
@ -141,11 +135,6 @@ describe('WorkspaceEntityManager', () => {
return entityName; return entityName;
}); });
// Mock getFeatureFlagMap
jest.spyOn(entityManager as any, 'getFeatureFlagMap').mockReturnValue({
[FeatureFlagKey.IS_PERMISSIONS_V2_ENABLED]: true,
});
// Mock typeORM's EntityManager methods // Mock typeORM's EntityManager methods
jest jest
.spyOn(EntityManager.prototype, 'save') .spyOn(EntityManager.prototype, 'save')

View File

@ -32,7 +32,6 @@ import { InstanceChecker } from 'typeorm/util/InstanceChecker';
import { FeatureFlagMap } from 'src/engine/core-modules/feature-flag/interfaces/feature-flag-map.interface'; import { FeatureFlagMap } from 'src/engine/core-modules/feature-flag/interfaces/feature-flag-map.interface';
import { WorkspaceInternalContext } from 'src/engine/twenty-orm/interfaces/workspace-internal-context.interface'; import { WorkspaceInternalContext } from 'src/engine/twenty-orm/interfaces/workspace-internal-context.interface';
import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum';
import { import {
PermissionsException, PermissionsException,
PermissionsExceptionCode, PermissionsExceptionCode,
@ -94,25 +93,18 @@ export class WorkspaceEntityManager extends EntityManager {
let objectPermissions = {}; let objectPermissions = {};
const featureFlagMap = this.getFeatureFlagMap();
const isPermissionsV2Enabled =
featureFlagMap[FeatureFlagKey.IS_PERMISSIONS_V2_ENABLED];
if (permissionOptions?.roleId) { if (permissionOptions?.roleId) {
const objectPermissionsByRoleId = dataSource.permissionsPerRoleId; const objectPermissionsByRoleId = dataSource.permissionsPerRoleId;
if (!isDefined(objectPermissionsByRoleId?.[permissionOptions.roleId])) { if (!isDefined(objectPermissionsByRoleId?.[permissionOptions.roleId])) {
if (isPermissionsV2Enabled) { throw new PermissionsException(
throw new PermissionsException( `No permissions found for role in datasource (missing ${
`No permissions found for role in datasource (missing ${ !isDefined(objectPermissionsByRoleId)
!isDefined(objectPermissionsByRoleId) ? 'objectPermissionsByRoleId object'
? 'objectPermissionsByRoleId object' : `roleId in objectPermissionsByRoleId object (${permissionOptions.roleId})`
: `roleId in objectPermissionsByRoleId object (${permissionOptions.roleId})` })`,
})`, PermissionsExceptionCode.NO_PERMISSIONS_FOUND_IN_DATASOURCE,
PermissionsExceptionCode.NO_PERMISSIONS_FOUND_IN_DATASOURCE, );
);
}
} else { } else {
objectPermissions = objectPermissionsByRoleId[permissionOptions.roleId]; objectPermissions = objectPermissionsByRoleId[permissionOptions.roleId];
} }
@ -165,21 +157,12 @@ export class WorkspaceEntityManager extends EntityManager {
); );
} }
const featureFlagMap = this.getFeatureFlagMap(); return new WorkspaceSelectQueryBuilder(
queryBuilder,
const isPermissionsV2Enabled = options?.objectRecordsPermissions ?? {},
featureFlagMap[FeatureFlagKey.IS_PERMISSIONS_V2_ENABLED]; this.internalContext,
options?.shouldBypassPermissionChecks ?? false,
if (!isPermissionsV2Enabled) { );
return queryBuilder;
} else {
return new WorkspaceSelectQueryBuilder(
queryBuilder,
options?.objectRecordsPermissions ?? {},
this.internalContext,
options?.shouldBypassPermissionChecks ?? false,
);
}
} }
override insert<Entity extends ObjectLiteral>( override insert<Entity extends ObjectLiteral>(
@ -391,15 +374,6 @@ export class WorkspaceEntityManager extends EntityManager {
objectRecordsPermissions?: ObjectRecordsPermissions; objectRecordsPermissions?: ObjectRecordsPermissions;
}, },
): void { ): void {
const featureFlagMap = this.getFeatureFlagMap();
const isPermissionsV2Enabled =
featureFlagMap[FeatureFlagKey.IS_PERMISSIONS_V2_ENABLED];
if (!isPermissionsV2Enabled) {
return;
}
if (permissionOptions?.shouldBypassPermissionChecks === true) { if (permissionOptions?.shouldBypassPermissionChecks === true) {
return; return;
} }

View File

@ -23,7 +23,6 @@ import { UpsertOptions } from 'typeorm/repository/UpsertOptions';
import { FeatureFlagMap } from 'src/engine/core-modules/feature-flag/interfaces/feature-flag-map.interface'; import { FeatureFlagMap } from 'src/engine/core-modules/feature-flag/interfaces/feature-flag-map.interface';
import { WorkspaceInternalContext } from 'src/engine/twenty-orm/interfaces/workspace-internal-context.interface'; import { WorkspaceInternalContext } from 'src/engine/twenty-orm/interfaces/workspace-internal-context.interface';
import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum';
import { import {
PermissionsException, PermissionsException,
PermissionsExceptionCode, PermissionsExceptionCode,
@ -70,23 +69,17 @@ export class WorkspaceRepository<
alias, alias,
queryRunner, queryRunner,
) as unknown as WorkspaceSelectQueryBuilder<U>; ) as unknown as WorkspaceSelectQueryBuilder<U>;
const isPermissionsV2Enabled =
this.featureFlagMap[FeatureFlagKey.IS_PERMISSIONS_V2_ENABLED];
if (!isPermissionsV2Enabled) { if (!this.objectRecordsPermissions) {
return queryBuilder; throw new Error('Object records permissions are required');
} else {
if (!this.objectRecordsPermissions) {
throw new Error('Object records permissions are required');
}
return new WorkspaceSelectQueryBuilder(
queryBuilder,
this.objectRecordsPermissions,
this.internalContext,
this.shouldBypassPermissionChecks,
);
} }
return new WorkspaceSelectQueryBuilder(
queryBuilder,
this.objectRecordsPermissions,
this.internalContext,
this.shouldBypassPermissionChecks,
);
} }
/** /**

View File

@ -40,11 +40,6 @@ export const seedFeatureFlags = async (
workspaceId: workspaceId, workspaceId: workspaceId,
value: true, value: true,
}, },
{
key: FeatureFlagKey.IS_PERMISSIONS_V2_ENABLED,
workspaceId: workspaceId,
value: true,
},
]) ])
.execute(); .execute();
}; };

View File

@ -3,7 +3,6 @@ import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm'; import { Repository } from 'typeorm';
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 { FeatureFlagService } from 'src/engine/core-modules/feature-flag/services/feature-flag.service';
import { UserWorkspace } from 'src/engine/core-modules/user-workspace/user-workspace.entity'; import { UserWorkspace } from 'src/engine/core-modules/user-workspace/user-workspace.entity';
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
@ -194,10 +193,5 @@ export class WorkspaceManagerService {
await this.workspaceRepository.update(workspaceId, { await this.workspaceRepository.update(workspaceId, {
defaultRoleId: memberRole.id, defaultRoleId: memberRole.id,
}); });
await this.featureFlagService.enableFeatureFlags(
[FeatureFlagKey.IS_PERMISSIONS_V2_ENABLED],
workspaceId,
);
} }
} }