Skip to content
This repository has been archived by the owner on Sep 11, 2024. It is now read-only.

Device manager - confirm sign out of other sessions (PSG-921) #9487

Merged
merged 7 commits into from
Oct 25, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions cypress/e2e/settings/device-management.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ describe("Device manager", () => {
cy.get('.mx_FilteredDeviceList_list .mx_FilteredDeviceList_listItem .mx_Checkbox').last().click();
// sign out from list selection action buttons
cy.get('[data-testid="sign-out-selection-cta"]').click();
cy.get('[data-testid="dialog-primary-button"]').click();
// list updated after sign out
cy.get('.mx_FilteredDeviceList_list').find('.mx_FilteredDeviceList_listItem').should('have.length', 1);
// security recommendation count updated
Expand Down Expand Up @@ -106,6 +107,8 @@ describe("Device manager", () => {
// sign out using the device details sign out
cy.get('[data-testid="device-detail-sign-out-cta"]').click();
});
// confirm the signout
cy.get('[data-testid="dialog-primary-button"]').click();

// no other sessions or security recommendations sections when only one session
cy.contains('Other sessions').should('not.exist');
Expand Down
4 changes: 2 additions & 2 deletions src/components/views/elements/DialogButtons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ export default class DialogButtons extends React.Component<IProps> {
cancelButton = <button
// important: the default type is 'submit' and this button comes before the
// primary in the DOM so will get form submissions unless we make it not a submit.
data-test-id="dialog-cancel-button"
data-testid="dialog-cancel-button"
type="button"
onClick={this.onCancelClick}
className={this.props.cancelButtonClass}
Expand All @@ -104,7 +104,7 @@ export default class DialogButtons extends React.Component<IProps> {
{ cancelButton }
{ this.props.children }
<button type={this.props.primaryIsSubmit ? 'submit' : 'button'}
data-test-id="dialog-primary-button"
data-testid="dialog-primary-button"
className={primaryButtonClassName}
onClick={this.props.onPrimaryButtonClick}
autoFocus={this.props.focus}
Expand Down
24 changes: 24 additions & 0 deletions src/components/views/settings/tabs/user/SessionManagerTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,25 @@ import LoginWithQRSection from '../../devices/LoginWithQRSection';
import LoginWithQR, { Mode } from '../../../auth/LoginWithQR';
import SettingsStore from '../../../../../settings/SettingsStore';
import { useAsyncMemo } from '../../../../../hooks/useAsyncMemo';
import QuestionDialog from '../../../dialogs/QuestionDialog';

const confirmSignOut = async (sessionsToSignOutCount: number): Promise<boolean> => {
const { finished } = Modal.createDialog(QuestionDialog, {
title: _t("Sign out"),
description: (
<div>
<p>{ _t("Are you sure you want to sign out of %(count)s sessions?", {
count: sessionsToSignOutCount,
}) }</p>
</div>
),
cancelButton: _t('Cancel'),
button: _t("Sign out"),
});
const [confirmed] = await finished;

return confirmed;
};

const useSignOut = (
matrixClient: MatrixClient,
Expand All @@ -61,6 +80,11 @@ const useSignOut = (
if (!deviceIds.length) {
return;
}
const userConfirmedSignout = await confirmSignOut(deviceIds.length);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The promise is not catch in case of reject, is-it relevant to catch it here ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Modal shouldn't throw any errors outside of exceptional circumstances so I think defaulting to the nearest error boundary is the right thing to do here.

if (!userConfirmedSignout) {
return;
}

try {
setSigningOutDeviceIds([...signingOutDeviceIds, ...deviceIds]);
await deleteDevicesWithInteractiveAuth(
Expand Down
4 changes: 3 additions & 1 deletion src/i18n/strings/en_EN.json
Original file line number Diff line number Diff line change
Expand Up @@ -1596,6 +1596,9 @@
"Sessions": "Sessions",
"Where you're signed in": "Where you're signed in",
"Manage your signed-in devices below. A device's name is visible to people you communicate with.": "Manage your signed-in devices below. A device's name is visible to people you communicate with.",
"Sign out": "Sign out",
"Are you sure you want to sign out of %(count)s sessions?|other": "Are you sure you want to sign out of %(count)s sessions?",
"Are you sure you want to sign out of %(count)s sessions?|one": "Are you sure you want to sign out of %(count)s session?",
"Other sessions": "Other sessions",
"For best security, verify your sessions and sign out from any session that you don't recognize or use anymore.": "For best security, verify your sessions and sign out from any session that you don't recognize or use anymore.",
"Sidebar": "Sidebar",
Expand Down Expand Up @@ -1732,7 +1735,6 @@
"Please enter verification code sent via text.": "Please enter verification code sent via text.",
"Verification code": "Verification code",
"Discovery options will appear once you have added a phone number above.": "Discovery options will appear once you have added a phone number above.",
"Sign out": "Sign out",
"Sign out all other sessions": "Sign out all other sessions",
"Current session": "Current session",
"Confirm logging out these devices by using Single Sign On to prove your identity.|other": "Confirm logging out these devices by using Single Sign On to prove your identity.",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import { mount, ReactWrapper } from 'enzyme';
import { act } from 'react-dom/test-utils';
import { IPassphraseInfo } from 'matrix-js-sdk/src/crypto/api';

import { findByTestId, getMockClientWithEventEmitter, unmockClientPeg } from '../../../test-utils';
import { findByAttr, getMockClientWithEventEmitter, unmockClientPeg } from '../../../test-utils';
import { findById, flushPromises } from '../../../test-utils';
import AccessSecretStorageDialog from "../../../../src/components/views/dialogs/security/AccessSecretStorageDialog";

Expand Down Expand Up @@ -91,7 +91,7 @@ describe("AccessSecretStorageDialog", () => {
wrapper.setProps({});
});

const submitButton = findByTestId(wrapper, 'dialog-primary-button').at(0);
const submitButton = findByAttr('data-testid')(wrapper, 'dialog-primary-button').at(0);
// submit button is enabled when key is valid
expect(submitButton.props().disabled).toBeFalsy();
expect(wrapper.find('.mx_AccessSecretStorageDialog_recoveryKeyFeedback').text()).toEqual('Looks good!');
Expand All @@ -112,7 +112,7 @@ describe("AccessSecretStorageDialog", () => {
// @ts-ignore private
await wrapper.instance().validateRecoveryKey();

const submitButton = findByTestId(wrapper, 'dialog-primary-button').at(0);
const submitButton = findByAttr('data-testid')(wrapper, 'dialog-primary-button').at(0);
// submit button is disabled when recovery key is invalid
expect(submitButton.props().disabled).toBeTruthy();
expect(
Expand Down
4 changes: 2 additions & 2 deletions test/components/views/dialogs/ExportDialog-test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,8 @@ describe('<ExportDialog />', () => {
const getAttachmentsCheckbox = (component) => component.find('input[id="include-attachments"]');
const getMessageCountInput = (component) => component.find('input[id="message-count"]');
const getExportFormatInput = (component, format) => component.find(`input[id="exportFormat-${format}"]`);
const getPrimaryButton = (component) => component.find('[data-test-id="dialog-primary-button"]');
const getSecondaryButton = (component) => component.find('[data-test-id="dialog-cancel-button"]');
const getPrimaryButton = (component) => component.find('[data-testid="dialog-primary-button"]');
const getSecondaryButton = (component) => component.find('[data-testid="dialog-cancel-button"]');

const submitForm = async (component) => act(async () => {
getPrimaryButton(component).simulate('click');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,14 +111,14 @@ exports[`<ChangelogDialog /> should fetch github proxy url for each repo with ol
class="mx_Dialog_buttons_row"
>
<button
data-test-id="dialog-cancel-button"
data-testid="dialog-cancel-button"
type="button"
>
Cancel
</button>
<button
class="mx_Dialog_primary"
data-test-id="dialog-primary-button"
data-testid="dialog-primary-button"
type="button"
>
Update
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -249,14 +249,14 @@ Array [
class="mx_Dialog_buttons_row"
>
<button
data-test-id="dialog-cancel-button"
data-testid="dialog-cancel-button"
type="button"
>
Cancel
</button>
<button
class="mx_Dialog_primary"
data-test-id="dialog-primary-button"
data-testid="dialog-primary-button"
type="button"
>
Export
Expand Down Expand Up @@ -474,14 +474,14 @@ Array [
class="mx_Dialog_buttons_row"
>
<button
data-test-id="dialog-cancel-button"
data-testid="dialog-cancel-button"
type="button"
>
Cancel
</button>
<button
class="mx_Dialog_primary"
data-test-id="dialog-primary-button"
data-testid="dialog-primary-button"
type="button"
>
Export
Expand Down Expand Up @@ -827,7 +827,7 @@ Array [
className="mx_Dialog_buttons_row"
>
<button
data-test-id="dialog-cancel-button"
data-testid="dialog-cancel-button"
disabled={false}
onClick={[Function]}
type="button"
Expand All @@ -836,7 +836,7 @@ Array [
</button>
<button
className="mx_Dialog_primary"
data-test-id="dialog-primary-button"
data-testid="dialog-primary-button"
onClick={[Function]}
type="button"
>
Expand Down Expand Up @@ -1102,14 +1102,14 @@ Array [
class="mx_Dialog_buttons_row"
>
<button
data-test-id="dialog-cancel-button"
data-testid="dialog-cancel-button"
type="button"
>
Cancel
</button>
<button
class="mx_Dialog_primary"
data-test-id="dialog-primary-button"
data-testid="dialog-primary-button"
type="button"
>
Export
Expand Down Expand Up @@ -1327,14 +1327,14 @@ Array [
class="mx_Dialog_buttons_row"
>
<button
data-test-id="dialog-cancel-button"
data-testid="dialog-cancel-button"
type="button"
>
Cancel
</button>
<button
class="mx_Dialog_primary"
data-test-id="dialog-primary-button"
data-testid="dialog-primary-button"
type="button"
>
Export
Expand Down Expand Up @@ -1680,7 +1680,7 @@ Array [
className="mx_Dialog_buttons_row"
>
<button
data-test-id="dialog-cancel-button"
data-testid="dialog-cancel-button"
disabled={false}
onClick={[Function]}
type="button"
Expand All @@ -1689,7 +1689,7 @@ Array [
</button>
<button
className="mx_Dialog_primary"
data-test-id="dialog-primary-button"
data-testid="dialog-primary-button"
onClick={[Function]}
type="button"
>
Expand Down Expand Up @@ -1942,14 +1942,14 @@ Array [
class="mx_Dialog_buttons_row"
>
<button
data-test-id="dialog-cancel-button"
data-testid="dialog-cancel-button"
type="button"
>
Cancel
</button>
<button
class="mx_Dialog_primary"
data-test-id="dialog-primary-button"
data-testid="dialog-primary-button"
type="button"
>
Export
Expand Down Expand Up @@ -2167,14 +2167,14 @@ Array [
class="mx_Dialog_buttons_row"
>
<button
data-test-id="dialog-cancel-button"
data-testid="dialog-cancel-button"
type="button"
>
Cancel
</button>
<button
class="mx_Dialog_primary"
data-test-id="dialog-primary-button"
data-testid="dialog-primary-button"
type="button"
>
Export
Expand Down Expand Up @@ -2520,7 +2520,7 @@ Array [
className="mx_Dialog_buttons_row"
>
<button
data-test-id="dialog-cancel-button"
data-testid="dialog-cancel-button"
disabled={false}
onClick={[Function]}
type="button"
Expand All @@ -2529,7 +2529,7 @@ Array [
</button>
<button
className="mx_Dialog_primary"
data-test-id="dialog-primary-button"
data-testid="dialog-primary-button"
onClick={[Function]}
type="button"
>
Expand Down Expand Up @@ -2873,7 +2873,7 @@ Array [
className="mx_Dialog_buttons_row"
>
<button
data-test-id="dialog-cancel-button"
data-testid="dialog-cancel-button"
disabled={false}
onClick={[Function]}
type="button"
Expand All @@ -2882,7 +2882,7 @@ Array [
</button>
<button
className="mx_Dialog_primary"
data-test-id="dialog-primary-button"
data-testid="dialog-primary-button"
onClick={[Function]}
type="button"
>
Expand Down
Loading