1
0
mirror of https://github.com/matrix-org/matrix-react-sdk.git synced 2025-08-07 21:23:00 +03:00

Replace uses of checkDeviceTrust with getDeviceVerificationStatus (#10663)

matrix-org/matrix-js-sdk#3287 and matrix-org/matrix-js-sdk#3303 added a new API called getDeviceVerificationStatus. Let's use it.
This commit is contained in:
Richard van der Hoff
2023-04-24 14:19:46 +01:00
committed by GitHub
parent aa8c0f5cc7
commit d7bb8043ea
22 changed files with 286 additions and 161 deletions

View File

@@ -18,9 +18,18 @@ import React from "react";
import { fireEvent, render, screen, waitFor, cleanup, act, within } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { Mocked, mocked } from "jest-mock";
import { Room, User, MatrixClient, RoomMember, MatrixEvent, EventType } from "matrix-js-sdk/src/matrix";
import {
Room,
User,
MatrixClient,
RoomMember,
MatrixEvent,
EventType,
CryptoApi,
DeviceVerificationStatus,
} from "matrix-js-sdk/src/matrix";
import { Phase, VerificationRequest } from "matrix-js-sdk/src/crypto/verification/request/VerificationRequest";
import { DeviceTrustLevel, UserTrustLevel } from "matrix-js-sdk/src/crypto/CrossSigning";
import { UserTrustLevel } from "matrix-js-sdk/src/crypto/CrossSigning";
import { DeviceInfo } from "matrix-js-sdk/src/crypto/deviceinfo";
import { defer } from "matrix-js-sdk/src/utils";
@@ -79,6 +88,7 @@ const defaultUser = new User(defaultUserId);
let mockRoom: Mocked<Room>;
let mockSpace: Mocked<Room>;
let mockClient: Mocked<MatrixClient>;
let mockCrypto: Mocked<CryptoApi>;
beforeEach(() => {
mockRoom = mocked({
@@ -115,6 +125,10 @@ beforeEach(() => {
getEventReadUpTo: jest.fn(),
} as unknown as Room);
mockCrypto = mocked({
getDeviceVerificationStatus: jest.fn(),
} as unknown as CryptoApi);
mockClient = mocked({
getUser: jest.fn(),
isGuest: jest.fn().mockReturnValue(false),
@@ -134,13 +148,13 @@ beforeEach(() => {
currentState: {
on: jest.fn(),
},
checkDeviceTrust: jest.fn(),
checkUserTrust: jest.fn(),
getRoom: jest.fn(),
credentials: {},
setPowerLevel: jest.fn(),
downloadKeys: jest.fn(),
getStoredDevicesForUser: jest.fn(),
getCrypto: jest.fn().mockReturnValue(mockCrypto),
} as unknown as MatrixClient);
jest.spyOn(MatrixClientPeg, "get").mockReturnValue(mockClient);
@@ -251,7 +265,6 @@ describe("<UserInfo />", () => {
beforeEach(() => {
mockClient.isCryptoEnabled.mockReturnValue(true);
mockClient.checkUserTrust.mockReturnValue(new UserTrustLevel(false, false, false));
mockClient.checkDeviceTrust.mockReturnValue(new DeviceTrustLevel(false, false, false, false));
const device1 = DeviceInfo.fromStorage(
{
@@ -370,10 +383,10 @@ describe("<DeviceItem />", () => {
mockClient.checkUserTrust.mockReturnValue({ isVerified: () => isVerified } as UserTrustLevel);
};
const setMockDeviceTrust = (isVerified = false, isCrossSigningVerified = false) => {
mockClient.checkDeviceTrust.mockReturnValue({
mockCrypto.getDeviceVerificationStatus.mockResolvedValue({
isVerified: () => isVerified,
isCrossSigningVerified: () => isCrossSigningVerified,
} as DeviceTrustLevel);
crossSigningVerified: isCrossSigningVerified,
} as DeviceVerificationStatus);
};
const mockVerifyDevice = jest.spyOn(mockVerification, "verifyDevice");
@@ -384,7 +397,7 @@ describe("<DeviceItem />", () => {
});
afterEach(() => {
mockClient.checkDeviceTrust.mockReset();
mockCrypto.getDeviceVerificationStatus.mockReset();
mockClient.checkUserTrust.mockReset();
mockVerifyDevice.mockClear();
});
@@ -393,32 +406,36 @@ describe("<DeviceItem />", () => {
mockVerifyDevice.mockRestore();
});
it("with unverified user and device, displays button without a label", () => {
it("with unverified user and device, displays button without a label", async () => {
renderComponent();
await act(flushPromises);
expect(screen.getByRole("button", { name: device.getDisplayName()! })).toBeInTheDocument;
expect(screen.queryByText(/trusted/i)).not.toBeInTheDocument();
});
it("with verified user only, displays button with a 'Not trusted' label", () => {
it("with verified user only, displays button with a 'Not trusted' label", async () => {
setMockUserTrust(true);
renderComponent();
await act(flushPromises);
expect(screen.getByRole("button", { name: `${device.getDisplayName()} Not trusted` })).toBeInTheDocument;
});
it("with verified device only, displays no button without a label", () => {
it("with verified device only, displays no button without a label", async () => {
setMockDeviceTrust(true);
renderComponent();
await act(flushPromises);
expect(screen.getByText(device.getDisplayName()!)).toBeInTheDocument();
expect(screen.queryByText(/trusted/)).not.toBeInTheDocument();
});
it("when userId is the same as userId from client, uses isCrossSigningVerified to determine if button is shown", () => {
it("when userId is the same as userId from client, uses isCrossSigningVerified to determine if button is shown", async () => {
mockClient.getSafeUserId.mockReturnValueOnce(defaultUserId);
mockClient.getUserId.mockReturnValueOnce(defaultUserId);
renderComponent();
await act(flushPromises);
// set trust to be false for isVerified, true for isCrossSigningVerified
setMockDeviceTrust(false, true);
@@ -428,10 +445,11 @@ describe("<DeviceItem />", () => {
expect(screen.getByText(device.getDisplayName()!)).toBeInTheDocument();
});
it("with verified user and device, displays no button and a 'Trusted' label", () => {
it("with verified user and device, displays no button and a 'Trusted' label", async () => {
setMockUserTrust(true);
setMockDeviceTrust(true);
renderComponent();
await act(flushPromises);
expect(screen.queryByRole("button")).not.toBeInTheDocument;
expect(screen.getByText(device.getDisplayName()!)).toBeInTheDocument();
@@ -441,6 +459,7 @@ describe("<DeviceItem />", () => {
it("does not call verifyDevice if client.getUser returns null", async () => {
mockClient.getUser.mockReturnValueOnce(null);
renderComponent();
await act(flushPromises);
const button = screen.getByRole("button", { name: device.getDisplayName()! });
expect(button).toBeInTheDocument;
@@ -455,6 +474,7 @@ describe("<DeviceItem />", () => {
// even more mocking
mockClient.isGuest.mockReturnValueOnce(true);
renderComponent();
await act(flushPromises);
const button = screen.getByRole("button", { name: device.getDisplayName()! });
expect(button).toBeInTheDocument;

View File

@@ -19,7 +19,7 @@ import { render, waitFor, screen, act, fireEvent } from "@testing-library/react"
import { mocked } from "jest-mock";
import { EventType } from "matrix-js-sdk/src/@types/event";
import { MatrixClient, PendingEventOrdering } from "matrix-js-sdk/src/client";
import { TweakName } from "matrix-js-sdk/src/matrix";
import { CryptoApi, TweakName } from "matrix-js-sdk/src/matrix";
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
import { NotificationCountType, Room } from "matrix-js-sdk/src/models/room";
import { DeviceTrustLevel, UserTrustLevel } from "matrix-js-sdk/src/crypto/CrossSigning";
@@ -30,7 +30,7 @@ import EventTile, { EventTileProps } from "../../../../src/components/views/room
import MatrixClientContext from "../../../../src/contexts/MatrixClientContext";
import RoomContext, { TimelineRenderingType } from "../../../../src/contexts/RoomContext";
import { MatrixClientPeg } from "../../../../src/MatrixClientPeg";
import { getRoomContext, mkEncryptedEvent, mkEvent, mkMessage, stubClient } from "../../../test-utils";
import { flushPromises, getRoomContext, mkEncryptedEvent, mkEvent, mkMessage, stubClient } from "../../../test-utils";
import { mkThread } from "../../../test-utils/threads";
import DMRoomMap from "../../../../src/utils/DMRoomMap";
import dis from "../../../../src/dispatcher/dispatcher";
@@ -221,13 +221,16 @@ describe("EventTile", () => {
// a version of checkDeviceTrust which says that TRUSTED_DEVICE is trusted, and others are not.
const trustedDeviceTrustLevel = DeviceTrustLevel.fromUserTrustLevel(trustedUserTrustLevel, true, false);
const untrustedDeviceTrustLevel = DeviceTrustLevel.fromUserTrustLevel(trustedUserTrustLevel, false, false);
client.checkDeviceTrust = (userId, deviceId) => {
if (deviceId === TRUSTED_DEVICE.deviceId) {
return trustedDeviceTrustLevel;
} else {
return untrustedDeviceTrustLevel;
}
};
const mockCrypto = {
getDeviceVerificationStatus: async (userId: string, deviceId: string) => {
if (deviceId === TRUSTED_DEVICE.deviceId) {
return trustedDeviceTrustLevel;
} else {
return untrustedDeviceTrustLevel;
}
},
} as unknown as CryptoApi;
client.getCrypto = () => mockCrypto;
});
it("shows a warning for an event from an unverified device", async () => {
@@ -243,6 +246,7 @@ describe("EventTile", () => {
} as IEncryptedEventInfo);
const { container } = getComponent();
await act(flushPromises);
const eventTiles = container.getElementsByClassName("mx_EventTile");
expect(eventTiles).toHaveLength(1);
@@ -270,6 +274,7 @@ describe("EventTile", () => {
} as IEncryptedEventInfo);
const { container } = getComponent();
await act(flushPromises);
const eventTiles = container.getElementsByClassName("mx_EventTile");
expect(eventTiles).toHaveLength(1);
@@ -295,6 +300,7 @@ describe("EventTile", () => {
} as IEncryptedEventInfo);
const { container } = getComponent();
await act(flushPromises);
const eventTiles = container.getElementsByClassName("mx_EventTile");
expect(eventTiles).toHaveLength(1);
@@ -317,8 +323,9 @@ describe("EventTile", () => {
sender: UNTRUSTED_DEVICE,
} as IEncryptedEventInfo);
act(() => {
await act(async () => {
mxEvent.makeReplaced(replacementEvent);
flushPromises();
});
// check it was updated
@@ -345,6 +352,7 @@ describe("EventTile", () => {
} as IEncryptedEventInfo);
const { container } = getComponent();
await act(flushPromises);
const eventTiles = container.getElementsByClassName("mx_EventTile");
expect(eventTiles).toHaveLength(1);
@@ -363,8 +371,9 @@ describe("EventTile", () => {
event: true,
});
act(() => {
await act(async () => {
mxEvent.makeReplaced(replacementEvent);
await flushPromises();
});
// check it was updated

View File

@@ -18,7 +18,6 @@ import { act, fireEvent, render } from "@testing-library/react";
import { DeviceInfo } from "matrix-js-sdk/src/crypto/deviceinfo";
import { sleep } from "matrix-js-sdk/src/utils";
import { PUSHER_DEVICE_ID, PUSHER_ENABLED } from "matrix-js-sdk/src/@types/event";
import { DeviceTrustLevel } from "matrix-js-sdk/src/crypto/CrossSigning";
import DevicesPanel from "../../../../src/components/views/settings/DevicesPanel";
import { flushPromises, getMockClientWithEventEmitter, mkPusher, mockClientMethodsUser } from "../../../test-utils";
@@ -29,16 +28,21 @@ describe("<DevicesPanel />", () => {
const device1 = { device_id: "device_1" };
const device2 = { device_id: "device_2" };
const device3 = { device_id: "device_3" };
const mockCrypto = {
getDeviceVerificationStatus: jest.fn().mockResolvedValue({
crossSigningVerified: false,
}),
};
const mockClient = getMockClientWithEventEmitter({
...mockClientMethodsUser(userId),
getDevices: jest.fn(),
getDeviceId: jest.fn().mockReturnValue(device1.device_id),
deleteMultipleDevices: jest.fn(),
checkDeviceTrust: jest.fn().mockReturnValue(new DeviceTrustLevel(false, false, false, false)),
getStoredDevice: jest.fn().mockReturnValue(new DeviceInfo("id")),
generateClientSecret: jest.fn(),
getPushers: jest.fn(),
setPusher: jest.fn(),
getCrypto: jest.fn().mockReturnValue(mockCrypto),
});
const getComponent = () => (

View File

@@ -18,7 +18,6 @@ import React from "react";
import { act, fireEvent, render, RenderResult } from "@testing-library/react";
import { DeviceInfo } from "matrix-js-sdk/src/crypto/deviceinfo";
import { logger } from "matrix-js-sdk/src/logger";
import { DeviceTrustLevel } from "matrix-js-sdk/src/crypto/CrossSigning";
import { VerificationRequest } from "matrix-js-sdk/src/crypto/verification/request/VerificationRequest";
import { defer, sleep } from "matrix-js-sdk/src/utils";
import {
@@ -30,7 +29,10 @@ import {
PUSHER_ENABLED,
IAuthData,
UNSTABLE_MSC3882_CAPABILITY,
CryptoApi,
DeviceVerificationStatus,
} from "matrix-js-sdk/src/matrix";
import { mocked } from "jest-mock";
import { clearAllModals } from "../../../../../test-utils";
import SessionManagerTab from "../../../../../../src/components/views/settings/tabs/user/SessionManagerTab";
@@ -78,9 +80,14 @@ describe("<SessionManagerTab />", () => {
cancel: jest.fn(),
on: jest.fn(),
} as unknown as VerificationRequest;
const mockCrypto = mocked({
getDeviceVerificationStatus: jest.fn(),
} as unknown as CryptoApi);
const mockClient = getMockClientWithEventEmitter({
...mockClientMethodsUser(aliceId),
checkDeviceTrust: jest.fn(),
getCrypto: jest.fn().mockReturnValue(mockCrypto),
getDevices: jest.fn(),
getStoredDevice: jest.fn(),
getDeviceId: jest.fn().mockReturnValue(deviceId),
@@ -171,7 +178,7 @@ describe("<SessionManagerTab />", () => {
const device = [alicesDevice, alicesMobileDevice].find((device) => device.device_id === id);
return device ? new DeviceInfo(device.device_id) : null;
});
mockClient.checkDeviceTrust.mockReset().mockReturnValue(new DeviceTrustLevel(false, false, false, false));
mockCrypto.getDeviceVerificationStatus.mockReset().mockResolvedValue(new DeviceVerificationStatus({}));
mockClient.getDevices.mockReset().mockResolvedValue({ devices: [alicesDevice, alicesMobileDevice] });
@@ -221,13 +228,13 @@ describe("<SessionManagerTab />", () => {
});
it("does not fail when checking device verification fails", async () => {
const logSpy = jest.spyOn(console, "error").mockImplementation(() => {});
const logSpy = jest.spyOn(console, "error").mockImplementation((e) => {});
mockClient.getDevices.mockResolvedValue({
devices: [alicesDevice, alicesMobileDevice],
});
const noCryptoError = new Error("End-to-end encryption disabled");
mockClient.checkDeviceTrust.mockImplementation(() => {
throw noCryptoError;
const failError = new Error("non-specific failure");
mockCrypto.getDeviceVerificationStatus.mockImplementation(() => {
throw failError;
});
render(getComponent());
@@ -236,9 +243,9 @@ describe("<SessionManagerTab />", () => {
});
// called for each device despite error
expect(mockClient.checkDeviceTrust).toHaveBeenCalledWith(aliceId, alicesDevice.device_id);
expect(mockClient.checkDeviceTrust).toHaveBeenCalledWith(aliceId, alicesMobileDevice.device_id);
expect(logSpy).toHaveBeenCalledWith("Error getting device cross-signing info", noCryptoError);
expect(mockCrypto.getDeviceVerificationStatus).toHaveBeenCalledWith(aliceId, alicesDevice.device_id);
expect(mockCrypto.getDeviceVerificationStatus).toHaveBeenCalledWith(aliceId, alicesMobileDevice.device_id);
expect(logSpy).toHaveBeenCalledWith("Error getting device cross-signing info", failError);
});
it("sets device verification status correctly", async () => {
@@ -246,14 +253,14 @@ describe("<SessionManagerTab />", () => {
devices: [alicesDevice, alicesMobileDevice, alicesOlderMobileDevice],
});
mockClient.getStoredDevice.mockImplementation((_userId, deviceId) => new DeviceInfo(deviceId));
mockClient.checkDeviceTrust.mockImplementation((_userId, deviceId) => {
mockCrypto.getDeviceVerificationStatus.mockImplementation(async (_userId, deviceId) => {
// alices device is trusted
if (deviceId === alicesDevice.device_id) {
return new DeviceTrustLevel(true, true, false, false);
return new DeviceVerificationStatus({ crossSigningVerified: true, localVerified: true });
}
// alices mobile device is not
if (deviceId === alicesMobileDevice.device_id) {
return new DeviceTrustLevel(false, false, false, false);
return new DeviceVerificationStatus({});
}
// alicesOlderMobileDevice does not support encryption
throw new Error("encryption not supported");
@@ -265,7 +272,7 @@ describe("<SessionManagerTab />", () => {
await flushPromises();
});
expect(mockClient.checkDeviceTrust).toHaveBeenCalledTimes(3);
expect(mockCrypto.getDeviceVerificationStatus).toHaveBeenCalledTimes(3);
expect(
getByTestId(`device-tile-${alicesDevice.device_id}`).querySelector('[aria-label="Verified"]'),
).toBeTruthy();
@@ -418,7 +425,9 @@ describe("<SessionManagerTab />", () => {
devices: [alicesDevice, alicesMobileDevice],
});
mockClient.getStoredDevice.mockImplementation(() => new DeviceInfo(alicesDevice.device_id));
mockClient.checkDeviceTrust.mockReturnValue(new DeviceTrustLevel(true, true, false, false));
mockCrypto.getDeviceVerificationStatus.mockResolvedValue(
new DeviceVerificationStatus({ crossSigningVerified: true, localVerified: true }),
);
const { getByTestId } = render(getComponent());
@@ -520,11 +529,11 @@ describe("<SessionManagerTab />", () => {
devices: [alicesDevice, alicesMobileDevice],
});
mockClient.getStoredDevice.mockImplementation((_userId, deviceId) => new DeviceInfo(deviceId));
mockClient.checkDeviceTrust.mockImplementation((_userId, deviceId) => {
mockCrypto.getDeviceVerificationStatus.mockImplementation(async (_userId, deviceId) => {
if (deviceId === alicesDevice.device_id) {
return new DeviceTrustLevel(true, true, false, false);
return new DeviceVerificationStatus({ crossSigningVerified: true, localVerified: true });
}
return new DeviceTrustLevel(false, false, false, false);
return new DeviceVerificationStatus({});
});
const { getByTestId } = render(getComponent());
@@ -547,12 +556,13 @@ describe("<SessionManagerTab />", () => {
devices: [alicesDevice, alicesMobileDevice],
});
mockClient.getStoredDevice.mockImplementation((_userId, deviceId) => new DeviceInfo(deviceId));
mockClient.checkDeviceTrust.mockImplementation((_userId, deviceId) => {
mockCrypto.getDeviceVerificationStatus.mockImplementation(async (_userId, deviceId) => {
// current session verified = able to verify other sessions
if (deviceId === alicesDevice.device_id) {
return new DeviceTrustLevel(true, true, false, false);
return new DeviceVerificationStatus({ crossSigningVerified: true, localVerified: true });
}
// but alicesMobileDevice doesn't support encryption
// XXX this is not what happens if a device doesn't support encryption.
throw new Error("encryption not supported");
});
@@ -581,11 +591,11 @@ describe("<SessionManagerTab />", () => {
devices: [alicesDevice, alicesMobileDevice],
});
mockClient.getStoredDevice.mockImplementation((_userId, deviceId) => new DeviceInfo(deviceId));
mockClient.checkDeviceTrust.mockImplementation((_userId, deviceId) => {
mockCrypto.getDeviceVerificationStatus.mockImplementation(async (_userId, deviceId) => {
if (deviceId === alicesDevice.device_id) {
return new DeviceTrustLevel(true, true, false, false);
return new DeviceVerificationStatus({ crossSigningVerified: true, localVerified: true });
}
return new DeviceTrustLevel(false, false, false, false);
return new DeviceVerificationStatus({});
});
const { getByTestId } = render(getComponent());