Deprecate dummy enterprise key 1/2 (#20890)

Remove usage of hasValidEnterpriseKey in FE (replaced by
hasValidSignedEnterpriseKey)

To avoid breaking change at deploy time, we will wait until after this
has been deployed in prod, to remove hasValidEnterpriseKey in the BE.
This commit is contained in:
Marie
2026-05-27 14:34:48 +02:00
committed by GitHub
parent ae7db23bdd
commit 986b9dcb3d
13 changed files with 21 additions and 106 deletions
File diff suppressed because one or more lines are too long
@@ -2,12 +2,12 @@ import { gql, InMemoryCache } from '@apollo/client';
import { CombinedGraphQLErrors } from '@apollo/client/errors';
import fetchMock, { enableFetchMocks } from 'jest-fetch-mock';
import { ApolloFactory, type Options } from '@/apollo/services/apollo.factory';
import { CUSTOM_WORKSPACE_APPLICATION_MOCK } from '@/object-metadata/hooks/__tests__/constants/CustomWorkspaceApplicationMock.test.constant';
import {
AUTO_SELECT_FAST_MODEL_ID,
AUTO_SELECT_SMART_MODEL_ID,
} from 'twenty-shared/constants';
import { ApolloFactory, type Options } from '@/apollo/services/apollo.factory';
import { CUSTOM_WORKSPACE_APPLICATION_MOCK } from '@/object-metadata/hooks/__tests__/constants/CustomWorkspaceApplicationMock.test.constant';
import { WorkspaceActivationStatus } from '~/generated-metadata/graphql';
enableFetchMocks();
@@ -67,7 +67,6 @@ const mockWorkspace = {
isGoogleAuthBypassEnabled: false,
isPasswordAuthBypassEnabled: false,
isMicrosoftAuthBypassEnabled: false,
hasValidEnterpriseKey: false,
hasActivatedAndValidEnterpriseKey: false,
hasValidSignedEnterpriseKey: false,
hasValidEnterpriseValidityToken: false,
@@ -26,7 +26,6 @@ export type CurrentWorkspace = Pick<
| 'isPasswordAuthEnabled'
| 'isPasswordAuthBypassEnabled'
| 'isCustomDomainEnabled'
| 'hasValidEnterpriseKey'
| 'hasValidSignedEnterpriseKey'
| 'hasValidEnterpriseValidityToken'
| 'subdomain'
@@ -6,7 +6,6 @@ import { InformationBannerBillingSubscriptionPaused } from '@/information-banner
import { InformationBannerEndTrialPeriod } from '@/information-banner/components/billing/InformationBannerEndTrialPeriod';
import { InformationBannerFailPaymentInfo } from '@/information-banner/components/billing/InformationBannerFailPaymentInfo';
import { InformationBannerNoBillingSubscription } from '@/information-banner/components/billing/InformationBannerNoBillingSubscription';
import { InformationBannerInvalidEnterpriseKey } from '@/information-banner/components/enterprise/InformationBannerInvalidEnterpriseKey';
import { InformationBannerMaintenance } from '@/information-banner/components/maintenance/InformationBannerMaintenance';
import { InformationBannerReconnectAccountEmailAliases } from '@/information-banner/components/reconnect-account/InformationBannerReconnectAccountEmailAliases';
import { InformationBannerReconnectAccountInsufficientPermissions } from '@/information-banner/components/reconnect-account/InformationBannerReconnectAccountInsufficientPermissions';
@@ -65,7 +64,6 @@ export const InformationBannerWrapper = () => {
return (
<StyledInformationBannerWrapper>
<InformationBannerMaintenance />
<InformationBannerInvalidEnterpriseKey />
{isAccountSyncEnabled && (
<InformationBannerReconnectAccountInsufficientPermissions />
)}
@@ -1,46 +0,0 @@
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
import { InformationBanner } from '@/information-banner/components/InformationBanner';
import { informationBannerIsOpenComponentState } from '@/information-banner/states/informationBannerIsOpenComponentState';
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
import { useSetAtomComponentState } from '@/ui/utilities/state/jotai/hooks/useSetAtomComponentState';
import { useLingui } from '@lingui/react/macro';
import { useNavigate } from 'react-router-dom';
import { SettingsPath } from 'twenty-shared/types';
import { getSettingsPath } from 'twenty-shared/utils';
import { IconKey } from 'twenty-ui/display';
const COMPONENT_INSTANCE_ID = 'information-banner-invalid-enterprise-key';
export const InformationBannerInvalidEnterpriseKey = () => {
const { t } = useLingui();
const navigate = useNavigate();
const currentWorkspace = useAtomStateValue(currentWorkspaceState);
const setInformationBannerIsOpen = useSetAtomComponentState(
informationBannerIsOpenComponentState,
COMPONENT_INSTANCE_ID,
);
const hasInvalidKey =
currentWorkspace?.hasValidEnterpriseKey === true &&
currentWorkspace?.hasValidSignedEnterpriseKey !== true &&
currentWorkspace?.hasValidEnterpriseValidityToken !== true;
if (!hasInvalidKey) {
return null;
}
return (
<InformationBanner
componentInstanceId={COMPONENT_INSTANCE_ID}
variant="secondary"
message={t`Your enterprise key is no longer valid. Activate a new key to continue using enterprise features.`}
buttonTitle={t`Activate`}
buttonIcon={IconKey}
buttonOnClick={() =>
navigate(getSettingsPath(SettingsPath.AdminPanelEnterprise))
}
onClose={() => setInformationBannerIsOpen(false)}
/>
);
};
@@ -161,7 +161,6 @@ export const responseData = {
allowImpersonation: false,
activationStatus: 'active',
isPublicInviteLinkEnabled: false,
hasValidEnterpriseKey: false,
hasValidSignedEnterpriseKey: false,
hasValidEnterpriseValidityToken: false,
isGoogleAuthEnabled: true,
@@ -4,19 +4,19 @@ import { Link } from 'react-router-dom';
import { SettingsPath } from 'twenty-shared/types';
import { useSnackBarOnQueryError } from '@/apollo/hooks/useSnackBarOnQueryError';
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
import { SettingsCard } from '@/settings/components/SettingsCard';
import { SettingsSSOIdentitiesProvidersListCardWrapper } from '@/settings/security/components/SSO/SettingsSSOIdentitiesProvidersListCardWrapper';
import { SSOIdentitiesProvidersState } from '@/settings/security/states/SSOIdentitiesProvidersState';
import { useSnackBarOnQueryError } from '@/apollo/hooks/useSnackBarOnQueryError';
import { styled } from '@linaria/react';
import { useEffect } from 'react';
import { useLingui } from '@lingui/react/macro';
import { useAtomState } from '@/ui/utilities/state/jotai/hooks/useAtomState';
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
import { useQuery } from '@apollo/client/react';
import { styled } from '@linaria/react';
import { useLingui } from '@lingui/react/macro';
import { useEffect } from 'react';
import { getSettingsPath } from 'twenty-shared/utils';
import { IconKey } from 'twenty-ui/display';
import { useQuery } from '@apollo/client/react';
import { GetSsoIdentityProvidersDocument } from '~/generated-metadata/graphql';
const StyledLinkContainer = styled.div<{ isDisabled: boolean }>`
@@ -42,7 +42,7 @@ export const SettingsSSOIdentitiesProvidersListCard = () => {
error: ssoError,
} = useQuery(GetSsoIdentityProvidersDocument, {
fetchPolicy: 'network-only',
skip: currentWorkspace?.hasValidEnterpriseKey === false,
skip: currentWorkspace?.hasValidSignedEnterpriseKey === false,
});
useEffect(() => {
@@ -55,12 +55,12 @@ export const SettingsSSOIdentitiesProvidersListCard = () => {
return loading || !SSOIdentitiesProviders.length ? (
<StyledLinkContainer
isDisabled={currentWorkspace?.hasValidEnterpriseKey !== true}
isDisabled={currentWorkspace?.hasValidSignedEnterpriseKey !== true}
>
<Link to={getSettingsPath(SettingsPath.NewSSOIdentityProvider)}>
<SettingsCard
title={t`Add SSO Identity Provider`}
disabled={currentWorkspace?.hasValidEnterpriseKey !== true}
disabled={currentWorkspace?.hasValidSignedEnterpriseKey !== true}
Icon={<IconKey />}
/>
</Link>
@@ -60,7 +60,6 @@ export const USER_QUERY_FRAGMENT = gql`
isPasswordAuthBypassEnabled
subdomain
customDomain
hasValidEnterpriseKey
hasValidSignedEnterpriseKey
hasValidEnterpriseValidityToken
workspaceCustomApplication {
@@ -11,6 +11,7 @@ import { Separator } from '@/settings/components/Separator';
import { SettingsEnterpriseFeatureGateCard } from '@/settings/components/SettingsEnterpriseFeatureGateCard';
import { SettingsOptionCardContentButton } from '@/settings/components/SettingsOptions/SettingsOptionCardContentButton';
import { SettingsOptionCardContentCounter } from '@/settings/components/SettingsOptions/SettingsOptionCardContentCounter';
import { SettingsOptionCardContentToggle } from '@/settings/components/SettingsOptions/SettingsOptionCardContentToggle';
import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer';
import { SettingsRoleDefaultRole } from '@/settings/roles/components/SettingsRolesDefaultRole';
import { SettingsRolesQueryEffect } from '@/settings/roles/components/SettingsRolesQueryEffect';
@@ -20,7 +21,6 @@ import { SettingsSecurityAuthBypassOptionsList } from '@/settings/security/compo
import { SettingsSecurityAuthProvidersOptionsList } from '@/settings/security/components/SettingsSecurityAuthProvidersOptionsList';
import { SettingsSecurityEditableProfileFields } from '@/settings/security/components/SettingsSecurityEditableProfileFields';
import { SSOIdentitiesProvidersState } from '@/settings/security/states/SSOIdentitiesProvidersState';
import { SettingsOptionCardContentToggle } from '@/settings/components/SettingsOptions/SettingsOptionCardContentToggle';
import { ToggleImpersonate } from '@/settings/workspace/components/ToggleImpersonate';
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
import { SubMenuTopBarContainer } from '@/ui/layout/page/components/SubMenuTopBarContainer';
@@ -193,7 +193,8 @@ export const SettingsSecurity = () => {
!hasDirectAuthEnabled &&
hasBypassProviderAvailable;
const hasEnterpriseAccess = currentWorkspace?.hasValidEnterpriseKey === true;
const hasEnterpriseAccess =
currentWorkspace?.hasValidSignedEnterpriseKey === true;
const isEventLogsEnabled = hasEnterpriseAccess && isClickHouseConfigured;
return (
@@ -1,10 +1,10 @@
import { type CurrentUserWorkspace } from '@/auth/states/currentUserWorkspaceState';
import { CUSTOM_WORKSPACE_APPLICATION_MOCK } from '@/object-metadata/hooks/__tests__/constants/CustomWorkspaceApplicationMock.test.constant';
import { type WorkspaceMember } from '@/workspace-member/types/WorkspaceMember';
import {
AUTO_SELECT_FAST_MODEL_ID,
AUTO_SELECT_SMART_MODEL_ID,
} from 'twenty-shared/constants';
import { type CurrentUserWorkspace } from '@/auth/states/currentUserWorkspaceState';
import { CUSTOM_WORKSPACE_APPLICATION_MOCK } from '@/object-metadata/hooks/__tests__/constants/CustomWorkspaceApplicationMock.test.constant';
import { type WorkspaceMember } from '@/workspace-member/types/WorkspaceMember';
import {
OnboardingStatus,
PermissionFlagType,
@@ -286,31 +286,6 @@ describe('EnterprisePlanService', () => {
});
});
// hasValidEnterpriseKey now means "has any ENTERPRISE_KEY configured"
describe('hasValidEnterpriseKey', () => {
it('should return true when signed enterprise key is valid', async () => {
await setupValidState();
expect(service.hasValidEnterpriseKey()).toBe(true);
});
it('should return true with unsigned legacy key', async () => {
setupEnterpriseKey('some-legacy-key');
mockCryptoVerify.mockReturnValue(false);
appTokenFindOneMock.mockResolvedValue(null);
await service.onModuleInit();
expect(service.hasValidEnterpriseKey()).toBe(true);
});
it('should return false when no key is configured', async () => {
setupEnterpriseKey(undefined);
await service.onModuleInit();
expect(service.hasValidEnterpriseKey()).toBe(false);
});
});
describe('isValid', () => {
it('should return true when validity token is valid', async () => {
await setupValidState();
@@ -142,19 +142,10 @@ export class EnterprisePlanService implements OnModuleInit {
return false;
}
hasValidEnterpriseKey(): boolean {
return this.hasValidSignedEnterpriseKey() || this.checkLegacyKey();
}
isValid(): boolean {
return this.hasValidEnterpriseValidityToken();
}
private checkLegacyKey(): boolean {
// temporary
return isDefined(this.twentyConfigService.get('ENTERPRISE_KEY'));
}
isValidEnterpriseKeyFormat(key: string): boolean {
return this.verifyJwt<EnterpriseKeyPayload>(key) !== null;
}
@@ -311,7 +311,7 @@ export class WorkspaceResolver {
@ResolveField(() => Boolean)
hasValidEnterpriseKey(): boolean {
return this.enterprisePlanService.hasValidEnterpriseKey();
return this.enterprisePlanService.hasValidSignedEnterpriseKey();
}
@ResolveField(() => Boolean)