Skip to content

Commit

Permalink
chore: Sync release/q1-2024 and dev (#16666)
Browse files Browse the repository at this point in the history
* 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 <thomasbelin4@gmail.com>

---------

Co-authored-by: Thomas Belin <thomasbelin4@gmail.com>

* 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 757aa10.

* 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 <thomasbelin4@gmail.com>

* 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 <patrykbunix@gmail.com>
Co-authored-by: Timothy LeBon <tlebon@gmail.com>
Co-authored-by: Przemysław Jóźwik <przemyslaw.jozwik96@gmail.com>
Co-authored-by: Arjita <arjitamitra3@gmail.com>
Co-authored-by: Stefan Matting <smatting@users.noreply.github.com>
Co-authored-by: Virgile <78490891+V-Gira@users.noreply.github.com>
Co-authored-by: Amir Ghezelbash <thisisamir98@gmail.com>
Co-authored-by: Adrian Weiß <77456193+aweiss-dev@users.noreply.github.com>
  • Loading branch information
9 people authored Jan 29, 2024
1 parent c43835f commit 2a67150
Show file tree
Hide file tree
Showing 13 changed files with 165 additions and 46 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
4 changes: 4 additions & 0 deletions src/__mocks__/@wireapp/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand Down
10 changes: 9 additions & 1 deletion src/i18n/en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -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. <br/> <br/> Enter your identity provider’s credentials in the next step to update the certificate automatically. <br/> <br/> <a href=\"{{url}}\" target=\"_blank\"> Learn more about end-to-end identity </a>",
"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. <br/> <br/> Enter your identity provider’s credentials in the next step to update the certificate automatically. <br/> <br/> <a href=\"{{url}}\" target=\"_blank\"> Learn more about end-to-end identity </a>",
"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.<br/>Log out to reduce security risks. Then log in again, get a new certificate, and reset your password.<br/><br/>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] <br/> <br/> <a href=\"{{url}}\" target=\"_blank\"> Learn more about end-to-end identity </a>",
"acme.renewal.inProgress.headline": "Updating Certificate...",
Expand Down Expand Up @@ -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.",
Expand Down Expand Up @@ -1521,4 +1529,4 @@
"wireMacos": "{{brandName}} for macOS",
"wireWindows": "{{brandName}} for Windows",
"wire_for_web": "{{brandName}} for Web"
}
}
11 changes: 10 additions & 1 deletion src/script/E2EIdentity/E2EIdentityEnrollment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,16 @@ export class E2EIHandler extends TypedEventEmitter<Events> {
}),
};

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;
}
Expand Down
16 changes: 16 additions & 0 deletions src/script/E2EIdentity/Modals/Modals.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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',
}

Expand Down Expand Up @@ -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: {
Expand Down
2 changes: 1 addition & 1 deletion src/script/E2EIdentity/certificateDetails.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export const mapMLSStatus = (status?: CoreStatus) => {
const statusMap: Record<any, MLSStatuses> = {
Valid: MLSStatuses.VALID,
Expired: MLSStatuses.EXPIRED,
Revoked: MLSStatuses.EXPIRED,
Revoked: MLSStatuses.REVOKED,
};

if (!status) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,12 @@ const MLSVerificationBadge = ({context, MLSStatus}: {MLSStatus?: MLSStatuses; co
<CertificateExpiredIcon />
</span>
);
case MLSStatuses.REVOKED:
return (
<span {...mlsVerificationProps} data-tooltip={t('E2EI.certificateRevoked')}>
<CertificateExpiredIcon />
</span>
);
case MLSStatuses.EXPIRES_SOON:
return (
<span {...mlsVerificationProps} data-tooltip={t('E2EI.certificateExpiresSoon')}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,15 +47,15 @@ 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();
});

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();
});
Expand All @@ -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));
Expand All @@ -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));
Expand All @@ -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));
Expand All @@ -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(() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -38,6 +44,7 @@ class MLSConversationVerificationStateHandler {

public constructor(
private readonly onConversationVerificationStateChange: OnConversationE2EIVerificationStateChange,
private readonly onSelfClientCertificateRevoked: () => Promise<void>,
private readonly conversationState: ConversationState,
private readonly core: Core,
) {
Expand All @@ -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);
}

/**
Expand Down Expand Up @@ -89,7 +98,34 @@ class MLSConversationVerificationStateHandler {
});
}

private checkConversationVerificationState = async ({groupId}: {groupId: string}): Promise<void> => {
/**
* This function checks if self client certificate is revoked
*/
private checkSelfCertificateRevocation = async (): Promise<void> => {
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<void> => {
const conversations = this.conversationState.conversations();
await Promise.all(conversations.map(conversation => this.checkConversationVerificationState(conversation)));
};

private onEpochChanged = async ({groupId}: {groupId: string}): Promise<void> => {
// 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(() =>
Expand All @@ -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<void> => {
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 &&
Expand All @@ -123,8 +163,14 @@ class MLSConversationVerificationStateHandler {

export const registerMLSConversationVerificationStateHandler = (
onConversationVerificationStateChange: OnConversationE2EIVerificationStateChange = () => {},
onSelfClientCertificateRevoked: () => Promise<void> = async () => {},
conversationState: ConversationState = container.resolve(ConversationState),
core: Core = container.resolve(Core),
): void => {
new MLSConversationVerificationStateHandler(onConversationVerificationStateChange, conversationState, core);
new MLSConversationVerificationStateHandler(
onConversationVerificationStateChange,
onSelfClientCertificateRevoked,
conversationState,
core,
);
};
16 changes: 15 additions & 1 deletion src/script/main/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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';
Expand Down Expand Up @@ -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'));
Expand Down Expand Up @@ -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);
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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 = {
Expand Down Expand Up @@ -95,6 +98,7 @@ describe('FeatureConfigChangeNotifier', () => {
...baseConfig,
[feature]: {
status: FeatureStatus.ENABLED,
...(feature === FEATURE_KEY.ENFORCE_DOWNLOAD_PATH && {config: {enforcedDownloadLocation: 'dlpath'}}),
},
});
});
Expand All @@ -116,6 +120,7 @@ describe('FeatureConfigChangeNotifier', () => {
...baseConfig,
[feature]: {
status: FeatureStatus.DISABLED,
...(feature === FEATURE_KEY.ENFORCE_DOWNLOAD_PATH && {config: {enforcedDownloadLocation: ''}}),
},
});
});
Expand Down
Loading

0 comments on commit 2a67150

Please sign in to comment.