Skip to content

Commit

Permalink
runfix: reset timer only after user interaction with modal [WPB-9431] (
Browse files Browse the repository at this point in the history
…#17469)

* runfix: reset timer only after user interaction with modal

* test: reset timer on user interaction

* docs: add comment
  • Loading branch information
PatrykBuniX authored May 27, 2024
1 parent f473566 commit 2a302ee
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 10 deletions.
32 changes: 32 additions & 0 deletions src/script/E2EIdentity/E2EIdentityEnrollment.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,38 @@ describe('E2EIHandler', () => {
});

describe('startTimers()', () => {
it('should reset the timer after user interaction with modal', async () => {
jest.spyOn(coreMock.service!.e2eIdentity!, 'isEnrollmentInProgress').mockResolvedValue(false);
jest.spyOn(coreMock.service!.e2eIdentity!, 'isFreshMLSSelfClient').mockResolvedValue(false);
jest
.spyOn(e2EIdentityVerification, 'getActiveWireIdentity')
.mockResolvedValue(
generateWireIdentity(selfClientId, CredentialType.Basic, e2EIdentityVerification.MLSStatuses.VALID),
);

const instance = await E2EIHandler.getInstance().initialize(params);
await instance.startTimers();

const enrollmentStore = getEnrollmentStore(user.qualifiedId, selfClientId);

const mockedFireDate = Date.now() + 1000;

enrollmentStore.store.timer(mockedFireDate);
jest.spyOn(enrollmentStore.clear, 'timer');

expect(enrollmentStore.get.timer()).toEqual(mockedFireDate);

const secondaryActions = modalMock.mock.calls[0][1].secondaryAction;
const action = Array.isArray(secondaryActions) ? secondaryActions[0].action : secondaryActions?.action;
await action?.();

await waitFor(() => {
expect(enrollmentStore.get.timer()).not.toEqual(mockedFireDate);
});

return instance;
});

describe('should start enrollment for existing devices', () => {
it('with a basic credential type', async () => {
jest.spyOn(coreMock.service!.e2eIdentity!, 'isEnrollmentInProgress').mockResolvedValue(false);
Expand Down
27 changes: 17 additions & 10 deletions src/script/E2EIdentity/E2EIdentityEnrollment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,10 @@ export class E2EIHandler extends TypedEventEmitter<Events> {

/**
* Will initiate the timer that will regularly prompt the user to enroll (or to renew the certificate if it is about to expire)
* - If the client is a brand new device (never logged in before) and the feature is enabled, the timer will start immediately, and the user will be forced to enroll
* - If the client is an existing MLS device, and the E2EI feature was just activated, the timer will start immediately (but the grace period will be respected)
* - If the client has already enrolled, but the cert is about to expire, they will be reminded to renew the certificate during the grace period
* - If the client has already enrolled, and the cert has already expired, they will be forced to enroll
* @returns the delay under which the next enrollment/renewal modal will be prompted
*/
public async startTimers() {
Expand All @@ -192,25 +196,25 @@ export class E2EIHandler extends TypedEventEmitter<Events> {
const timerKey = 'enrollmentTimer';
const identity = await getActiveWireIdentity();

const isNotActivated = identity?.status === MLSStatuses.NOT_ACTIVATED;
const isBasicDevice = identity?.credentialType === CredentialType.Basic;
const isFirstE2EIActivation = !storedE2eActivatedAt && (!identity || isNotActivated || isBasicDevice);

const {firingDate: computedFiringDate, isSnoozable} = getEnrollmentTimer(
identity,
e2eActivatedAt,
this.config.gracePeriodInMs,
);

const task = async (isSnoozable: boolean) => {
this.enrollmentStore.clear.timer();
await this.processEnrollmentUponExpiry(isSnoozable);
await this.processEnrollmentUponExpiry(isSnoozable, () => this.enrollmentStore.clear.timer());
};

const firingDate = this.enrollmentStore.get.timer() || computedFiringDate;
const storedFiringDate = this.enrollmentStore.get.timer();
const firingDate = isFirstE2EIActivation ? Date.now() : storedFiringDate || computedFiringDate;
this.enrollmentStore.store.timer(firingDate);

const isNotActivated = identity?.status === MLSStatuses.NOT_ACTIVATED;
const isBasicDevice = identity?.credentialType === CredentialType.Basic;

const isFirstE2EIActivation = !storedE2eActivatedAt && (!identity || isNotActivated || isBasicDevice);
if (isFirstE2EIActivation || firingDate <= Date.now()) {
if (firingDate <= Date.now()) {
// We want to automatically trigger the enrollment modal if it's a devices in team that just activated e2eidentity
// Or if the timer is supposed to fire now
void task(isSnoozable);
Expand All @@ -228,11 +232,11 @@ export class E2EIHandler extends TypedEventEmitter<Events> {
return firingDate - Date.now();
}

private async processEnrollmentUponExpiry(snoozable: boolean) {
private async processEnrollmentUponExpiry(snoozable: boolean, onUserAction: () => void) {
const hasCertificate = await hasActiveCertificate();
const enrollmentType = hasCertificate ? ModalType.CERTIFICATE_RENEWAL : ModalType.ENROLL;
this.emit('deviceStatusUpdated', {status: snoozable ? 'valid' : 'locked'});
await this.startEnrollment(enrollmentType, snoozable);
await this.startEnrollment(enrollmentType, snoozable, onUserAction);
}

/**
Expand Down Expand Up @@ -396,15 +400,18 @@ export class E2EIHandler extends TypedEventEmitter<Events> {
private async startEnrollment(
modalType: ModalType.ENROLL | ModalType.CERTIFICATE_RENEWAL,
snoozable: boolean,
onUserAction?: () => void,
): Promise<void> {
return new Promise<void>(resolve => {
const {modalOptions, modalType: determinedModalType} = getModalOptions({
hideSecondary: !snoozable,
primaryActionFn: async () => {
onUserAction?.();
await this.enroll({snoozable});
resolve();
},
secondaryActionFn: async () => {
onUserAction?.();
const delay = await this.startTimers();
if (delay > 0) {
this.showSnoozeConfirmationModal(delay);
Expand Down

0 comments on commit 2a302ee

Please sign in to comment.