diff --git a/packages/twenty-front/src/generated-metadata/graphql.ts b/packages/twenty-front/src/generated-metadata/graphql.ts index 031b761496..53fecc4c44 100644 --- a/packages/twenty-front/src/generated-metadata/graphql.ts +++ b/packages/twenty-front/src/generated-metadata/graphql.ts @@ -135,7 +135,7 @@ export type ApiKey = { id: Scalars['UUID']; name: Scalars['String']; revokedAt?: Maybe; - role?: Maybe; + role: Role; updatedAt: Scalars['DateTime']; workspace: Workspace; workspaceId: Scalars['UUID']; @@ -999,7 +999,6 @@ export type FeatureFlagDto = { export enum FeatureFlagKey { IS_AIRTABLE_INTEGRATION_ENABLED = 'IS_AIRTABLE_INTEGRATION_ENABLED', IS_AI_ENABLED = 'IS_AI_ENABLED', - IS_API_KEY_ROLES_ENABLED = 'IS_API_KEY_ROLES_ENABLED', IS_CORE_VIEW_ENABLED = 'IS_CORE_VIEW_ENABLED', IS_CORE_VIEW_SYNCING_ENABLED = 'IS_CORE_VIEW_SYNCING_ENABLED', IS_DATABASE_EVENT_TRIGGER_ENABLED = 'IS_DATABASE_EVENT_TRIGGER_ENABLED', @@ -4644,7 +4643,7 @@ export type GetSystemHealthStatusQueryVariables = Exact<{ [key: string]: never; export type GetSystemHealthStatusQuery = { __typename?: 'Query', getSystemHealthStatus: { __typename?: 'SystemHealth', services: Array<{ __typename?: 'SystemHealthService', id: HealthIndicatorId, label: string, status: AdminPanelHealthServiceStatus }> } }; -export type ApiKeyFragmentFragment = { __typename?: 'ApiKey', id: string, name: string, expiresAt: string, revokedAt?: string | null, role?: { __typename?: 'Role', id: string, label: string, icon?: string | null } | null }; +export type ApiKeyFragmentFragment = { __typename?: 'ApiKey', id: string, name: string, expiresAt: string, revokedAt?: string | null, role: { __typename?: 'Role', id: string, label: string, icon?: string | null } }; export type WebhookFragmentFragment = { __typename?: 'Webhook', id: string, targetUrl: string, operations: Array, description?: string | null, secret: string }; @@ -4661,7 +4660,7 @@ export type CreateApiKeyMutationVariables = Exact<{ }>; -export type CreateApiKeyMutation = { __typename?: 'Mutation', createApiKey: { __typename?: 'ApiKey', id: string, name: string, expiresAt: string, revokedAt?: string | null, role?: { __typename?: 'Role', id: string, label: string, icon?: string | null } | null } }; +export type CreateApiKeyMutation = { __typename?: 'Mutation', createApiKey: { __typename?: 'ApiKey', id: string, name: string, expiresAt: string, revokedAt?: string | null, role: { __typename?: 'Role', id: string, label: string, icon?: string | null } } }; export type CreateWebhookMutationVariables = Exact<{ input: CreateWebhookDto; @@ -4689,7 +4688,7 @@ export type UpdateApiKeyMutationVariables = Exact<{ }>; -export type UpdateApiKeyMutation = { __typename?: 'Mutation', updateApiKey?: { __typename?: 'ApiKey', id: string, name: string, expiresAt: string, revokedAt?: string | null, role?: { __typename?: 'Role', id: string, label: string, icon?: string | null } | null } | null }; +export type UpdateApiKeyMutation = { __typename?: 'Mutation', updateApiKey?: { __typename?: 'ApiKey', id: string, name: string, expiresAt: string, revokedAt?: string | null, role: { __typename?: 'Role', id: string, label: string, icon?: string | null } } | null }; export type UpdateWebhookMutationVariables = Exact<{ input: UpdateWebhookDto; @@ -4703,12 +4702,12 @@ export type GetApiKeyQueryVariables = Exact<{ }>; -export type GetApiKeyQuery = { __typename?: 'Query', apiKey?: { __typename?: 'ApiKey', createdAt: string, id: string, name: string, expiresAt: string, revokedAt?: string | null, role?: { __typename?: 'Role', id: string, label: string, icon?: string | null } | null } | null }; +export type GetApiKeyQuery = { __typename?: 'Query', apiKey?: { __typename?: 'ApiKey', createdAt: string, id: string, name: string, expiresAt: string, revokedAt?: string | null, role: { __typename?: 'Role', id: string, label: string, icon?: string | null } } | null }; export type GetApiKeysQueryVariables = Exact<{ [key: string]: never; }>; -export type GetApiKeysQuery = { __typename?: 'Query', apiKeys: Array<{ __typename?: 'ApiKey', id: string, name: string, expiresAt: string, revokedAt?: string | null, role?: { __typename?: 'Role', id: string, label: string, icon?: string | null } | null }> }; +export type GetApiKeysQuery = { __typename?: 'Query', apiKeys: Array<{ __typename?: 'ApiKey', id: string, name: string, expiresAt: string, revokedAt?: string | null, role: { __typename?: 'Role', id: string, label: string, icon?: string | null } }> }; export type GetWebhookQueryVariables = Exact<{ input: GetWebhookDto; diff --git a/packages/twenty-front/src/generated/graphql.ts b/packages/twenty-front/src/generated/graphql.ts index d0a81b3907..b511f0cb39 100644 --- a/packages/twenty-front/src/generated/graphql.ts +++ b/packages/twenty-front/src/generated/graphql.ts @@ -135,7 +135,7 @@ export type ApiKey = { id: Scalars['UUID']; name: Scalars['String']; revokedAt?: Maybe; - role?: Maybe; + role: Role; updatedAt: Scalars['DateTime']; workspace: Workspace; workspaceId: Scalars['UUID']; @@ -963,7 +963,6 @@ export type FeatureFlagDto = { export enum FeatureFlagKey { IS_AIRTABLE_INTEGRATION_ENABLED = 'IS_AIRTABLE_INTEGRATION_ENABLED', IS_AI_ENABLED = 'IS_AI_ENABLED', - IS_API_KEY_ROLES_ENABLED = 'IS_API_KEY_ROLES_ENABLED', IS_CORE_VIEW_ENABLED = 'IS_CORE_VIEW_ENABLED', IS_CORE_VIEW_SYNCING_ENABLED = 'IS_CORE_VIEW_SYNCING_ENABLED', IS_DATABASE_EVENT_TRIGGER_ENABLED = 'IS_DATABASE_EVENT_TRIGGER_ENABLED', diff --git a/packages/twenty-front/src/modules/settings/developers/components/SettingsApiKeysFieldItemTableRow.tsx b/packages/twenty-front/src/modules/settings/developers/components/SettingsApiKeysFieldItemTableRow.tsx index c6317ca004..d756d5b3c9 100644 --- a/packages/twenty-front/src/modules/settings/developers/components/SettingsApiKeysFieldItemTableRow.tsx +++ b/packages/twenty-front/src/modules/settings/developers/components/SettingsApiKeysFieldItemTableRow.tsx @@ -7,10 +7,9 @@ import { } from '@/settings/developers/utils/formatExpiration'; import { TableCell } from '@/ui/layout/table/components/TableCell'; import { TableRow } from '@/ui/layout/table/components/TableRow'; -import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled'; import { IconChevronRight } from 'twenty-ui/display'; import { MOBILE_VIEWPORT } from 'twenty-ui/theme'; -import { type ApiKey, FeatureFlagKey } from '~/generated-metadata/graphql'; +import { type ApiKey } from '~/generated-metadata/graphql'; export const StyledApisFieldTableRow = styled(TableRow)` @media (max-width: ${MOBILE_VIEWPORT}px) { @@ -47,11 +46,7 @@ export const SettingsApiKeysFieldItemTableRow = ({ const theme = useTheme(); const formattedExpiration = formatExpiration(apiKey.expiresAt || null); - const isApiKeyRolesEnabled = useIsFeatureEnabled( - FeatureFlagKey.IS_API_KEY_ROLES_ENABLED, - ); - - const gridColumns = isApiKeyRolesEnabled ? '5fr 2fr 3fr 1fr' : '5fr 3fr 1fr'; + const gridColumns = '5fr 2fr 3fr 1fr'; return ( @@ -59,11 +54,9 @@ export const SettingsApiKeysFieldItemTableRow = ({ {apiKey.name} - {isApiKeyRolesEnabled && ( - - {apiKey.role?.label || '-'} - - )} + + {apiKey.role?.label || '-'} + theme.border.color.light}; `; export const SettingsApiKeysTable = () => { - const isApiKeyRolesEnabled = useIsFeatureEnabled( - FeatureFlagKey.IS_API_KEY_ROLES_ENABLED, - ); - const { data: apiKeysData } = useGetApiKeysQuery(); const apiKeys = apiKeysData?.apiKeys; - const gridAutoColumns = isApiKeyRolesEnabled - ? '5fr 2fr 3fr 1fr' - : '5fr 3fr 1fr'; + const gridAutoColumns = '5fr 2fr 3fr 1fr'; return ( @@ -36,11 +26,9 @@ export const SettingsApiKeysTable = () => { Name - {isApiKeyRolesEnabled && ( - - Role - - )} + + Role + Expiration diff --git a/packages/twenty-front/src/pages/settings/developers/api-keys/SettingsDevelopersApiKeyDetail.tsx b/packages/twenty-front/src/pages/settings/developers/api-keys/SettingsDevelopersApiKeyDetail.tsx index dbb0db8555..d0f5bd36ed 100644 --- a/packages/twenty-front/src/pages/settings/developers/api-keys/SettingsDevelopersApiKeyDetail.tsx +++ b/packages/twenty-front/src/pages/settings/developers/api-keys/SettingsDevelopersApiKeyDetail.tsx @@ -17,7 +17,6 @@ import { SettingsTextInput } from '@/ui/input/components/SettingsTextInput'; import { ConfirmationModal } from '@/ui/layout/modal/components/ConfirmationModal'; import { useModal } from '@/ui/layout/modal/hooks/useModal'; import { SubMenuTopBarContainer } from '@/ui/layout/page/components/SubMenuTopBarContainer'; -import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled'; import { Trans, useLingui } from '@lingui/react/macro'; import { SettingsPath } from 'twenty-shared/types'; import { getSettingsPath, isDefined } from 'twenty-shared/utils'; @@ -25,7 +24,6 @@ import { H2Title, IconRepeat, IconTrash } from 'twenty-ui/display'; import { Button } from 'twenty-ui/input'; import { Section } from 'twenty-ui/layout'; import { - FeatureFlagKey, useAssignRoleToApiKeyMutation, useCreateApiKeyMutation, useGenerateApiKeyTokenMutation, @@ -71,10 +69,6 @@ export const SettingsDevelopersApiKeyDetail = () => { [], ); - const isApiKeyRolesEnabled = useIsFeatureEnabled( - FeatureFlagKey.IS_API_KEY_ROLES_ENABLED, - ); - const [generateOneApiKeyToken] = useGenerateApiKeyTokenMutation(); const [createApiKey] = useCreateApiKeyMutation(); const [revokeApiKey] = useRevokeApiKeyMutation(); @@ -155,10 +149,9 @@ export const SettingsDevelopersApiKeyDetail = () => { name: string, newExpiresAt: string | null, ) => { - const adminRole = roles.find((role) => role.label === 'Admin'); - const roleIdToUse = isApiKeyRolesEnabled ? selectedRoleId : adminRole?.id; + const roleIdToUse = selectedRoleId; - if (!roleIdToUse && isApiKeyRolesEnabled) { + if (!roleIdToUse) { enqueueErrorSnackBar({ message: t`A role must be selected for the API key`, }); @@ -166,7 +159,7 @@ export const SettingsDevelopersApiKeyDetail = () => { } if (!isDefined(roleIdToUse)) { - throw new Error('Admin role not found - this should never happen'); + throw new Error('Role not selected - this should never happen'); } const { data: newApiKeyData } = await createApiKey({ @@ -285,19 +278,17 @@ export const SettingsDevelopersApiKeyDetail = () => { onNameUpdate={setApiKeyName} /> - {isApiKeyRolesEnabled && ( -
- - -
- )} +
+ + +
{ const currentWorkspace = useRecoilValue(currentWorkspaceState); const { data: rolesData, loading: rolesLoading } = useGetRolesQuery({ onCompleted: (data) => { - if (isApiKeyRolesEnabled && isDefined(data?.getRoles)) { + if (isDefined(data?.getRoles)) { const defaultRole = data.getRoles.find( (role) => role.id === currentWorkspace?.defaultRole?.id, ); @@ -52,11 +50,6 @@ export const SettingsDevelopersApiKeysNew = () => { }, }); const roles = rolesData?.getRoles ?? []; - const isApiKeyRolesEnabled = useIsFeatureEnabled( - FeatureFlagKey.IS_API_KEY_ROLES_ENABLED, - ); - - const adminRole = roles.find((role) => role.label === 'Admin'); const [formValues, setFormValues] = useState<{ name: string; @@ -83,9 +76,7 @@ export const SettingsDevelopersApiKeysNew = () => { .plus({ days: formValues.expirationDate ?? 30 }) .toString(); - const roleIdToUse = isApiKeyRolesEnabled - ? formValues.roleId - : adminRole?.id; + const roleIdToUse = formValues.roleId; if (!roleIdToUse) { return; @@ -125,9 +116,7 @@ export const SettingsDevelopersApiKeysNew = () => { } }; - const canSave = isApiKeyRolesEnabled - ? !!formValues.name && !!formValues.roleId && createApiKey - : !!formValues.name && !!adminRole?.id && createApiKey; + const canSave = !!formValues.name && !!formValues.roleId && createApiKey; if (rolesLoading) { return ; @@ -178,24 +167,22 @@ export const SettingsDevelopersApiKeysNew = () => { fullWidth />
- {isApiKeyRolesEnabled && ( -
- - { - setFormValues((prevState) => ({ - ...prevState, - roleId, - })); - }} - roles={roles} - /> -
- )} +
+ + { + setFormValues((prevState) => ({ + ...prevState, + roleId, + })); + }} + roles={roles} + /> +
, - @InjectRepository(ApiKey) - private readonly apiKeyRepository: Repository, - @InjectRepository(RoleEntity) - private readonly roleRepository: Repository, - @InjectRepository(RoleTargetsEntity) - private readonly roleTargetsRepository: Repository, - private readonly featureFlagService: FeatureFlagService, - private readonly workspacePermissionsCacheService: WorkspacePermissionsCacheService, - private readonly workspaceFeatureFlagsMapCacheService: WorkspaceFeatureFlagsMapCacheService, - protected readonly twentyORMGlobalManager: TwentyORMGlobalManager, - @InjectDataSource() - private readonly dataSource: DataSource, - ) { - super(workspaceRepository, twentyORMGlobalManager); - } - - override async runOnWorkspace({ - index, - total, - workspaceId, - options, - }: RunOnWorkspaceArgs): Promise { - this.logger.log( - `Assigning roles to existing API keys for workspace ${workspaceId} ${index + 1}/${total}`, - ); - - const queryRunner = this.dataSource.createQueryRunner(); - - await queryRunner.connect(); - - try { - await queryRunner.startTransaction(); - - try { - const result = await this.assignRolesToWorkspaceApiKeys( - workspaceId, - options.dryRun ?? false, - queryRunner, - ); - - if (result.failed.length > 0) { - this.logger.warn( - `Workspace ${workspaceId}: Processed ${result.processed}, Assigned roles to ${result.assigned} API keys, Failed: ${result.failed.length}`, - ); - this.logger.warn( - `Failed API keys: ${result.failed.map((f) => `${f.name} (${f.id}): ${f.error}`).join(', ')}`, - ); - throw new Error( - `Failed to assign roles to ${result.failed.length} API keys`, - ); - } - - this.logger.log( - `Workspace ${workspaceId}: Processed ${result.processed}, Assigned roles to ${result.assigned} API keys`, - ); - - if (options.dryRun) { - this.logger.log( - `DRY RUN: Would enable IS_API_KEY_ROLES_ENABLED feature flag for workspace ${workspaceId}`, - ); - } else { - const shouldEnableFeatureFlagAndRecomputeCache = - result.assigned > 0 || - (await this.shouldEnableForZeroApiKeys(workspaceId)); - - if (shouldEnableFeatureFlagAndRecomputeCache) { - await this.enableApiKeyRolesFeatureFlagWithTransaction( - workspaceId, - result, - queryRunner, - ); - } else { - this.logger.log( - `All API keys already have roles and feature flag IS_API_KEY_ROLES_ENABLED already enabled for workspace ${workspaceId}, no action needed`, - ); - } - - await queryRunner.commitTransaction(); - - if (shouldEnableFeatureFlagAndRecomputeCache) { - try { - await this.workspacePermissionsCacheService.recomputeApiKeyRoleMapCache( - { - workspaceId, - }, - ); - await this.workspaceFeatureFlagsMapCacheService.recomputeFeatureFlagsMapCache( - { - workspaceId, - }, - ); - this.logger.log( - `Recomputed API key role cache and feature flag cache for workspace ${workspaceId}`, - ); - } catch (error) { - this.logger.warn( - `Failed to recompute API key role cache and feature flag cache for workspace ${workspaceId}: ${error.message}`, - ); - } - } - } - } catch (error) { - if (queryRunner.isTransactionActive) { - await queryRunner.rollbackTransaction(); - this.logger.error( - `Transaction rolled back for workspace ${workspaceId} due to error: ${error.message}`, - ); - } - - throw error; - } - } catch (error) { - this.logger.error( - `Failed to assign roles to existing API keys for workspace ${workspaceId}: ${error.message}`, - ); - - throw error; - } finally { - await queryRunner.release(); - } - } - - private async assignRolesToWorkspaceApiKeys( - workspaceId: string, - dryRun: boolean, - queryRunner: QueryRunner, - ): Promise<{ - processed: number; - assigned: number; - failed: Array<{ id: string; name: string; error: string }>; - }> { - const apiKeys = await this.apiKeyRepository.find({ - where: { workspaceId }, - select: ['id', 'name', 'workspaceId'], - }); - - if (apiKeys.length === 0) { - this.logger.log(`No API keys found in workspace ${workspaceId}`); - - return { processed: 0, assigned: 0, failed: [] }; - } - - this.logger.log( - `Found ${apiKeys.length} API keys in workspace ${workspaceId}`, - ); - - const apiKeyIds = apiKeys.map((key) => key.id); - const existingRoleTargets = await this.roleTargetsRepository.find({ - where: { - workspaceId, - apiKeyId: In(apiKeyIds), - }, - }); - - const apiKeysWithRoles = new Set( - existingRoleTargets.map((rt) => rt.apiKeyId), - ); - const apiKeysWithoutRoles = apiKeys.filter( - (key) => !apiKeysWithRoles.has(key.id), - ); - - if (apiKeysWithoutRoles.length === 0) { - this.logger.log( - `All API keys already have role assignments for workspace ${workspaceId}`, - ); - - return { processed: apiKeys.length, assigned: 0, failed: [] }; - } - - this.logger.log( - `${dryRun ? 'DRY RUN: ' : ''}Found ${apiKeysWithoutRoles.length} API keys without role assignments for workspace ${workspaceId}`, - ); - - if (dryRun) { - this.logger.log( - `DRY RUN: Would assign Admin roles to ${apiKeysWithoutRoles.length} API keys for workspace ${workspaceId}`, - ); - - return { - processed: apiKeys.length, - assigned: apiKeysWithoutRoles.length, - failed: [], - }; - } - - const adminRole = await this.roleRepository.findOne({ - where: { - workspaceId, - standardId: ADMIN_ROLE.standardId, - }, - }); - - if (!adminRole) { - throw new Error( - `No Admin role found in workspace ${workspaceId}. Should not happen.`, - ); - } - - this.logger.log( - `Using Admin role ${adminRole.id} for workspace ${workspaceId}`, - ); - - try { - let assignedCount = 0; - - for (const apiKey of apiKeysWithoutRoles) { - await queryRunner.manager.delete(RoleTargetsEntity, { - apiKeyId: apiKey.id, - workspaceId, - }); - - const roleTarget = queryRunner.manager.create(RoleTargetsEntity, { - apiKeyId: apiKey.id, - roleId: adminRole.id, - workspaceId, - }); - - await queryRunner.manager.save(roleTarget); - - this.logger.log( - `Assigned Admin role to API key "${apiKey.name}" (${apiKey.id})`, - ); - assignedCount++; - } - - this.logger.log( - `Successfully assigned roles to ${assignedCount} API keys for workspace ${workspaceId}`, - ); - - return { - processed: apiKeys.length, - assigned: assignedCount, - failed: [], - }; - } catch (error) { - this.logger.error( - `Failed to assign roles to API keys for workspace ${workspaceId}: ${error.message}`, - ); - - const errorMessage = - error instanceof Error ? error.message : String(error); - const failedApiKeys = apiKeysWithoutRoles.map((apiKey) => ({ - id: apiKey.id, - name: apiKey.name, - error: errorMessage, - })); - - return { - processed: apiKeys.length, - assigned: 0, - failed: failedApiKeys, - }; - } - } - - private async enableApiKeyRolesFeatureFlagWithTransaction( - workspaceId: string, - result: { processed: number; assigned: number }, - queryRunner: QueryRunner, - ): Promise { - const shouldEnableFeatureFlag = - result.processed > 0 || - (await this.shouldEnableForZeroApiKeys(workspaceId)); - - if (shouldEnableFeatureFlag) { - try { - const existingFeatureFlag = await queryRunner.manager.findOne( - FeatureFlag, - { - where: { - key: FeatureFlagKey.IS_API_KEY_ROLES_ENABLED, - workspaceId: workspaceId, - }, - }, - ); - - const featureFlagToSave = existingFeatureFlag - ? { - ...existingFeatureFlag, - value: true, - } - : { - key: FeatureFlagKey.IS_API_KEY_ROLES_ENABLED, - value: true, - workspaceId: workspaceId, - }; - - await queryRunner.manager.save(FeatureFlag, featureFlagToSave); - - this.logger.log( - `Enabled IS_API_KEY_ROLES_ENABLED feature flag for workspace ${workspaceId}`, - ); - } catch (error) { - this.logger.error( - `Failed to enable feature flag for workspace ${workspaceId}: ${error.message}`, - ); - throw new Error( - `Failed to enable API key roles feature flag for workspace ${workspaceId}: ${error.message}`, - ); - } - } else { - this.logger.log( - `Feature flag already enabled for workspace ${workspaceId}`, - ); - } - } - - private async shouldEnableForZeroApiKeys( - workspaceId: string, - ): Promise { - const isAlreadyEnabled = await this.featureFlagService.isFeatureEnabled( - FeatureFlagKey.IS_API_KEY_ROLES_ENABLED, - workspaceId, - ); - - return !isAlreadyEnabled; - } -} diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/1-3/1-3-upgrade-version-command.module.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/1-3/1-3-upgrade-version-command.module.ts index 694c70521f..daf8cd49dd 100644 --- a/packages/twenty-server/src/database/commands/upgrade-version-command/1-3/1-3-upgrade-version-command.module.ts +++ b/packages/twenty-server/src/database/commands/upgrade-version-command/1-3/1-3-upgrade-version-command.module.ts @@ -2,7 +2,6 @@ import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; import { AddNextStepIdsToWorkflowRunsTrigger } from 'src/database/commands/upgrade-version-command/1-3/1-3-add-next-step-ids-to-workflow-runs-trigger.command'; -import { AssignRolesToExistingApiKeysCommand } from 'src/database/commands/upgrade-version-command/1-3/1-3-assign-roles-to-existing-api-keys.command'; import { UpdateTimestampColumnTypeInWorkspaceSchemaCommand } from 'src/database/commands/upgrade-version-command/1-3/1-3-update-timestamp-column-type-in-workspace-schema.command'; import { ApiKey } from 'src/engine/core-modules/api-key/api-key.entity'; import { ApiKeyModule } from 'src/engine/core-modules/api-key/api-key.module'; @@ -37,12 +36,10 @@ import { WorkspaceDataSourceModule } from 'src/engine/workspace-datasource/works WorkspaceFeatureFlagsMapCacheModule, ], providers: [ - AssignRolesToExistingApiKeysCommand, AddNextStepIdsToWorkflowRunsTrigger, UpdateTimestampColumnTypeInWorkspaceSchemaCommand, ], exports: [ - AssignRolesToExistingApiKeysCommand, AddNextStepIdsToWorkflowRunsTrigger, UpdateTimestampColumnTypeInWorkspaceSchemaCommand, ], diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/upgrade.command.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/upgrade.command.ts index 165d5438e1..17e9dfdd20 100644 --- a/packages/twenty-server/src/database/commands/upgrade-version-command/upgrade.command.ts +++ b/packages/twenty-server/src/database/commands/upgrade-version-command/upgrade.command.ts @@ -23,7 +23,6 @@ import { AddEnqueuedStatusToWorkflowRunV2Command } from 'src/database/commands/u import { AddNextStepIdsToWorkflowVersionTriggers } from 'src/database/commands/upgrade-version-command/1-2/1-2-add-next-step-ids-to-workflow-version-triggers.command'; import { RemoveWorkflowRunsWithoutState } from 'src/database/commands/upgrade-version-command/1-2/1-2-remove-workflow-runs-without-state.command'; import { AddNextStepIdsToWorkflowRunsTrigger } from 'src/database/commands/upgrade-version-command/1-3/1-3-add-next-step-ids-to-workflow-runs-trigger.command'; -import { AssignRolesToExistingApiKeysCommand } from 'src/database/commands/upgrade-version-command/1-3/1-3-assign-roles-to-existing-api-keys.command'; import { UpdateTimestampColumnTypeInWorkspaceSchemaCommand } from 'src/database/commands/upgrade-version-command/1-3/1-3-update-timestamp-column-type-in-workspace-schema.command'; import { AddPositionsToWorkflowVersionsAndWorkflowRunsCommand } from 'src/database/commands/upgrade-version-command/1-5/1-5-add-positions-to-workflow-versions-and-workflow-runs.command'; import { MigrateViewsToCoreCommand } from 'src/database/commands/upgrade-version-command/1-5/1-5-migrate-views-to-core.command'; @@ -69,7 +68,6 @@ export class UpgradeCommand extends UpgradeCommandRunner { protected readonly addEnqueuedStatusToWorkflowRunV2Command: AddEnqueuedStatusToWorkflowRunV2Command, // 1.3 Commands - protected readonly assignRolesToExistingApiKeysCommand: AssignRolesToExistingApiKeysCommand, protected readonly addNextStepIdsToWorkflowRunsTrigger: AddNextStepIdsToWorkflowRunsTrigger, protected readonly updateTimestampColumnTypeInWorkspaceSchemaCommand: UpdateTimestampColumnTypeInWorkspaceSchemaCommand, @@ -132,14 +130,13 @@ export class UpgradeCommand extends UpgradeCommandRunner { this.addNextStepIdsToWorkflowVersionTriggers, this.addEnqueuedStatusToWorkflowRunV2Command, ], - afterSyncMetadata: [this.assignRolesToExistingApiKeysCommand], + afterSyncMetadata: [], }; const commands_130: VersionCommands = { beforeSyncMetadata: [ this.addNextStepIdsToWorkflowVersionTriggers, // We add that command again because nextStepIds where not added on freshly created triggers. It will be done in 1.3 this.addNextStepIdsToWorkflowRunsTrigger, - this.assignRolesToExistingApiKeysCommand, this.updateTimestampColumnTypeInWorkspaceSchemaCommand, ], afterSyncMetadata: [], diff --git a/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/interfaces/base-resolver-service.ts b/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/interfaces/base-resolver-service.ts index 21bd2a5de3..5fe0e41745 100644 --- a/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/interfaces/base-resolver-service.ts +++ b/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/interfaces/base-resolver-service.ts @@ -119,17 +119,10 @@ export abstract class GraphqlQueryBaseResolverService< let shouldBypassPermissionChecks = false; if (isDefined(authContext.apiKey)) { - const isApiKeyRolesEnabled = - featureFlagsMap[FeatureFlagKey.IS_API_KEY_ROLES_ENABLED]; - - if (!isApiKeyRolesEnabled) { - shouldBypassPermissionChecks = true; - } else { - roleId = await this.apiKeyRoleService.getRoleIdForApiKey( - authContext.apiKey.id, - workspace.id, - ); - } + roleId = await this.apiKeyRoleService.getRoleIdForApiKey( + authContext.apiKey.id, + workspace.id, + ); } if (isDefined(authContext.userWorkspaceId)) { diff --git a/packages/twenty-server/src/engine/api/rest/core/interfaces/rest-api-base.handler.ts b/packages/twenty-server/src/engine/api/rest/core/interfaces/rest-api-base.handler.ts index 05258a81ac..dd17c66ecf 100644 --- a/packages/twenty-server/src/engine/api/rest/core/interfaces/rest-api-base.handler.ts +++ b/packages/twenty-server/src/engine/api/rest/core/interfaces/rest-api-base.handler.ts @@ -31,8 +31,6 @@ import { getAllSelectableFields } from 'src/engine/api/utils/get-all-selectable- import { CreatedByFromAuthContextService } from 'src/engine/core-modules/actor/services/created-by-from-auth-context.service'; import { ApiKeyRoleService } from 'src/engine/core-modules/api-key/api-key-role.service'; import { type AuthContext } from 'src/engine/core-modules/auth/types/auth-context.type'; -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 { InternalServerError } from 'src/engine/core-modules/graphql/utils/graphql-errors.util'; import { RecordInputTransformerService } from 'src/engine/core-modules/record-transformer/services/record-input-transformer.service'; import { @@ -96,8 +94,6 @@ export abstract class RestApiBaseHandler { @Inject() protected readonly createdByFromAuthContextService: CreatedByFromAuthContextService; @Inject() - protected readonly featureFlagService: FeatureFlagService; - @Inject() protected readonly apiKeyRoleService: ApiKeyRoleService; protected abstract handle( @@ -142,20 +138,10 @@ export abstract class RestApiBaseHandler { let shouldBypassPermissionChecks = false; if (isDefined(apiKey)) { - const isApiKeyRolesEnabled = - await this.featureFlagService.isFeatureEnabled( - FeatureFlagKey.IS_API_KEY_ROLES_ENABLED, - workspace.id, - ); - - if (!isApiKeyRolesEnabled) { - shouldBypassPermissionChecks = true; - } else { - roleId = await this.apiKeyRoleService.getRoleIdForApiKey( - apiKey.id, - workspace.id, - ); - } + roleId = await this.apiKeyRoleService.getRoleIdForApiKey( + apiKey.id, + workspace.id, + ); } if (isDefined(userWorkspaceId)) { diff --git a/packages/twenty-server/src/engine/core-modules/api-key/api-key.resolver.ts b/packages/twenty-server/src/engine/core-modules/api-key/api-key.resolver.ts index fe83e4478b..b6be204f51 100644 --- a/packages/twenty-server/src/engine/core-modules/api-key/api-key.resolver.ts +++ b/packages/twenty-server/src/engine/core-modules/api-key/api-key.resolver.ts @@ -18,8 +18,6 @@ import { GetApiKeyDTO } from 'src/engine/core-modules/api-key/dtos/get-api-key.d import { RevokeApiKeyDTO } from 'src/engine/core-modules/api-key/dtos/revoke-api-key.dto'; import { UpdateApiKeyDTO } from 'src/engine/core-modules/api-key/dtos/update-api-key.dto'; import { apiKeyGraphqlApiExceptionHandler } from 'src/engine/core-modules/api-key/utils/api-key-graphql-api-exception-handler.util'; -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 { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; import { AuthWorkspace } from 'src/engine/decorators/auth/auth-workspace.decorator'; import { SettingsPermissionsGuard } from 'src/engine/guards/settings-permissions.guard'; @@ -40,7 +38,6 @@ export class ApiKeyResolver { constructor( private readonly apiKeyService: ApiKeyService, private readonly apiKeyRoleService: ApiKeyRoleService, - private readonly featureFlagService: FeatureFlagService, ) {} @Query(() => [ApiKey]) @@ -126,20 +123,11 @@ export class ApiKeyResolver { } } - @ResolveField(() => RoleDTO, { nullable: true }) + @ResolveField(() => RoleDTO) async role( @Parent() apiKey: ApiKey, @AuthWorkspace() workspace: Workspace, - ): Promise { - const isApiKeyRolesEnabled = await this.featureFlagService.isFeatureEnabled( - FeatureFlagKey.IS_API_KEY_ROLES_ENABLED, - workspace.id, - ); - - if (!isApiKeyRolesEnabled) { - return null; - } - + ): Promise { const rolesMap = await this.apiKeyRoleService.getRolesByApiKeys({ apiKeyIds: [apiKey.id], workspaceId: workspace.id, diff --git a/packages/twenty-server/src/engine/core-modules/feature-flag/enums/feature-flag-key.enum.ts b/packages/twenty-server/src/engine/core-modules/feature-flag/enums/feature-flag-key.enum.ts index d22d3b4bd9..2e537f1dc1 100644 --- a/packages/twenty-server/src/engine/core-modules/feature-flag/enums/feature-flag-key.enum.ts +++ b/packages/twenty-server/src/engine/core-modules/feature-flag/enums/feature-flag-key.enum.ts @@ -11,7 +11,6 @@ export enum FeatureFlagKey { IS_CORE_VIEW_SYNCING_ENABLED = 'IS_CORE_VIEW_SYNCING_ENABLED', IS_CORE_VIEW_ENABLED = 'IS_CORE_VIEW_ENABLED', IS_WORKSPACE_MIGRATION_V2_ENABLED = 'IS_WORKSPACE_MIGRATION_V2_ENABLED', - IS_API_KEY_ROLES_ENABLED = 'IS_API_KEY_ROLES_ENABLED', IS_PAGE_LAYOUT_ENABLED = 'IS_PAGE_LAYOUT_ENABLED', IS_MESSAGE_FOLDER_CONTROL_ENABLED = 'IS_MESSAGE_FOLDER_CONTROL_ENABLED', IS_WORKFLOW_ITERATOR_ENABLED = 'IS_WORKFLOW_ITERATOR_ENABLED', 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 8b1884eabb..d19bda7c09 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 @@ -6,8 +6,6 @@ import { isDefined } from 'twenty-shared/utils'; import { Repository } from 'typeorm'; import { ApiKeyRoleService } from 'src/engine/core-modules/api-key/api-key-role.service'; -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 { PermissionFlagType } from 'src/engine/metadata-modules/permissions/constants/permission-flag-type.constants'; import { TOOL_PERMISSION_FLAGS } from 'src/engine/metadata-modules/permissions/constants/tool-permission-flags'; import { @@ -26,7 +24,6 @@ export class PermissionsService { private readonly userRoleService: UserRoleService, private readonly workspacePermissionsCacheService: WorkspacePermissionsCacheService, private readonly apiKeyRoleService: ApiKeyRoleService, - private readonly featureFlagService: FeatureFlagService, @InjectRepository(RoleEntity) private readonly roleRepository: Repository, ) {} @@ -145,16 +142,6 @@ export class PermissionsService { apiKeyId?: string; }): Promise { if (apiKeyId) { - const isApiKeyRolesEnabled = - await this.featureFlagService.isFeatureEnabled( - FeatureFlagKey.IS_API_KEY_ROLES_ENABLED, - workspaceId, - ); - - if (!isApiKeyRolesEnabled) { - return true; - } - const roleId = await this.apiKeyRoleService.getRoleIdForApiKey( apiKeyId, workspaceId, diff --git a/packages/twenty-server/src/engine/twenty-orm/entity-manager/workspace-entity-manager.spec.ts b/packages/twenty-server/src/engine/twenty-orm/entity-manager/workspace-entity-manager.spec.ts index c0d102b105..25cfa88c54 100644 --- a/packages/twenty-server/src/engine/twenty-orm/entity-manager/workspace-entity-manager.spec.ts +++ b/packages/twenty-server/src/engine/twenty-orm/entity-manager/workspace-entity-manager.spec.ts @@ -134,7 +134,6 @@ describe('WorkspaceEntityManager', () => { IS_CORE_VIEW_SYNCING_ENABLED: false, IS_CORE_VIEW_ENABLED: false, IS_WORKSPACE_MIGRATION_V2_ENABLED: false, - IS_API_KEY_ROLES_ENABLED: false, IS_PAGE_LAYOUT_ENABLED: false, IS_MESSAGE_FOLDER_CONTROL_ENABLED: false, IS_WORKFLOW_ITERATOR_ENABLED: false, @@ -161,7 +160,6 @@ describe('WorkspaceEntityManager', () => { IS_CORE_VIEW_SYNCING_ENABLED: false, IS_CORE_VIEW_ENABLED: false, IS_WORKSPACE_MIGRATION_V2_ENABLED: false, - IS_API_KEY_ROLES_ENABLED: false, IS_PAGE_LAYOUT_ENABLED: false, IS_WORKFLOW_ITERATOR_ENABLED: false, }, diff --git a/packages/twenty-server/src/engine/workspace-manager/dev-seeder/core/utils/seed-feature-flags.util.ts b/packages/twenty-server/src/engine/workspace-manager/dev-seeder/core/utils/seed-feature-flags.util.ts index 2f0dd09d05..d4d48a7fdf 100644 --- a/packages/twenty-server/src/engine/workspace-manager/dev-seeder/core/utils/seed-feature-flags.util.ts +++ b/packages/twenty-server/src/engine/workspace-manager/dev-seeder/core/utils/seed-feature-flags.util.ts @@ -55,11 +55,6 @@ export const seedFeatureFlags = async ( workspaceId: workspaceId, value: false, }, - { - key: FeatureFlagKey.IS_API_KEY_ROLES_ENABLED, - workspaceId: workspaceId, - value: true, - }, { key: FeatureFlagKey.IS_PAGE_LAYOUT_ENABLED, workspaceId: workspaceId,