Skip to content

Commit 5af5203

Browse files
authored
feat: Allow reset E2E key from EnterE2EPassword modal (RocketChat#36778)
1 parent ccfae1c commit 5af5203

11 files changed

Lines changed: 135 additions & 23 deletions

File tree

.changeset/purple-sheep-bathe.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'@rocket.chat/i18n': minor
3+
'@rocket.chat/meteor': minor
4+
---
5+
6+
Introduces the ability to reset the e2e encrypted password from the enter e2e encrypted password modal

apps/meteor/app/e2e/server/methods/resetOwnE2EKey.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { MeteorError } from '@rocket.chat/core-services';
12
import type { ServerMethods } from '@rocket.chat/ddp-client';
23
import { Meteor } from 'meteor/meteor';
34

@@ -16,13 +17,15 @@ Meteor.methods<ServerMethods>({
1617
const userId = Meteor.userId();
1718

1819
if (!userId) {
19-
throw new Meteor.Error('error-invalid-user', 'Invalid user', {
20+
throw new MeteorError('error-invalid-user', 'Invalid user', {
2021
method: 'resetOwnE2EKey',
2122
});
2223
}
2324

2425
if (!(await resetUserE2EEncriptionKey(userId, false))) {
25-
return false;
26+
throw new MeteorError('failed-reset-e2e-password', 'Failed to reset E2E password', {
27+
method: 'resetOwnE2EKey',
28+
});
2629
}
2730
return true;
2831
}),

apps/meteor/client/views/account/security/AccountSecurityPage.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,15 +49,15 @@ const AccountSecurityPage = (): ReactElement => {
4949
{allowPasswordChange && (
5050
<FormProvider {...methods}>
5151
<Accordion>
52-
<AccordionItem title={t('Password')} expanded={!require2faSetup}>
52+
<AccordionItem title={t('Password')} defaultExpanded={!require2faSetup}>
5353
<ChangePassword id={passwordFormId} />
5454
</AccordionItem>
5555
</Accordion>
5656
</FormProvider>
5757
)}
5858
<Accordion>
5959
{(twoFactorTOTP || showEmailTwoFactor) && twoFactorEnabled && (
60-
<AccordionItem expanded={require2faSetup} title={t('Two Factor Authentication')}>
60+
<AccordionItem defaultExpanded={require2faSetup} title={t('Two Factor Authentication')}>
6161
{require2faSetup && (
6262
<Callout type='warning' title={t('Enable_two-factor_authentication')} mbe='24px'>
6363
{t('Enable_two-factor_authentication_callout_description')}

apps/meteor/client/views/account/security/EndToEnd.tsx

Lines changed: 5 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,22 @@
11
import { Box, PasswordInput, Field, FieldGroup, FieldLabel, FieldRow, FieldError, FieldHint, Button, Divider } from '@rocket.chat/fuselage';
2-
import { useToastMessageDispatch, useMethod, useTranslation, useLogout } from '@rocket.chat/ui-contexts';
2+
import { useToastMessageDispatch, useTranslation } from '@rocket.chat/ui-contexts';
33
import DOMPurify from 'dompurify';
44
import { Accounts } from 'meteor/accounts-base';
55
import type { ComponentProps, ReactElement } from 'react';
6-
import { useId, useCallback, useEffect } from 'react';
6+
import { useId, useEffect } from 'react';
77
import { Controller, useForm } from 'react-hook-form';
88

99
import { e2e } from '../../../lib/e2ee/rocketchat.e2e';
10+
import { useResetE2EPasswordMutation } from '../../hooks/useResetE2EPasswordMutation';
1011

1112
const EndToEnd = (props: ComponentProps<typeof Box>): ReactElement => {
1213
const t = useTranslation();
1314
const dispatchToastMessage = useToastMessageDispatch();
14-
const logout = useLogout();
1515

1616
const publicKey = Accounts.storageLocation.getItem('public_key');
1717
const privateKey = Accounts.storageLocation.getItem('private_key');
1818

19-
const resetE2eKey = useMethod('e2e.resetOwnE2EKey');
19+
const resetE2EPassword = useResetE2EPasswordMutation();
2020

2121
const {
2222
handleSubmit,
@@ -48,18 +48,6 @@ const EndToEnd = (props: ComponentProps<typeof Box>): ReactElement => {
4848
}
4949
};
5050

51-
const handleResetE2eKey = useCallback(async () => {
52-
try {
53-
const result = await resetE2eKey();
54-
if (result) {
55-
dispatchToastMessage({ type: 'success', message: t('User_e2e_key_was_reset') });
56-
logout();
57-
}
58-
} catch (error) {
59-
dispatchToastMessage({ type: 'error', message: error });
60-
}
61-
}, [dispatchToastMessage, resetE2eKey, logout, t]);
62-
6351
useEffect(() => {
6452
if (password?.trim() === '') {
6553
resetField('passwordConfirm');
@@ -161,7 +149,7 @@ const EndToEnd = (props: ComponentProps<typeof Box>): ReactElement => {
161149
{t('Reset_E2E_Key')}
162150
</Box>
163151
<Box is='p' fontScale='p1' mbe={12} dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(t('E2E_Reset_Key_Explanation')) }} />
164-
<Button onClick={handleResetE2eKey} data-qa-type='e2e-encryption-reset-key-button'>
152+
<Button onClick={() => resetE2EPassword.mutate()} data-qa-type='e2e-encryption-reset-key-button'>
165153
{t('Reset_E2E_Key')}
166154
</Button>
167155
</Box>

apps/meteor/client/views/e2e/EnterE2EPasswordModal/EnterE2EPasswordModal.tsx

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1-
import { Box, PasswordInput, Field, FieldGroup, FieldRow, FieldError } from '@rocket.chat/fuselage';
1+
import { Box, PasswordInput, Field, FieldGroup, FieldRow, FieldError, FieldLink } from '@rocket.chat/fuselage';
22
import { GenericModal } from '@rocket.chat/ui-client';
33
import DOMPurify from 'dompurify';
4-
import { useEffect, useId } from 'react';
4+
import { useEffect, useId, useState } from 'react';
55
import { Controller, useForm } from 'react-hook-form';
66
import { useTranslation } from 'react-i18next';
77

8+
import { useResetE2EPasswordMutation } from '../../hooks/useResetE2EPasswordMutation';
9+
810
type EnterE2EPasswordModalProps = {
911
onConfirm: (password: string) => void;
1012
onClose: () => void;
@@ -13,6 +15,9 @@ type EnterE2EPasswordModalProps = {
1315

1416
const EnterE2EPasswordModal = ({ onConfirm, onClose, onCancel }: EnterE2EPasswordModalProps) => {
1517
const { t } = useTranslation();
18+
const [confirmResetPassword, setConfirmResetPassword] = useState(false);
19+
const resetE2EPassword = useResetE2EPasswordMutation({ options: { onSettled: () => onClose() } });
20+
1621
const {
1722
handleSubmit,
1823
control,
@@ -30,6 +35,22 @@ const EnterE2EPasswordModal = ({ onConfirm, onClose, onCancel }: EnterE2EPasswor
3035
setFocus('password');
3136
}, [setFocus]);
3237

38+
if (confirmResetPassword) {
39+
return (
40+
<GenericModal
41+
variant='warning'
42+
title={t('Reset_E2EE_password')}
43+
icon='warning'
44+
confirmText={t('Reset_E2EE_password')}
45+
onClose={onClose}
46+
onCancel={onClose}
47+
onConfirm={() => resetE2EPassword.mutate()}
48+
>
49+
<Box is='p'>{t('Reset_E2EE_password_description')}</Box>
50+
</GenericModal>
51+
);
52+
}
53+
3354
return (
3455
<GenericModal
3556
wrapperFunction={(props) => <Box is='form' onSubmit={handleSubmit(({ password }) => onConfirm(password))} {...props} />}
@@ -66,6 +87,18 @@ const EnterE2EPasswordModal = ({ onConfirm, onClose, onCancel }: EnterE2EPasswor
6687
{errors.password.message}
6788
</FieldError>
6889
)}
90+
<FieldRow alignSelf='end'>
91+
<FieldLink
92+
href='#'
93+
target={undefined}
94+
onClick={(e) => {
95+
e.preventDefault();
96+
setConfirmResetPassword(true);
97+
}}
98+
>
99+
{t('Forgot_E2EE_Password')}
100+
</FieldLink>
101+
</FieldRow>
69102
</Field>
70103
</FieldGroup>
71104
</GenericModal>

apps/meteor/client/views/e2e/EnterE2EPasswordModal/__snapshots__/EnterE2EPasswordModal.spec.tsx.snap

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,16 @@ exports[`renders Default without crashing 1`] = `
9898
</span>
9999
</label>
100100
</span>
101+
<span
102+
class="rcx-box rcx-box--full rcx-field__row rcx-css-idbaye"
103+
>
104+
<a
105+
class="rcx-box rcx-box--full rcx-field__link"
106+
href="#"
107+
>
108+
Forgot_E2EE_Password
109+
</a>
110+
</span>
101111
</div>
102112
</fieldset>
103113
</div>
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { useLogout, useMethod, useToastMessageDispatch } from '@rocket.chat/ui-contexts';
2+
import type { MutationOptions } from '@tanstack/react-query';
3+
import { useMutation } from '@tanstack/react-query';
4+
import { useTranslation } from 'react-i18next';
5+
6+
export const useResetE2EPasswordMutation = ({ options }: { options?: MutationOptions } = {}) => {
7+
const { t } = useTranslation();
8+
9+
const logout = useLogout();
10+
const resetE2eKey = useMethod('e2e.resetOwnE2EKey');
11+
const dispatchToastMessage = useToastMessageDispatch();
12+
13+
return useMutation({
14+
mutationFn: async () => resetE2eKey(),
15+
onSuccess: () => {
16+
dispatchToastMessage({ type: 'success', message: t('User_e2e_key_was_reset') });
17+
logout();
18+
},
19+
onError: (error) => {
20+
dispatchToastMessage({ type: 'error', message: error });
21+
},
22+
...options,
23+
});
24+
};

apps/meteor/tests/e2e/e2e-encryption.spec.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
E2EEKeyDecodeFailureBanner,
1414
EnterE2EEPasswordBanner,
1515
EnterE2EEPasswordModal,
16+
ResetE2EEPasswordModal,
1617
SaveE2EEPasswordBanner,
1718
SaveE2EEPasswordModal,
1819
} from './page-objects/fragments/e2ee';
@@ -92,6 +93,22 @@ test.describe('initial setup', () => {
9293
await loginPage.loginByUserState(Users.admin);
9394
});
9495

96+
test('should reset e2e password from the modal', async ({ page }) => {
97+
const sidenav = new HomeSidenav(page);
98+
const loginPage = new LoginPage(page);
99+
const enterE2EEPasswordBanner = new EnterE2EEPasswordBanner(page);
100+
const enterE2EEPasswordModal = new EnterE2EEPasswordModal(page);
101+
const resetE2EEPasswordModal = new ResetE2EEPasswordModal(page);
102+
103+
await sidenav.logout();
104+
await loginPage.loginByUserState(Users.admin);
105+
await enterE2EEPasswordBanner.click();
106+
await enterE2EEPasswordModal.forgotPassword();
107+
await resetE2EEPasswordModal.confirmReset();
108+
109+
await loginPage.loginByUserState(Users.admin);
110+
});
111+
95112
test('expect to manually set a new password', async ({ page }) => {
96113
const accountSecurityPage = new AccountSecurityPage(page);
97114
const loginPage = new LoginPage(page);

apps/meteor/tests/e2e/page-objects/fragments/e2ee.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,10 @@ export class EnterE2EEPasswordModal extends Modal {
7575
return this.root.getByPlaceholder('Please enter your E2EE password');
7676
}
7777

78+
private get forgotPasswordLink() {
79+
return this.root.getByRole('link', { name: 'Forgot E2EE password?' });
80+
}
81+
7882
private get enterE2EEPasswordButton() {
7983
return this.root.getByRole('button', { name: 'Enable encryption' });
8084
}
@@ -84,6 +88,26 @@ export class EnterE2EEPasswordModal extends Modal {
8488
await this.enterE2EEPasswordButton.click();
8589
await this.waitForDismissal();
8690
}
91+
92+
async forgotPassword() {
93+
await this.forgotPasswordLink.click();
94+
await this.waitForDismissal();
95+
}
96+
}
97+
98+
export class ResetE2EEPasswordModal extends Modal {
99+
constructor(page: Page) {
100+
super(page.getByRole('dialog', { name: 'Reset E2EE password' }));
101+
}
102+
103+
private get resetE2EEPasswordButton() {
104+
return this.root.getByRole('button', { name: 'Reset E2EE password' });
105+
}
106+
107+
async confirmReset() {
108+
await this.resetE2EEPasswordButton.click();
109+
await this.waitForDismissal();
110+
}
87111
}
88112

89113
export class EnableRoomEncryptionModal extends Modal {

apps/meteor/tests/e2e/page-objects/home-channel.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ export class HomeChannel {
2727
this.tabs = new HomeFlextab(page);
2828
}
2929

30+
goto() {
31+
return this.page.goto('/home');
32+
}
33+
3034
get toastSuccess(): Locator {
3135
return this.page.locator('.rcx-toastbar.rcx-toastbar--success');
3236
}

0 commit comments

Comments
 (0)