Skip to content

Commit

Permalink
Merge branch 'dev' into feat/WPB-3072-group-messages-in-the-chat-by-u…
Browse files Browse the repository at this point in the history
…ser-and-timestamp
  • Loading branch information
przemvs committed Jan 18, 2024
2 parents af061b7 + d4a285f commit 8ffd716
Show file tree
Hide file tree
Showing 16 changed files with 203 additions and 172 deletions.
20 changes: 20 additions & 0 deletions .github/workflows/cherry-pick-release-to-dev.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# This job will automatically create a cherry pick PR from any release/* branch to the dev branch
# It allows the dev branch to stay up-to-date with fixes made to specific release branches
name: Cherry pick to dev
on:
push:
branches:
- release/*
jobs:
cherry_pick:
runs-on: ubuntu-latest
steps:
- name: checkout
uses: actions/checkout@v4

- name: Create PR to branch
uses: gorillio/github-action-cherry-pick@master
with:
pr_branch: 'dev'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
2 changes: 0 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,6 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Setup Node.js
uses: actions/setup-node@v4
Expand Down
2 changes: 0 additions & 2 deletions .github/workflows/create_docker_image.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,6 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Setup Node.js
uses: actions/setup-node@v4
Expand Down
2 changes: 0 additions & 2 deletions .github/workflows/deploy-to-test-env.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,6 @@ jobs:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Setup Node.js
uses: actions/setup-node@v4
Expand Down
2 changes: 2 additions & 0 deletions src/i18n/en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -503,6 +503,8 @@
"conversationNewConversation": "Communication in Wire is always end-to-end encrypted. Everything you send and receive in this conversation is only accessible to you and your contact.",
"conversationNotClassified": "Security level: Unclassified",
"conversationNotFoundMessage": "You may not have permission with this account or it no longer exists.",
"conversationWithBlockedUserMessage": "The link to this group conversation is no longer valid or leads to a conversation with someone you blocked.",
"conversationWithBlockedUserTitle": "Conversation not reachable",
"conversationNotFoundTitle": "{{brandName}} can’t open this conversation.",
"conversationParticipantsSearchPlaceholder": "Search by name",
"conversationParticipantsTitle": "People",
Expand Down
42 changes: 20 additions & 22 deletions src/script/E2EIdentity/E2EIdentityEnrollment.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,6 @@ describe('E2EIHandler', () => {
Config.getConfig = jest.fn().mockReturnValue({FEATURE: {ENABLE_E2EI: true}});

jest.spyOn(PrimaryModal, 'show');
(getModalOptions as jest.Mock).mockClear();
(getCertificateDetails as jest.Mock).mockClear();

jest
.spyOn(container.resolve(UserState), 'self')
Expand All @@ -110,19 +108,19 @@ describe('E2EIHandler', () => {
});

it('should create instance with valid params', async () => {
const instance = await E2EIHandler.getInstance().initialize(params);
const instance = E2EIHandler.getInstance().initialize(params);
expect(instance).toBeInstanceOf(E2EIHandler);
});

it('should always return the same instance', async () => {
const instance1 = await E2EIHandler.getInstance().initialize(params);
const instance2 = await E2EIHandler.getInstance().initialize(params);
const instance1 = E2EIHandler.getInstance().initialize(params);
const instance2 = E2EIHandler.getInstance().initialize(params);
expect(instance1).toBe(instance2);
});

it('should set currentStep to INITIALIZE after initialize is called', async () => {
const instance = E2EIHandler.getInstance();
await instance.initialize(params);
instance.initialize(params);
void instance.attemptEnrollment();
await wait(1);
expect(instance['currentStep']).toBe(E2EIHandlerStep.INITIALIZED);
Expand All @@ -135,7 +133,7 @@ describe('E2EIHandler', () => {

jest.spyOn(container.resolve(Core), 'enrollE2EI').mockResolvedValueOnce(true);

const instance = await E2EIHandler.getInstance().initialize(params);
const instance = E2EIHandler.getInstance().initialize(params);
void instance['enroll']();
await wait(1);
expect(instance['currentStep']).toBe(E2EIHandlerStep.SUCCESS);
Expand All @@ -146,14 +144,14 @@ describe('E2EIHandler', () => {
jest.spyOn(container.resolve(Core), 'enrollE2EI').mockImplementationOnce(jest.fn(() => Promise.reject()));
jest.spyOn(container.resolve(UserState), 'self').mockImplementationOnce(() => user);

const instance = await E2EIHandler.getInstance().initialize(params);
const instance = E2EIHandler.getInstance().initialize(params);
void instance['enroll']();
await wait(1);
expect(instance['currentStep']).toBe(E2EIHandlerStep.ERROR);
});

it('should display user info message when initialized', async () => {
const instance = await E2EIHandler.getInstance().initialize(params);
const instance = E2EIHandler.getInstance().initialize(params);
void instance.attemptEnrollment();
await wait(1);
expect(getModalOptions).toHaveBeenCalledWith(
Expand All @@ -170,7 +168,7 @@ describe('E2EIHandler', () => {
});

it('should display loading message when enroled', async () => {
const handler = await E2EIHandler.getInstance().initialize(params);
const handler = E2EIHandler.getInstance().initialize(params);
void handler['enroll']();
await wait(1);
expect(getModalOptions).toHaveBeenCalledWith(
Expand All @@ -183,7 +181,7 @@ describe('E2EIHandler', () => {
it('should display success message when enrollment is done', async () => {
jest.spyOn(container.resolve(Core), 'enrollE2EI').mockResolvedValueOnce(true);

const handler = await E2EIHandler.getInstance().initialize(params);
const handler = E2EIHandler.getInstance().initialize(params);
handler['showLoadingMessage'] = jest.fn();
void handler['enroll']();
await wait(1);
Expand All @@ -197,7 +195,7 @@ describe('E2EIHandler', () => {
it('should display error message when enrollment fails', async () => {
jest.spyOn(container.resolve(Core), 'enrollE2EI').mockRejectedValueOnce(false);

const handler = await E2EIHandler.getInstance().initialize(params);
const handler = E2EIHandler.getInstance().initialize(params);
handler['showLoadingMessage'] = jest.fn();
void handler['enroll']();
await wait(1);
Expand All @@ -220,7 +218,7 @@ describe('E2EIHandler', () => {
const renewCertificateSpy = jest.spyOn(handler as any, 'renewCertificate');

// Initialize E2EI
await handler.initialize(params);
handler.initialize(params);
void handler.attemptRenewal();
await wait(1);

Expand All @@ -240,7 +238,7 @@ describe('E2EIHandler', () => {
const enrollSpy = jest.spyOn(handler, 'enroll');

// Initialize E2EI
await handler.initialize(params);
handler.initialize(params);
void handler.attemptRenewal();
await wait(1);

Expand Down Expand Up @@ -272,30 +270,30 @@ describe('E2EIHandler', () => {
const renewCertificateSpy = jest.spyOn(handler as any, 'renewCertificate');

// Initialize E2EI
await handler.initialize(params);
handler.initialize(params);
void handler.attemptRenewal();
await wait(1);

expect(getCertificateDetails as jest.Mock).toHaveBeenCalled();
expect(renewCertificateSpy).not.toHaveBeenCalled();
});

it('call showE2EINotificationMessage when no active certificate is found', async () => {
it('call startEnrollment when no active certificate is found', async () => {
const handler = E2EIHandler.getInstance();

// Set active certificate to be false
(hasActiveCertificate as jest.Mock).mockResolvedValue(false);

const renewCertificateSpy = jest.spyOn(handler as any, 'renewCertificate');
const showE2EINotificationMessageSpy = jest.spyOn(handler as any, 'showE2EINotificationMessage');
const startEnrollmentSpy = jest.spyOn(handler as any, 'startEnrollment');

// Initialize E2EI
await handler.initialize(params);
handler.initialize(params);
void handler.attemptEnrollment();
await wait(1);

expect(renewCertificateSpy).not.toHaveBeenCalled();
expect(showE2EINotificationMessageSpy).toHaveBeenCalled();
expect(startEnrollmentSpy).toHaveBeenCalled();
});

it('for invalid certificate user can not get another certificate until deleting a client', async () => {
Expand All @@ -305,7 +303,7 @@ describe('E2EIHandler', () => {
(hasActiveCertificate as jest.Mock).mockResolvedValue(true);

const renewCertificateSpy = jest.spyOn(handler as any, 'renewCertificate');
const showE2EINotificationMessageSpy = jest.spyOn(handler as any, 'showE2EINotificationMessage');
const startEnrollmentSpy = jest.spyOn(handler as any, 'startEnrollment');

const timeRemainingMS = 5 * TimeInMillis.DAY; // 5 days remaining

Expand All @@ -318,11 +316,11 @@ describe('E2EIHandler', () => {
jest.spyOn(handler as any, 'shouldRefresh').mockReturnValue(false);

// Initialize E2EI
await handler.initialize(params);
handler.initialize(params);
void handler.attemptRenewal();
await wait(1);

expect(renewCertificateSpy).not.toHaveBeenCalled();
expect(showE2EINotificationMessageSpy).not.toHaveBeenCalled();
expect(startEnrollmentSpy).not.toHaveBeenCalled();
});
});
75 changes: 25 additions & 50 deletions src/script/E2EIdentity/E2EIdentityEnrollment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,6 @@ import {formatDelayTime, TIME_IN_MILLIS} from 'Util/TimeUtil';
import {removeUrlParameters} from 'Util/UrlUtil';
import {supportsMLS} from 'Util/util';

import {getDelayTime, shouldEnableSoftLock} from './DelayTimer/delay';
import {DelayTimerService} from './DelayTimer/DelayTimer';
import {
hasActiveCertificate,
isE2EIEnabled,
Expand All @@ -46,6 +44,8 @@ import {
import {getModalOptions, ModalType} from './Modals';
import {OIDCService} from './OIDCService';
import {OIDCServiceStore} from './OIDCService/OIDCServiceStorage';
import {getSnoozeTime, shouldEnableSoftLock} from './SnoozableTimer/delay';
import {SnoozableTimer} from './SnoozableTimer/SnoozableTimer';

import {Config} from '../Config';

Expand All @@ -68,7 +68,7 @@ type Events = {
};

export type EnrollmentConfig = {
timer: DelayTimerService;
timer: SnoozableTimer;
discoveryUrl: string;
gracePeriodInMs: number;
};
Expand All @@ -80,7 +80,7 @@ export class E2EIHandler extends TypedEventEmitter<Events> {
private readonly core = container.resolve(Core);
private readonly userState = container.resolve(UserState);
private config?: EnrollmentConfig;
private currentStep: E2EIHandlerStep | null = E2EIHandlerStep.UNINITIALIZED;
private currentStep: E2EIHandlerStep = E2EIHandlerStep.UNINITIALIZED;
private oidcService?: OIDCService;

private get coreE2EIService() {
Expand Down Expand Up @@ -126,12 +126,13 @@ export class E2EIHandler extends TypedEventEmitter<Events> {
this.config = {
discoveryUrl,
gracePeriodInMs,
timer: new DelayTimerService({
timer: new SnoozableTimer({
gracePeriodInMS: gracePeriodInMs,
gracePeriodExpiredCallback: () => null,
delayPeriodExpiredCallback: () => null,
onGracePeriodExpired: () => this.startEnrollment(ModalType.ENROLL),
onSnoozeExpired: () => this.startEnrollment(ModalType.ENROLL),
}),
};
this.currentStep = E2EIHandlerStep.INITIALIZED;
}
return this;
}
Expand All @@ -142,7 +143,7 @@ export class E2EIHandler extends TypedEventEmitter<Events> {
// If the client already has a certificate, we don't need to start the enrollment
return;
}
return this.showE2EINotificationMessage(ModalType.ENROLL);
return this.startEnrollment(ModalType.ENROLL);
}

public async attemptRenewal(): Promise<void> {
Expand Down Expand Up @@ -203,7 +204,7 @@ export class E2EIHandler extends TypedEventEmitter<Events> {

// If the silent authentication fails, clear the oidc service progress/data and renew manually
await this.cleanUp(true);
this.showE2EINotificationMessage(ModalType.CERTIFICATE_RENEWAL);
await this.startEnrollment(ModalType.CERTIFICATE_RENEWAL);
}
}

Expand Down Expand Up @@ -377,7 +378,7 @@ export class E2EIHandler extends TypedEventEmitter<Events> {
resolve();
},
secondaryActionFn: async () => {
await this.showE2EINotificationMessage(ModalType.ENROLL);
await this.startEnrollment(ModalType.ENROLL);
resolve();
},
});
Expand All @@ -386,33 +387,12 @@ export class E2EIHandler extends TypedEventEmitter<Events> {
});
}

private shouldShowNotification(): boolean {
// If the user has already snoozed the notification, don't show it again until the snooze period has expired
if (this.currentStep !== E2EIHandlerStep.UNINITIALIZED && this.currentStep !== E2EIHandlerStep.SNOOZE) {
return false;
}
return true;
}

private initializeEnrollmentTimer(): void {
// Only initialize the timer when the it is uninitialized
if (this.currentStep === E2EIHandlerStep.UNINITIALIZED) {
this.config?.timer.updateParams({
gracePeriodInMS: this.config.gracePeriodInMs,
gracePeriodExpiredCallback: () => {
this.showE2EINotificationMessage(ModalType.ENROLL);
},
delayPeriodExpiredCallback: () => {
this.showE2EINotificationMessage(ModalType.ENROLL);
},
});
this.currentStep = E2EIHandlerStep.INITIALIZED;
}
}

private async showEnrollmentModal(modalType: ModalType.ENROLL | ModalType.CERTIFICATE_RENEWAL): Promise<void> {
private async showEnrollmentModal(
modalType: ModalType.ENROLL | ModalType.CERTIFICATE_RENEWAL,
config: EnrollmentConfig,
): Promise<void> {
// Show the modal with the provided modal type
const disableSnooze = await shouldEnableSoftLock(this.config!);
const disableSnooze = await shouldEnableSoftLock(config);
return new Promise<void>(resolve => {
const {modalOptions, modalType: determinedModalType} = getModalOptions({
hideSecondary: disableSnooze,
Expand All @@ -422,8 +402,8 @@ export class E2EIHandler extends TypedEventEmitter<Events> {
},
secondaryActionFn: () => {
this.currentStep = E2EIHandlerStep.SNOOZE;
this.config?.timer.delayPrompt();
this.showSnoozeModal();
this.config?.timer.snooze();
this.showSnoozeConfirmationModal();
resolve();
},
type: modalType,
Expand All @@ -433,38 +413,33 @@ export class E2EIHandler extends TypedEventEmitter<Events> {
});
}

private showSnoozeModal() {
private showSnoozeConfirmationModal() {
// Show the modal with the provided modal type
const {modalOptions, modalType: determinedModalType} = getModalOptions({
type: ModalType.SNOOZE_REMINDER,
hideClose: true,
extraParams: {
delayTime: formatDelayTime(getDelayTime(this.config!.gracePeriodInMs)),
delayTime: formatDelayTime(getSnoozeTime(this.config!.gracePeriodInMs)),
},
});
PrimaryModal.show(determinedModalType, modalOptions);
}

public async showE2EINotificationMessage(
modalType: ModalType.CERTIFICATE_RENEWAL | ModalType.ENROLL,
disableSnooze: boolean = false,
): Promise<void> {
private async startEnrollment(enrollmentType: ModalType.CERTIFICATE_RENEWAL | ModalType.ENROLL): Promise<void> {
// If the user has already started enrolment, don't show the notification. Instead, show the loading modal
// This will occur after the redirect from the oauth provider
if (this.coreE2EIService.isEnrollmentInProgress()) {
return this.enroll();
}

// Early return if we shouldn't show the notification
if (!this.shouldShowNotification()) {
if (this.config?.timer.isSnoozableTimerActive()) {
// If the user has snoozed, no need to show the notification modal
return;
}

this.initializeEnrollmentTimer();

// If the timer is not active, show the notification modal
if (this.config && !this.config.timer.isDelayTimerActive()) {
return this.showEnrollmentModal(modalType);
if (this.config) {
return this.showEnrollmentModal(enrollmentType, this.config);
}
}
}
Loading

0 comments on commit 8ffd716

Please sign in to comment.