From 2a67150db747535dd24c06c4034e23c7011561ea Mon Sep 17 00:00:00 2001 From: Thomas Belin Date: Mon, 29 Jan 2024 11:13:50 +0100 Subject: [PATCH] chore: Sync release/q1-2024 and dev (#16666) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * runfix: add e2ei-verification type to expected events (#16554) * runfix: exclude self mls conversation from setting verification state * refactor: simplify if statement * chore: unstuck ci please * chore: Make sure entire repo is fetched when cherry-picking release commits * feat: one2one migrated to mls system message [WPB-6195] (#16560) * feat: add system message for 1:1 conversation migrated to mls * test: check if message was injected * refactor: improve naming * refactor: add check for connection request to conversation selectors * runfix: change type to 1:1 only in init proteus method * runfix: always blacklist proteus 1:1 if its being migrated to mls * refactor: improve naming * docs: type Co-authored-by: Thomas Belin --------- Co-authored-by: Thomas Belin * feat: update download path (#16460) * feat: initial code for listening to feature change * feat: catch team dl path event * feat: small updates for type changes and passing event correctly to wrapper * chore: updates for catching download path on restart * feat: add modal for catching feature changes * test: extend/fix tests * fix: Read indicator design improvements (#16591) * fix: Read indicator desing improvements * add padding-left for reactions * fix cursor * fix: don't load self convo's messages into memory [WPB-6166] (#16589) * fix: don't load self convo's messages into memory * [tmp] * test: update tests * refactor: improve naming * test: fix tests * test: simplify generating a convo * refactor: Remove oidcproxy (#16596) * fix: when grace period is over select the enrollment type based on certificate availability (#16599) * chore: upgrade CoreCrypto to rc31 (#16598) * ci: set more fields and retry (#16586) * feat: bump core with new core-crypto (#16603) * fix: read indicator images improvements (#16605) * fix: read indicator images improvements * fixes * fix image asset width * runfix: Fix enrolling to e2ei with non-ascii user name (#16607) * chore: Fix running CI jobs on cherry-picked commits (#16609) * chore: Auto merge cherry picked commits to dev (#16611) * chore: Auto merge cherry picked commits to dev * fix naming * fix target * fix naming * chore: Fix permission for flagging auto cherry picked PR * test: gh ci test * test: revert gh ci test This reverts commit 757aa10236d5831226a90fd1b3a5a940a4244e6c. * chore: Fix permission for cherry-pick job * chore: fix tagging auto merge PR * chore: use github token to auto approve PR * chore: Auto merge PR from otto-the-bot and dependabot (#16616) * chore: Auto merge PR from otto-the-bot and dependabot * removeme * fix config * fixup * fix: Enforce download path change if featureStatus is not changed (#16623) * feat: show modal if feature status unchanged * fix: test/functionality * chore: Trigger publish job on release branches (#16624) * chore: Fix docker images tag no slashes are allowed in quay.io image tags * runfix: Deregister the single instance of the app as late as possible (#16627) * runfix: address read indicator design review (WPB-5369) (#16629) * runfix: assign max-width to all assets * remove read indicator from message header * feat: group messages sent within a short timeframe (WPB-6237) (#16631) * feat: group messages sent within a short timeframe (WPB-6237) * use TIME_IN_MILLIS util * chore: Make cherry-pick PR title semantically correct (#16632) * chore: Fix renaming PR job * chore: fix action token * chore: fix renaming job access token * chore: Make sure we have the repo when renaming PR * chore: use PR urls * runfix: position system message timestamps correctly (#16640) * feat: Do not load all team members on app load [WPB-4553 WPB-4548] (#16160) (#16258) * feat: Do not load all team members on app load [WPB-4553 WPB-4548 WPB-1912] (#16160) * fix: bring back subscribing to team members changes * fix e2eiHandler error --------- Co-authored-by: Thomas Belin * feat(e2ei): link renew certificate action with UI button (#16643) * feat(e2ei): link cert renewal with ui button * chore: remove comment * fix: tests * runfix: make assets use all available width (#16646) * feat: client certificate revocation list [WPB-3232] (#16649) * feat: handle conversation state every time crl changes * feat: show modal if self client's cert is revoked * test: fix tests by updating mocks * runfix: cover validating slef crl with try catch * chore: remove node script * chore: bump core * runfix: bump core with rolled back core-crypto to vrc33 * feat: client certificate revocation list [WPB-3232] (#16649) * feat: handle conversation state every time crl changes * feat: show modal if self client's cert is revoked * test: fix tests by updating mocks * runfix: cover validating slef crl with try catch * chore: remove node script * chore: bump core * runfix: bump core with rolled back core-crypto to vrc33 * runfix: address read indicator design review (WPB-5369) (#16629) * runfix: assign max-width to all assets * remove read indicator from message header * chore: Make cherry-pick PR title semantically correct (#16632) * chore: Fix renaming PR job * chore: fix renaming job access token --------- Co-authored-by: Patryk Górka Co-authored-by: Timothy LeBon Co-authored-by: Przemysław Jóźwik Co-authored-by: Arjita Co-authored-by: Stefan Matting Co-authored-by: Virgile <78490891+V-Gira@users.noreply.github.com> Co-authored-by: Amir Ghezelbash Co-authored-by: Adrian Weiß <77456193+aweiss-dev@users.noreply.github.com> --- package.json | 2 +- src/__mocks__/@wireapp/core.ts | 4 ++ src/i18n/en-US.json | 10 +++- .../E2EIdentity/E2EIdentityEnrollment.ts | 11 +++- src/script/E2EIdentity/Modals/Modals.ts | 16 ++++++ src/script/E2EIdentity/certificateDetails.ts | 2 +- .../VerificationBadge/VerificationBadges.tsx | 6 ++ .../MLS/MLSStateHandler.test.ts | 12 ++-- .../MLS/MLSStateHandler.ts | 56 +++++++++++++++++-- src/script/main/app.ts | 16 +++++- .../FeatureConfigChangeNotifier.test.tsx | 5 ++ .../FeatureConfigChangeNotifier.ts | 51 ++++++++++------- yarn.lock | 20 +++---- 13 files changed, 165 insertions(+), 46 deletions(-) diff --git a/package.json b/package.json index c9a45e15aa1..99f41648f99 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "@peculiar/x509": "1.9.6", "@wireapp/avs": "9.6.9", "@wireapp/commons": "5.2.4", - "@wireapp/core": "43.9.1", + "@wireapp/core": "43.11.2", "@wireapp/react-ui-kit": "9.12.8", "@wireapp/store-engine-dexie": "2.1.7", "@wireapp/webapp-events": "0.20.1", diff --git a/src/__mocks__/@wireapp/core.ts b/src/__mocks__/@wireapp/core.ts index 3e4735c2d18..8a35812e90c 100644 --- a/src/__mocks__/@wireapp/core.ts +++ b/src/__mocks__/@wireapp/core.ts @@ -41,6 +41,10 @@ export class Account extends EventEmitter { getDeviceIdentities: jest.fn(), getConversationState: jest.fn(), registerServerCertificates: jest.fn(), + on: jest.fn(), + emit: jest.fn(), + off: jest.fn(), + initialize: jest.fn(), }, mls: { schedulePeriodicKeyMaterialRenewals: jest.fn(), diff --git a/src/i18n/en-US.json b/src/i18n/en-US.json index aa0a2e263da..cc052fd8818 100644 --- a/src/i18n/en-US.json +++ b/src/i18n/en-US.json @@ -182,6 +182,10 @@ "acme.renewCertificate.gracePeriodOver.paragraph": "The end-to-end identity certificate for this device has expired. To keep your Wire communication at the highest security level, please update the certificate.

Enter your identity provider’s credentials in the next step to update the certificate automatically.

Learn more about end-to-end identity ", "acme.renewCertificate.headline.alt": "Update end-to-end identity certificate", "acme.renewCertificate.paragraph": "The end-to-end identity certificate for this device expires soon. To keep your communication at the highest security level, update your certificate now.

Enter your identity provider’s credentials in the next step to update the certificate automatically.

Learn more about end-to-end identity ", + "acme.selfCertificateRevoked.button.primary": "Log out", + "acme.selfCertificateRevoked.button.cancel": "Continue using this device", + "acme.selfCertificateRevoked.text": "Your team admin revoked the certificate for this device.
Log out to reduce security risks. Then log in again, get a new certificate, and reset your password.

If you keep using this device, your conversations are no longer verified.", + "acme.selfCertificateRevoked.title": "End-to-end identity certificate revoked", "acme.renewal.done.headline": "Certificate updated", "acme.renewal.done.paragraph": "The certificate is updated and your device is verified. You can find more details about this certificate in your [bold]Wire Preferences[/bold] under [bold]Devices.[/bold]

Learn more about end-to-end identity ", "acme.renewal.inProgress.headline": "Updating Certificate...", @@ -642,6 +646,10 @@ "featureConfigChangeModalAudioVideoDescriptionItemCameraDisabled": "Camera in calls is disabled", "featureConfigChangeModalAudioVideoDescriptionItemCameraEnabled": "Camera in calls is enabled", "featureConfigChangeModalAudioVideoHeadline": "There has been a change in {{brandName}}", + "featureConfigChangeModalDownloadPathDisabled": "Enforced Download Path is disabled. You will need to restart the app if you want to save downloads in a new location.", + "featureConfigChangeModalDownloadPathEnabled": "Enforced Download Path is enabled. The App will restart for the new settings to take effect.", + "featureConfigChangeModalDownloadPathChanged": "Enforced Download Path location is changed. The App will restart for the new settings to take effect.", + "featureConfigChangeModalDownloadPathHeadline": "There has been a change in {{brandName}}", "featureConfigChangeModalConferenceCallingEnabled": "Your team was upgraded to {{brandName}} Enterprise, which gives you access to features such as conference calls and more. [link]Learn more about {{brandName}} Enterprise[/link]", "featureConfigChangeModalConferenceCallingTitle": "{{brandName}} Enterprise", "featureConfigChangeModalConversationGuestLinksDescriptionItemConversationGuestLinksDisabled": "Generating guest links is now disabled for all group admins.", @@ -1521,4 +1529,4 @@ "wireMacos": "{{brandName}} for macOS", "wireWindows": "{{brandName}} for Windows", "wire_for_web": "{{brandName}} for Web" -} +} \ No newline at end of file diff --git a/src/script/E2EIdentity/E2EIdentityEnrollment.ts b/src/script/E2EIdentity/E2EIdentityEnrollment.ts index 5eb785614da..5b4f2d6367a 100644 --- a/src/script/E2EIdentity/E2EIdentityEnrollment.ts +++ b/src/script/E2EIdentity/E2EIdentityEnrollment.ts @@ -131,7 +131,16 @@ export class E2EIHandler extends TypedEventEmitter { }), }; - await this.coreE2EIService.registerServerCertificates(discoveryUrl); + await this.coreE2EIService.initialize(discoveryUrl); + await this.coreE2EIService.registerServerCertificates(); + + try { + //FIXME: this doesn't work on curernt core-crypto version + await this.coreE2EIService.validateSelfCrl(); + } catch (error) { + console.error('Error validating self CRL', error); + } + this.currentStep = E2EIHandlerStep.INITIALIZED; return this; } diff --git a/src/script/E2EIdentity/Modals/Modals.ts b/src/script/E2EIdentity/Modals/Modals.ts index 10d80d80757..38ade1f224a 100644 --- a/src/script/E2EIdentity/Modals/Modals.ts +++ b/src/script/E2EIdentity/Modals/Modals.ts @@ -30,6 +30,7 @@ export enum ModalType { SUCCESS = 'success', LOADING = 'loading', CERTIFICATE_RENEWAL = 'certificate_renewal', + SELF_CERTIFICATE_REVOKED = 'self_certificate_revoked', SNOOZE_REMINDER = 'snooze_reminder', } @@ -120,6 +121,21 @@ export const getModalOptions = ({ hideSecondary || secondaryActionFn === undefined ? PrimaryModal.type.ACKNOWLEDGE : PrimaryModal.type.CONFIRM; break; + case ModalType.SELF_CERTIFICATE_REVOKED: + options = { + text: { + closeBtnLabel: t('acme.selfCertificateRevoked.button.cancel'), + htmlMessage: t('acme.selfCertificateRevoked.text'), + title: t('acme.selfCertificateRevoked.title'), + }, + primaryAction: { + action: primaryActionFn, + text: t('acme.selfCertificateRevoked.button.primary'), + }, + }; + modalType = PrimaryModal.type.CONFIRM; + break; + case ModalType.SNOOZE_REMINDER: options = { text: { diff --git a/src/script/E2EIdentity/certificateDetails.ts b/src/script/E2EIdentity/certificateDetails.ts index 91496ea9be4..1ffc8b907d6 100644 --- a/src/script/E2EIdentity/certificateDetails.ts +++ b/src/script/E2EIdentity/certificateDetails.ts @@ -27,7 +27,7 @@ export const mapMLSStatus = (status?: CoreStatus) => { const statusMap: Record = { Valid: MLSStatuses.VALID, Expired: MLSStatuses.EXPIRED, - Revoked: MLSStatuses.EXPIRED, + Revoked: MLSStatuses.REVOKED, }; if (!status) { diff --git a/src/script/components/VerificationBadge/VerificationBadges.tsx b/src/script/components/VerificationBadge/VerificationBadges.tsx index c635d0fd0aa..0f073c4dcc2 100644 --- a/src/script/components/VerificationBadge/VerificationBadges.tsx +++ b/src/script/components/VerificationBadge/VerificationBadges.tsx @@ -146,6 +146,12 @@ const MLSVerificationBadge = ({context, MLSStatus}: {MLSStatus?: MLSStatuses; co ); + case MLSStatuses.REVOKED: + return ( + + + + ); case MLSStatuses.EXPIRES_SOON: return ( diff --git a/src/script/conversation/ConversationVerificationStateHandler/MLS/MLSStateHandler.test.ts b/src/script/conversation/ConversationVerificationStateHandler/MLS/MLSStateHandler.test.ts index cc13b3bee75..ce0e8395e2d 100644 --- a/src/script/conversation/ConversationVerificationStateHandler/MLS/MLSStateHandler.test.ts +++ b/src/script/conversation/ConversationVerificationStateHandler/MLS/MLSStateHandler.test.ts @@ -47,7 +47,7 @@ describe('MLSConversationVerificationStateHandler', () => { it('should do nothing if MLS service is not available', () => { core.service!.mls = undefined; - const t = () => registerMLSConversationVerificationStateHandler(undefined, conversationState, core); + const t = () => registerMLSConversationVerificationStateHandler(undefined, undefined, conversationState, core); expect(t).not.toThrow(); }); @@ -55,7 +55,7 @@ describe('MLSConversationVerificationStateHandler', () => { it('should do nothing if e2eIdentity service is not available', () => { core.service!.e2eIdentity = undefined; - registerMLSConversationVerificationStateHandler(undefined, conversationState, core); + registerMLSConversationVerificationStateHandler(undefined, undefined, conversationState, core); expect(core.service?.mls?.on).not.toHaveBeenCalled(); }); @@ -69,7 +69,7 @@ describe('MLSConversationVerificationStateHandler', () => { .spyOn(core.service!.mls!, 'on') .mockImplementation((_event, listener) => (triggerEpochChange = listener) as any); - registerMLSConversationVerificationStateHandler(undefined, conversationState, core); + registerMLSConversationVerificationStateHandler(undefined, undefined, conversationState, core); triggerEpochChange({groupId}); await new Promise(resolve => setTimeout(resolve, 0)); @@ -84,7 +84,7 @@ describe('MLSConversationVerificationStateHandler', () => { .spyOn(core.service!.mls!, 'on') .mockImplementation((_event, listener) => (triggerEpochChange = listener) as any); - registerMLSConversationVerificationStateHandler(undefined, conversationState, core); + registerMLSConversationVerificationStateHandler(undefined, undefined, conversationState, core); triggerEpochChange({groupId}); await new Promise(resolve => setTimeout(resolve, 0)); @@ -99,7 +99,7 @@ describe('MLSConversationVerificationStateHandler', () => { .spyOn(core.service!.mls!, 'on') .mockImplementation((_event, listener) => (triggerEpochChange = listener) as any); - registerMLSConversationVerificationStateHandler(undefined, conversationState, core); + registerMLSConversationVerificationStateHandler(undefined, undefined, conversationState, core); triggerEpochChange({groupId}); await new Promise(resolve => setTimeout(resolve, 0)); @@ -116,7 +116,7 @@ describe('MLSConversationVerificationStateHandler', () => { .spyOn(core.service!.mls!, 'on') .mockImplementation((_event, listener) => (triggerEpochChange = listener) as any); - registerMLSConversationVerificationStateHandler(undefined, conversationState, core); + registerMLSConversationVerificationStateHandler(undefined, undefined, conversationState, core); triggerEpochChange({groupId: newConversation.groupId}); setTimeout(() => { diff --git a/src/script/conversation/ConversationVerificationStateHandler/MLS/MLSStateHandler.ts b/src/script/conversation/ConversationVerificationStateHandler/MLS/MLSStateHandler.ts index ddde7d78d6d..abc780b830c 100644 --- a/src/script/conversation/ConversationVerificationStateHandler/MLS/MLSStateHandler.ts +++ b/src/script/conversation/ConversationVerificationStateHandler/MLS/MLSStateHandler.ts @@ -22,7 +22,13 @@ import {QualifiedId} from '@wireapp/api-client/lib/user'; import {E2eiConversationState} from '@wireapp/core/lib/messagingProtocols/mls'; import {container} from 'tsyringe'; -import {getConversationVerificationState, getUsersIdentities, MLSStatuses} from 'src/script/E2EIdentity'; +import { + getActiveWireIdentity, + getConversationVerificationState, + getUsersIdentities, + MLSStatuses, +} from 'src/script/E2EIdentity'; +import {Conversation} from 'src/script/entity/Conversation'; import {E2EIVerificationMessageType} from 'src/script/message/E2EIVerificationMessageType'; import {Core} from 'src/script/service/CoreSingleton'; import {Logger, getLogger} from 'Util/Logger'; @@ -38,6 +44,7 @@ class MLSConversationVerificationStateHandler { public constructor( private readonly onConversationVerificationStateChange: OnConversationE2EIVerificationStateChange, + private readonly onSelfClientCertificateRevoked: () => Promise, private readonly conversationState: ConversationState, private readonly core: Core, ) { @@ -48,7 +55,9 @@ class MLSConversationVerificationStateHandler { } // We hook into the newEpoch event of the MLS service to check if the conversation needs to be verified or degraded - this.core.service.mls.on('newEpoch', this.checkConversationVerificationState); + this.core.service.mls.on('newEpoch', this.onEpochChanged); + this.core.service.e2eIdentity.on('remoteCrlChanged', this.checkAllConversationsVerificationState); + this.core.service.e2eIdentity.on('selfCrlChanged', this.checkSelfCertificateRevocation); } /** @@ -89,7 +98,34 @@ class MLSConversationVerificationStateHandler { }); } - private checkConversationVerificationState = async ({groupId}: {groupId: string}): Promise => { + /** + * This function checks if self client certificate is revoked + */ + private checkSelfCertificateRevocation = async (): Promise => { + const activeIdentity = await getActiveWireIdentity(); + + if (!activeIdentity) { + return; + } + + const isRevoked = activeIdentity.status === MLSStatuses.REVOKED; + + if (isRevoked) { + await this.onSelfClientCertificateRevoked(); + } + + await this.checkAllConversationsVerificationState(); + }; + + /** + * This function checks all conversations if they are verified or degraded and updates them accordingly + */ + private checkAllConversationsVerificationState = async (): Promise => { + const conversations = this.conversationState.conversations(); + await Promise.all(conversations.map(conversation => this.checkConversationVerificationState(conversation))); + }; + + private onEpochChanged = async ({groupId}: {groupId: string}): Promise => { // There could be a race condition where we would receive an epoch update for a conversation that is not yet known by the webapp. // We just wait for it to be available and then check the verification state const conversation = await waitFor(() => @@ -100,12 +136,16 @@ class MLSConversationVerificationStateHandler { return this.logger.warn(`Epoch changed but conversation could not be found after waiting for 5 seconds`); } + return this.checkConversationVerificationState(conversation); + }; + + private checkConversationVerificationState = async (conversation: Conversation): Promise => { const isSelfConversation = conversation.type() === CONVERSATION_TYPE.SELF; if (!isMLSConversation(conversation) || isSelfConversation) { return; } - const verificationState = await getConversationVerificationState(groupId); + const verificationState = await getConversationVerificationState(conversation.groupId); if ( verificationState === E2eiConversationState.NotVerified && @@ -123,8 +163,14 @@ class MLSConversationVerificationStateHandler { export const registerMLSConversationVerificationStateHandler = ( onConversationVerificationStateChange: OnConversationE2EIVerificationStateChange = () => {}, + onSelfClientCertificateRevoked: () => Promise = async () => {}, conversationState: ConversationState = container.resolve(ConversationState), core: Core = container.resolve(Core), ): void => { - new MLSConversationVerificationStateHandler(onConversationVerificationStateChange, conversationState, core); + new MLSConversationVerificationStateHandler( + onConversationVerificationStateChange, + onSelfClientCertificateRevoked, + conversationState, + core, + ); }; diff --git a/src/script/main/app.ts b/src/script/main/app.ts index 34ea64aa867..bad85b644f0 100644 --- a/src/script/main/app.ts +++ b/src/script/main/app.ts @@ -31,6 +31,7 @@ import {container} from 'tsyringe'; import {Runtime} from '@wireapp/commons'; import {WebAppEvents} from '@wireapp/webapp-events'; +import {PrimaryModal} from 'Components/Modals/PrimaryModal'; import {E2EIHandler} from 'src/script/E2EIdentity'; import {initializeDataDog} from 'Util/DataDog'; import {DebugUtil} from 'Util/DebugUtil'; @@ -64,6 +65,7 @@ import {OnConversationE2EIVerificationStateChange} from '../conversation/Convers import {EventBuilder} from '../conversation/EventBuilder'; import {MessageRepository} from '../conversation/MessageRepository'; import {CryptographyRepository} from '../cryptography/CryptographyRepository'; +import {getModalOptions, ModalType} from '../E2EIdentity/Modals'; import {User} from '../entity/User'; import {AccessTokenError} from '../error/AccessTokenError'; import {AuthError} from '../error/AuthError'; @@ -450,7 +452,10 @@ export class App { if (supportsMLS()) { //if mls is supported, we need to initialize the callbacks (they are used when decrypting messages) conversationRepository.initMLSConversationRecoveredListener(); - registerMLSConversationVerificationStateHandler(this.updateConversationE2EIVerificationState); + registerMLSConversationVerificationStateHandler( + this.updateConversationE2EIVerificationState, + this.showClientCertificateRevokedWarning, + ); } onProgress(25, t('initReceivedUserData')); @@ -859,4 +864,13 @@ export class App { break; } }; + + private showClientCertificateRevokedWarning = async () => { + const {modalOptions, modalType} = getModalOptions({ + type: ModalType.SELF_CERTIFICATE_REVOKED, + primaryActionFn: () => this.logout(SIGN_OUT_REASON.APP_INIT, false), + }); + + PrimaryModal.show(modalType, modalOptions); + }; } diff --git a/src/script/page/components/FeatureConfigChange/FeatureConfigChangeNotifier/FeatureConfigChangeNotifier.test.tsx b/src/script/page/components/FeatureConfigChange/FeatureConfigChangeNotifier/FeatureConfigChangeNotifier.test.tsx index bcbfef682ec..1e9369925e1 100644 --- a/src/script/page/components/FeatureConfigChange/FeatureConfigChangeNotifier/FeatureConfigChangeNotifier.test.tsx +++ b/src/script/page/components/FeatureConfigChange/FeatureConfigChangeNotifier/FeatureConfigChangeNotifier.test.tsx @@ -19,6 +19,7 @@ import {act, render, waitFor} from '@testing-library/react'; import {FeatureStatus, FEATURE_KEY, FeatureList} from '@wireapp/api-client/lib/team/feature'; +import {Runtime} from '@wireapp/commons/lib/util/Runtime'; import {PrimaryModal} from 'Components/Modals/PrimaryModal'; import en from 'I18n/en-US.json'; @@ -36,6 +37,8 @@ describe('FeatureConfigChangeNotifier', () => { beforeEach(() => { showModalSpy.mockClear(); localStorage.clear(); + jest.spyOn(Runtime, 'isDesktopApp').mockReturnValue(true); + jest.spyOn(Runtime, 'isWindows').mockReturnValue(true); }); const baseConfig: FeatureList = { @@ -95,6 +98,7 @@ describe('FeatureConfigChangeNotifier', () => { ...baseConfig, [feature]: { status: FeatureStatus.ENABLED, + ...(feature === FEATURE_KEY.ENFORCE_DOWNLOAD_PATH && {config: {enforcedDownloadLocation: 'dlpath'}}), }, }); }); @@ -116,6 +120,7 @@ describe('FeatureConfigChangeNotifier', () => { ...baseConfig, [feature]: { status: FeatureStatus.DISABLED, + ...(feature === FEATURE_KEY.ENFORCE_DOWNLOAD_PATH && {config: {enforcedDownloadLocation: ''}}), }, }); }); diff --git a/src/script/page/components/FeatureConfigChange/FeatureConfigChangeNotifier/FeatureConfigChangeNotifier.ts b/src/script/page/components/FeatureConfigChange/FeatureConfigChangeNotifier/FeatureConfigChangeNotifier.ts index b0c81f3e14c..dbc62d2a8b0 100644 --- a/src/script/page/components/FeatureConfigChange/FeatureConfigChangeNotifier/FeatureConfigChangeNotifier.ts +++ b/src/script/page/components/FeatureConfigChange/FeatureConfigChangeNotifier/FeatureConfigChangeNotifier.ts @@ -80,31 +80,42 @@ const featureNotifications: Partial< }; }, [FEATURE_KEY.ENFORCE_DOWNLOAD_PATH]: (oldConfig, newConfig) => { - const status = wasTurnedOnOrOff(oldConfig, newConfig); - if (!status) { - return undefined; - } - if (newConfig && 'config' in newConfig) { + if ( + newConfig && + 'config' in newConfig && + oldConfig && + 'config' in oldConfig && + newConfig?.config?.enforcedDownloadLocation !== oldConfig?.config?.enforcedDownloadLocation && + Runtime.isDesktopApp() && + Runtime.isWindows() + ) { + const status = wasTurnedOnOrOff(oldConfig, newConfig); + const configStatus = newConfig?.config?.enforcedDownloadLocation !== oldConfig?.config?.enforcedDownloadLocation; + if (!status && !configStatus) { + return undefined; + } amplify.publish( WebAppEvents.TEAM.DOWNLOAD_PATH_UPDATE, newConfig.status === FeatureStatus.ENABLED ? newConfig.config.enforcedDownloadLocation : undefined, ); - } - return { - htmlMessage: - status === FeatureStatus.ENABLED - ? t('featureConfigChangeModalDownloadPathEnabled') - : t('featureConfigChangeModalDownloadPathDisabled'), - title: 'featureConfigChangeModalDownloadPathHeadline', - primaryAction: { - action: () => { - if (Runtime.isDesktopApp() && status === FeatureStatus.ENABLED) { - // if we are in a desktop env, we just warn the wrapper that we need to reload. It then decide what should be done - amplify.publish(WebAppEvents.LIFECYCLE.RESTART); - } + return { + htmlMessage: + status === FeatureStatus.ENABLED + ? t('featureConfigChangeModalDownloadPathEnabled') + : status === FeatureStatus.DISABLED + ? t('featureConfigChangeModalDownloadPathDisabled') + : t('featureConfigChangeModalDownloadPathChanged'), + title: 'featureConfigChangeModalDownloadPathHeadline', + primaryAction: { + action: () => { + if (Runtime.isDesktopApp() && status !== FeatureStatus.DISABLED) { + amplify.publish(WebAppEvents.LIFECYCLE.RESTART); + } + }, }, - }, - }; + }; + } + return undefined; }, [FEATURE_KEY.SELF_DELETING_MESSAGES]: (oldConfig, newConfig) => { if (!oldConfig || !('config' in oldConfig) || !newConfig || !('config' in newConfig)) { diff --git a/yarn.lock b/yarn.lock index 3e04d321260..ce2a582cac0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5118,20 +5118,20 @@ __metadata: languageName: node linkType: hard -"@wireapp/core-crypto@npm:1.0.0-rc.32": - version: 1.0.0-rc.32 - resolution: "@wireapp/core-crypto@npm:1.0.0-rc.32" - checksum: b8d5c8b28308e276419fd8528cee1ca2eb55f74e47d1ea7ca24054e6a13dd76a4f8fb761bf0697848942a06f412a6ee22491bb14eae30954bf09e2433f9181b6 +"@wireapp/core-crypto@npm:1.0.0-rc.33": + version: 1.0.0-rc.33 + resolution: "@wireapp/core-crypto@npm:1.0.0-rc.33" + checksum: 44b88f4b7a1ec2ce9c13ce48329c53193c91833f13345abf28e3bfcf51f41ab510187d1192dcf76293c23b9fa4167e67ee1bd084a9739f2dc598ca7987258d27 languageName: node linkType: hard -"@wireapp/core@npm:43.9.1": - version: 43.9.1 - resolution: "@wireapp/core@npm:43.9.1" +"@wireapp/core@npm:43.11.2": + version: 43.11.2 + resolution: "@wireapp/core@npm:43.11.2" dependencies: "@wireapp/api-client": ^26.10.1 "@wireapp/commons": ^5.2.4 - "@wireapp/core-crypto": 1.0.0-rc.32 + "@wireapp/core-crypto": 1.0.0-rc.33 "@wireapp/cryptobox": 12.8.0 "@wireapp/promise-queue": ^2.2.9 "@wireapp/protocol-messaging": 1.44.0 @@ -5147,7 +5147,7 @@ __metadata: long: ^5.2.0 uuidjs: 4.2.13 zod: 3.22.4 - checksum: ad69c04e17c82c01f96fda511c47a335c7972c475ac755042097f2c4e95f97846f8416795666be3eff4f4b4e5caa33022890472a1e99cdfbf89b5f7df928acde + checksum: 685d047c9dd296e528ae8500e6b82c1ad6aa07762fd6ae549672028e8f9741cc88c3dbab15959b68d3b0fc47870daab7ba30d3b2127c15d256409551165df002 languageName: node linkType: hard @@ -17846,7 +17846,7 @@ __metadata: "@wireapp/avs": 9.6.9 "@wireapp/commons": 5.2.4 "@wireapp/copy-config": 2.1.14 - "@wireapp/core": 43.9.1 + "@wireapp/core": 43.11.2 "@wireapp/eslint-config": 3.0.5 "@wireapp/prettier-config": 0.6.3 "@wireapp/react-ui-kit": 9.12.8