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

Device manager - add verify current session button (PSG-527) #9252

Merged
merged 3 commits into from
Sep 8, 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
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,11 @@ import { DeviceWithVerification } from './types';
interface Props {
device?: DeviceWithVerification;
isLoading: boolean;
onVerifyCurrentDevice: () => void;
}

const CurrentDeviceSection: React.FC<Props> = ({
device, isLoading,
device, isLoading, onVerifyCurrentDevice,
}) => {
const [isExpanded, setIsExpanded] = useState(false);

Expand All @@ -52,7 +53,7 @@ const CurrentDeviceSection: React.FC<Props> = ({
</DeviceTile>
{ isExpanded && <DeviceDetails device={device} /> }
<br />
<DeviceVerificationStatusCard device={device} />
<DeviceVerificationStatusCard device={device} onVerifyDevice={onVerifyCurrentDevice} />
</>
}
</SettingsSubsection>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ limitations under the License.
import React from 'react';

import { _t } from '../../../../languageHandler';
import AccessibleButton from '../../elements/AccessibleButton';
import DeviceSecurityCard from './DeviceSecurityCard';
import {
DeviceSecurityVariation,
Expand All @@ -25,12 +26,14 @@ import {

interface Props {
device: DeviceWithVerification;
onVerifyDevice?: () => void;
}

export const DeviceVerificationStatusCard: React.FC<Props> = ({
device,
onVerifyDevice,
}) => {
const securityCardProps = device?.isVerified ? {
const securityCardProps = device.isVerified ? {
variation: DeviceSecurityVariation.Verified,
heading: _t('Verified session'),
description: _t('This session is ready for secure messaging.'),
Expand All @@ -41,5 +44,15 @@ export const DeviceVerificationStatusCard: React.FC<Props> = ({
};
return <DeviceSecurityCard
{...securityCardProps}
/>;
>
{ !device.isVerified && !!onVerifyDevice &&
<AccessibleButton
kind='primary'
onClick={onVerifyDevice}
data-testid={`verification-status-button-${device.device_id}`}
>
{ _t('Verify session') }
</AccessibleButton>
}
</DeviceSecurityCard>;
};
41 changes: 22 additions & 19 deletions src/components/views/settings/devices/useOwnDevices.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/

import { useContext, useEffect, useState } from "react";
import { useCallback, useContext, useEffect, useState } from "react";
import { IMyDevice, MatrixClient } from "matrix-js-sdk/src/matrix";
import { CrossSigningInfo } from "matrix-js-sdk/src/crypto/CrossSigning";
import { logger } from "matrix-js-sdk/src/logger";
Expand Down Expand Up @@ -64,6 +64,7 @@ type DevicesState = {
devices: DevicesDictionary;
currentDeviceId: string;
isLoading: boolean;
refreshDevices: () => Promise<void>;
error?: OwnDevicesError;
};
export const useOwnDevices = (): DevicesState => {
Expand All @@ -75,30 +76,32 @@ export const useOwnDevices = (): DevicesState => {
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState<OwnDevicesError>();

useEffect(() => {
const getDevicesAsync = async () => {
setIsLoading(true);
try {
const devices = await fetchDevicesWithVerification(matrixClient);
setDevices(devices);
setIsLoading(false);
} catch (error) {
if (error.httpStatus == 404) {
// 404 probably means the HS doesn't yet support the API.
setError(OwnDevicesError.Unsupported);
} else {
logger.error("Error loading sessions:", error);
setError(OwnDevicesError.Default);
}
setIsLoading(false);
const refreshDevices = useCallback(async () => {
setIsLoading(true);
try {
const devices = await fetchDevicesWithVerification(matrixClient);
setDevices(devices);
setIsLoading(false);
} catch (error) {
if (error.httpStatus == 404) {
// 404 probably means the HS doesn't yet support the API.
setError(OwnDevicesError.Unsupported);
} else {
logger.error("Error loading sessions:", error);
setError(OwnDevicesError.Default);
}
};
getDevicesAsync();
setIsLoading(false);
}
}, [matrixClient]);

useEffect(() => {
refreshDevices();
}, [refreshDevices]);

return {
devices,
currentDeviceId,
refreshDevices,
isLoading,
error,
};
Expand Down
20 changes: 19 additions & 1 deletion src/components/views/settings/tabs/user/SessionManagerTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,16 @@ import CurrentDeviceSection from '../../devices/CurrentDeviceSection';
import SecurityRecommendations from '../../devices/SecurityRecommendations';
import { DeviceSecurityVariation, DeviceWithVerification } from '../../devices/types';
import SettingsTab from '../SettingsTab';
import Modal from '../../../../../Modal';
import SetupEncryptionDialog from '../../../dialogs/security/SetupEncryptionDialog';

const SessionManagerTab: React.FC = () => {
const { devices, currentDeviceId, isLoading } = useOwnDevices();
const {
devices,
currentDeviceId,
isLoading,
refreshDevices,
} = useOwnDevices();
const [filter, setFilter] = useState<DeviceSecurityVariation>();
const [expandedDeviceIds, setExpandedDeviceIds] = useState<DeviceWithVerification['device_id'][]>([]);
const filteredDeviceListRef = useRef<HTMLDivElement>(null);
Expand Down Expand Up @@ -57,6 +64,16 @@ const SessionManagerTab: React.FC = () => {
const { [currentDeviceId]: currentDevice, ...otherDevices } = devices;
const shouldShowOtherSessions = Object.keys(otherDevices).length > 0;

const onVerifyCurrentDevice = () => {
if (!currentDevice) {
return;
}
Modal.createDialog(
SetupEncryptionDialog as unknown as React.ComponentType,
{ onFinished: refreshDevices },
);
};

useEffect(() => () => {
clearTimeout(scrollIntoViewTimeoutRef.current);
}, [scrollIntoViewTimeoutRef]);
Expand All @@ -70,6 +87,7 @@ const SessionManagerTab: React.FC = () => {
<CurrentDeviceSection
device={currentDevice}
isLoading={isLoading}
onVerifyCurrentDevice={onVerifyCurrentDevice}
/>
{
shouldShowOtherSessions &&
Expand Down
2 changes: 1 addition & 1 deletion src/i18n/strings/en_EN.json
Original file line number Diff line number Diff line change
Expand Up @@ -1719,6 +1719,7 @@
"This session is ready for secure messaging.": "This session is ready for secure messaging.",
"Unverified session": "Unverified session",
"Verify or sign out from this session for best security and reliability.": "Verify or sign out from this session for best security and reliability.",
"Verify session": "Verify session",
"Verified sessions": "Verified sessions",
"For best security, sign out from any session that you don't recognize or use anymore.": "For best security, sign out from any session that you don't recognize or use anymore.",
"Unverified sessions": "Unverified sessions",
Expand Down Expand Up @@ -2765,7 +2766,6 @@
"Session name": "Session name",
"Session key": "Session key",
"If they don't match, the security of your communication may be compromised.": "If they don't match, the security of your communication may be compromised.",
"Verify session": "Verify session",
"Your homeserver doesn't seem to support this feature.": "Your homeserver doesn't seem to support this feature.",
"Message edits": "Message edits",
"Modal Widget": "Modal Widget",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ describe('<CurrentDeviceSection />', () => {

const defaultProps = {
device: alicesVerifiedDevice,
onVerifyCurrentDevice: jest.fn(),
isLoading: false,
};
const getComponent = (props = {}): React.ReactElement =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,18 @@ exports[`<CurrentDeviceSection /> renders device and correct security card when
>
Verify or sign out from this session for best security and reliability.
</p>
<div
class="mx_DeviceSecurityCard_actions"
>
<div
class="mx_AccessibleButton mx_AccessibleButton_hasKind mx_AccessibleButton_kind_primary"
data-testid="verification-status-button-alices_device"
role="button"
tabindex="0"
>
Verify session
</div>
</div>
</div>
</div>
</div>
Expand Down Expand Up @@ -316,6 +328,18 @@ exports[`<CurrentDeviceSection /> renders device and correct security card when
>
Verify or sign out from this session for best security and reliability.
</p>
<div
class="mx_DeviceSecurityCard_actions"
>
<div
class="mx_AccessibleButton mx_AccessibleButton_hasKind mx_AccessibleButton_kind_primary"
data-testid="verification-status-button-alices_device"
role="button"
tabindex="0"
>
Verify session
</div>
</div>
</div>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import {
getMockClientWithEventEmitter,
mockClientMethodsUser,
} from '../../../../../test-utils';
import Modal from '../../../../../../src/Modal';

jest.useFakeTimers();

Expand Down Expand Up @@ -154,6 +155,21 @@ describe('<SessionManagerTab />', () => {
expect(getByTestId('current-session-section')).toMatchSnapshot();
});

it('opens encryption setup dialog when verifiying current session', async () => {
mockClient.getDevices.mockResolvedValue({ devices: [alicesDevice, alicesMobileDevice] });
const { getByTestId } = render(getComponent());
const modalSpy = jest.spyOn(Modal, 'createDialog');

await act(async () => {
await flushPromisesWithFakeTimers();
});

// click verify button from current session section
fireEvent.click(getByTestId(`verification-status-button-${alicesDevice.device_id}`));

expect(modalSpy).toHaveBeenCalled();
});

it('renders current session section with a verified session', async () => {
mockClient.getDevices.mockResolvedValue({ devices: [alicesDevice, alicesMobileDevice] });
mockClient.getStoredDevice.mockImplementation(() => new DeviceInfo(alicesDevice.device_id));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,18 @@ exports[`<SessionManagerTab /> renders current session section with an unverifie
>
Verify or sign out from this session for best security and reliability.
</p>
<div
class="mx_DeviceSecurityCard_actions"
>
<div
class="mx_AccessibleButton mx_AccessibleButton_hasKind mx_AccessibleButton_kind_primary"
data-testid="verification-status-button-alices_device"
role="button"
tabindex="0"
>
Verify session
</div>
</div>
</div>
</div>
</div>
Expand Down