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

Commit

Permalink
DeviceListener: use new getUserDeviceInfo method (#10826)
Browse files Browse the repository at this point in the history
* DeviceListener: use new `getUserDeviceInfo` method

matrix-org/matrix-js-sdk#3272 added a new
`CryptoApi.getUserDeviceInfo` method, which also works with rust crypto. Update
DeviceListener to use it, thus making many of the cypress tests work on Element
Web R.

* add missing await

... mostly to silence the quality gate
  • Loading branch information
richvdh authored May 9, 2023
1 parent 339e7da commit 5e8488c
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 24 deletions.
43 changes: 27 additions & 16 deletions src/DeviceListener.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,21 +149,34 @@ export default class DeviceListener {
this.recheck();
}

private ensureDeviceIdsAtStartPopulated(): void {
private async ensureDeviceIdsAtStartPopulated(): Promise<void> {
if (this.ourDeviceIdsAtStart === null) {
const cli = MatrixClientPeg.get();
this.ourDeviceIdsAtStart = new Set(cli.getStoredDevicesForUser(cli.getUserId()!).map((d) => d.deviceId));
this.ourDeviceIdsAtStart = await this.getDeviceIds();
}
}

/** Get the device list for the current user
*
* @returns the set of device IDs
*/
private async getDeviceIds(): Promise<Set<string>> {
const cli = MatrixClientPeg.get();
const crypto = cli.getCrypto();
if (crypto === undefined) return new Set();

const userId = cli.getSafeUserId();
const devices = await crypto.getUserDeviceInfo([userId]);
return new Set(devices.get(userId)?.keys() ?? []);
}

private onWillUpdateDevices = async (users: string[], initialFetch?: boolean): Promise<void> => {
// If we didn't know about *any* devices before (ie. it's fresh login),
// then they are all pre-existing devices, so ignore this and set the
// devicesAtStart list to the devices that we see after the fetch.
if (initialFetch) return;

const myUserId = MatrixClientPeg.get().getUserId()!;
if (users.includes(myUserId)) this.ensureDeviceIdsAtStartPopulated();
if (users.includes(myUserId)) await this.ensureDeviceIdsAtStartPopulated();

// No need to do a recheck here: we just need to get a snapshot of our devices
// before we download any new ones.
Expand Down Expand Up @@ -299,7 +312,7 @@ export default class DeviceListener {

// This needs to be done after awaiting on downloadKeys() above, so
// we make sure we get the devices after the fetch is done.
this.ensureDeviceIdsAtStartPopulated();
await this.ensureDeviceIdsAtStartPopulated();

// Unverified devices that were there last time the app ran
// (technically could just be a boolean: we don't actually
Expand All @@ -319,18 +332,16 @@ export default class DeviceListener {
// as long as cross-signing isn't ready,
// you can't see or dismiss any device toasts
if (crossSigningReady) {
const devices = cli.getStoredDevicesForUser(cli.getUserId()!);
for (const device of devices) {
if (device.deviceId === cli.deviceId) continue;

const deviceTrust = await cli
.getCrypto()!
.getDeviceVerificationStatus(cli.getUserId()!, device.deviceId!);
if (!deviceTrust?.crossSigningVerified && !this.dismissed.has(device.deviceId)) {
if (this.ourDeviceIdsAtStart?.has(device.deviceId)) {
oldUnverifiedDeviceIds.add(device.deviceId);
const devices = await this.getDeviceIds();
for (const deviceId of devices) {
if (deviceId === cli.deviceId) continue;

const deviceTrust = await cli.getCrypto()!.getDeviceVerificationStatus(cli.getUserId()!, deviceId);
if (!deviceTrust?.crossSigningVerified && !this.dismissed.has(deviceId)) {
if (this.ourDeviceIdsAtStart?.has(deviceId)) {
oldUnverifiedDeviceIds.add(deviceId);
} else {
newUnverifiedDeviceIds.add(device.deviceId);
newUnverifiedDeviceIds.add(deviceId);
}
}
}
Expand Down
23 changes: 15 additions & 8 deletions test/DeviceListener-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@ limitations under the License.
import { Mocked, mocked } from "jest-mock";
import { MatrixEvent, Room, MatrixClient, DeviceVerificationStatus, CryptoApi } from "matrix-js-sdk/src/matrix";
import { logger } from "matrix-js-sdk/src/logger";
import { DeviceInfo } from "matrix-js-sdk/src/crypto/deviceinfo";
import { CrossSigningInfo } from "matrix-js-sdk/src/crypto/CrossSigning";
import { CryptoEvent } from "matrix-js-sdk/src/crypto";
import { IKeyBackupInfo } from "matrix-js-sdk/src/crypto/keybackup";
import { Device } from "matrix-js-sdk/src/models/device";

import DeviceListener from "../src/DeviceListener";
import { MatrixClientPeg } from "../src/MatrixClientPeg";
Expand Down Expand Up @@ -80,10 +80,12 @@ describe("DeviceListener", () => {
getDeviceVerificationStatus: jest.fn().mockResolvedValue({
crossSigningVerified: false,
}),
getUserDeviceInfo: jest.fn().mockResolvedValue(new Map()),
} as unknown as Mocked<CryptoApi>;
mockClient = getMockClientWithEventEmitter({
isGuest: jest.fn(),
getUserId: jest.fn().mockReturnValue(userId),
getSafeUserId: jest.fn().mockReturnValue(userId),
getKeyBackupVersion: jest.fn().mockResolvedValue(undefined),
getRooms: jest.fn().mockReturnValue([]),
isVersionSupported: jest.fn().mockResolvedValue(true),
Expand All @@ -92,7 +94,6 @@ describe("DeviceListener", () => {
isCryptoEnabled: jest.fn().mockReturnValue(true),
isInitialSyncComplete: jest.fn().mockReturnValue(true),
getKeyBackupEnabled: jest.fn(),
getStoredDevicesForUser: jest.fn().mockReturnValue([]),
getCrossSigningId: jest.fn(),
getStoredCrossSigningForUser: jest.fn(),
waitForClientWellKnown: jest.fn(),
Expand Down Expand Up @@ -393,16 +394,18 @@ describe("DeviceListener", () => {
});

describe("unverified sessions toasts", () => {
const currentDevice = new DeviceInfo(deviceId);
const device2 = new DeviceInfo("d2");
const device3 = new DeviceInfo("d3");
const currentDevice = new Device({ deviceId, userId: userId, algorithms: [], keys: new Map() });
const device2 = new Device({ deviceId: "d2", userId: userId, algorithms: [], keys: new Map() });
const device3 = new Device({ deviceId: "d3", userId: userId, algorithms: [], keys: new Map() });

const deviceTrustVerified = new DeviceVerificationStatus({ crossSigningVerified: true });
const deviceTrustUnverified = new DeviceVerificationStatus({});

beforeEach(() => {
mockClient!.isCrossSigningReady.mockResolvedValue(true);
mockClient!.getStoredDevicesForUser.mockReturnValue([currentDevice, device2, device3]);
mockCrypto!.getUserDeviceInfo.mockResolvedValue(
new Map([[userId, new Map([currentDevice, device2, device3].map((d) => [d.deviceId, d]))]]),
);
// all devices verified by default
mockCrypto!.getDeviceVerificationStatus.mockResolvedValue(deviceTrustVerified);
mockClient!.deviceId = currentDevice.deviceId;
Expand Down Expand Up @@ -525,13 +528,17 @@ describe("DeviceListener", () => {
return deviceTrustUnverified;
}
});
mockClient!.getStoredDevicesForUser.mockReturnValue([currentDevice, device2]);
mockCrypto!.getUserDeviceInfo.mockResolvedValue(
new Map([[userId, new Map([currentDevice, device2].map((d) => [d.deviceId, d]))]]),
);
await createAndStart();

expect(BulkUnverifiedSessionsToast.hideToast).toHaveBeenCalled();

// add an unverified device
mockClient!.getStoredDevicesForUser.mockReturnValue([currentDevice, device2, device3]);
mockCrypto!.getUserDeviceInfo.mockResolvedValue(
new Map([[userId, new Map([currentDevice, device2, device3].map((d) => [d.deviceId, d]))]]),
);
// trigger a recheck
mockClient!.emit(CryptoEvent.DevicesUpdated, [userId], false);
await flushPromises();
Expand Down

0 comments on commit 5e8488c

Please sign in to comment.