VerificationPanel: avoid use of `getStoredDevice` (#11129)

* VerificationPanel: avoid use of `getStoredDevice`

This is deprecated and doesn't work with the rust-sdk.

* fix types
pull/28217/head
Richard van der Hoff 2023-06-23 13:38:06 +01:00 committed by GitHub
parent 358c37ad69
commit 36c81f6416
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 100 additions and 23 deletions

View File

@ -18,15 +18,15 @@ import React from "react";
import { verificationMethods } from "matrix-js-sdk/src/crypto"; import { verificationMethods } from "matrix-js-sdk/src/crypto";
import { SCAN_QR_CODE_METHOD } from "matrix-js-sdk/src/crypto/verification/QRCode"; import { SCAN_QR_CODE_METHOD } from "matrix-js-sdk/src/crypto/verification/QRCode";
import { import {
VerificationRequest,
VerificationPhase as Phase, VerificationPhase as Phase,
VerificationRequest,
VerificationRequestEvent, VerificationRequestEvent,
} from "matrix-js-sdk/src/crypto-api"; } from "matrix-js-sdk/src/crypto-api";
import { RoomMember } from "matrix-js-sdk/src/models/room-member"; import { RoomMember } from "matrix-js-sdk/src/models/room-member";
import { User } from "matrix-js-sdk/src/models/user"; import { User } from "matrix-js-sdk/src/models/user";
import { logger } from "matrix-js-sdk/src/logger"; import { logger } from "matrix-js-sdk/src/logger";
import { DeviceInfo } from "matrix-js-sdk/src/crypto/deviceinfo";
import { ShowQrCodeCallbacks, ShowSasCallbacks, VerifierEvent } from "matrix-js-sdk/src/crypto-api/verification"; import { ShowQrCodeCallbacks, ShowSasCallbacks, VerifierEvent } from "matrix-js-sdk/src/crypto-api/verification";
import { Device } from "matrix-js-sdk/src/matrix";
import { MatrixClientPeg } from "../../../MatrixClientPeg"; import { MatrixClientPeg } from "../../../MatrixClientPeg";
import VerificationQRCode from "../elements/crypto/VerificationQRCode"; import VerificationQRCode from "../elements/crypto/VerificationQRCode";
@ -52,11 +52,21 @@ interface IState {
emojiButtonClicked?: boolean; emojiButtonClicked?: boolean;
reciprocateButtonClicked?: boolean; reciprocateButtonClicked?: boolean;
reciprocateQREvent: ShowQrCodeCallbacks | null; reciprocateQREvent: ShowQrCodeCallbacks | null;
/**
* Details of the other device involved in the transaction.
*
* `undefined` if there is not (yet) another device in the transaction, or if we do not know about it.
*/
otherDeviceDetails?: Device;
} }
export default class VerificationPanel extends React.PureComponent<IProps, IState> { export default class VerificationPanel extends React.PureComponent<IProps, IState> {
private hasVerifier: boolean; private hasVerifier: boolean;
/** have we yet tried to check the other device's info */
private haveCheckedDevice = false;
public constructor(props: IProps) { public constructor(props: IProps) {
super(props); super(props);
this.state = { sasEvent: null, reciprocateQREvent: null }; this.state = { sasEvent: null, reciprocateQREvent: null };
@ -201,14 +211,25 @@ export default class VerificationPanel extends React.PureComponent<IProps, IStat
this.state.reciprocateQREvent?.cancel(); this.state.reciprocateQREvent?.cancel();
}; };
private getDevice(): DeviceInfo | null { /**
* Get details of the other device involved in the verification, if we haven't before, and store in the state.
*/
private async maybeGetOtherDevice(): Promise<void> {
if (this.haveCheckedDevice) return;
const client = MatrixClientPeg.safeGet();
const deviceId = this.props.request?.otherDeviceId; const deviceId = this.props.request?.otherDeviceId;
const userId = MatrixClientPeg.safeGet().getUserId(); const userId = client.getUserId();
if (deviceId && userId) { if (!deviceId || !userId) {
return MatrixClientPeg.safeGet().getStoredDevice(userId, deviceId); return;
} else {
return null;
} }
this.haveCheckedDevice = true;
const deviceMap = await client.getCrypto()?.getUserDeviceInfo([userId]);
if (!deviceMap) return;
const userDevices = deviceMap.get(userId);
if (!userDevices) return;
this.setState({ otherDeviceDetails: userDevices.get(deviceId) });
} }
private renderQRReciprocatePhase(): JSX.Element { private renderQRReciprocatePhase(): JSX.Element {
@ -272,7 +293,7 @@ export default class VerificationPanel extends React.PureComponent<IProps, IStat
let description: string; let description: string;
if (request.isSelfVerification) { if (request.isSelfVerification) {
const device = this.getDevice(); const device = this.state.otherDeviceDetails;
if (!device) { if (!device) {
// This can happen if the device is logged out while we're still showing verification // This can happen if the device is logged out while we're still showing verification
// UI for it. // UI for it.
@ -280,8 +301,8 @@ export default class VerificationPanel extends React.PureComponent<IProps, IStat
description = _t("You've successfully verified your device!"); description = _t("You've successfully verified your device!");
} else { } else {
description = _t("You've successfully verified %(deviceName)s (%(deviceId)s)!", { description = _t("You've successfully verified %(deviceName)s (%(deviceId)s)!", {
deviceName: device ? device.getDisplayName() : "", deviceName: device.displayName,
deviceId: this.props.request.otherDeviceId, deviceId: device.deviceId,
}); });
} }
} else { } else {
@ -356,7 +377,7 @@ export default class VerificationPanel extends React.PureComponent<IProps, IStat
const emojis = this.state.sasEvent ? ( const emojis = this.state.sasEvent ? (
<VerificationShowSas <VerificationShowSas
displayName={displayName} displayName={displayName}
device={this.getDevice() ?? undefined} otherDeviceDetails={this.state.otherDeviceDetails}
sas={this.state.sasEvent.sas} sas={this.state.sasEvent.sas}
onCancel={this.onSasMismatchesClick} onCancel={this.onSasMismatchesClick}
onDone={this.onSasMatchesClick} onDone={this.onSasMatchesClick}
@ -410,6 +431,10 @@ export default class VerificationPanel extends React.PureComponent<IProps, IStat
private onRequestChange = async (): Promise<void> => { private onRequestChange = async (): Promise<void> => {
const { request } = this.props; const { request } = this.props;
// if we have a device ID and did not have one before, fetch the device's details
this.maybeGetOtherDevice();
const hadVerifier = this.hasVerifier; const hadVerifier = this.hasVerifier;
this.hasVerifier = !!request.verifier; this.hasVerifier = !!request.verifier;
if (!hadVerifier && this.hasVerifier) { if (!hadVerifier && this.hasVerifier) {

View File

@ -15,7 +15,7 @@ limitations under the License.
*/ */
import React from "react"; import React from "react";
import { DeviceInfo } from "matrix-js-sdk/src//crypto/deviceinfo"; import { Device } from "matrix-js-sdk/src/matrix";
import { GeneratedSas } from "matrix-js-sdk/src/crypto-api/verification"; import { GeneratedSas } from "matrix-js-sdk/src/crypto-api/verification";
import { _t, _td } from "../../../languageHandler"; import { _t, _td } from "../../../languageHandler";
@ -26,7 +26,10 @@ import { fixupColorFonts } from "../../../utils/FontManager";
interface IProps { interface IProps {
pending?: boolean; pending?: boolean;
displayName?: string; // required if pending is true displayName?: string; // required if pending is true
device?: DeviceInfo;
/** Details of the other device involved in the verification, if known */
otherDeviceDetails?: Device;
onDone: () => void; onDone: () => void;
onCancel: () => void; onCancel: () => void;
sas: GeneratedSas; sas: GeneratedSas;
@ -111,10 +114,11 @@ export default class VerificationShowSas extends React.Component<IProps, IState>
let text; let text;
// device shouldn't be null in this situation but it can be, eg. if the device is // device shouldn't be null in this situation but it can be, eg. if the device is
// logged out during verification // logged out during verification
if (this.props.device) { const otherDevice = this.props.otherDeviceDetails;
if (otherDevice) {
text = _t("Waiting for you to verify on your other device, %(deviceName)s (%(deviceId)s)…", { text = _t("Waiting for you to verify on your other device, %(deviceName)s (%(deviceId)s)…", {
deviceName: this.props.device ? this.props.device.getDisplayName() : "", deviceName: otherDevice.displayName,
deviceId: this.props.device ? this.props.device.deviceId : "", deviceId: otherDevice.deviceId,
}); });
} else { } else {
text = _t("Waiting for you to verify on your other device…"); text = _t("Waiting for you to verify on your other device…");

View File

@ -18,24 +18,27 @@ import { act, render, waitFor } from "@testing-library/react";
import React, { ComponentProps } from "react"; import React, { ComponentProps } from "react";
import { TypedEventEmitter } from "matrix-js-sdk/src/models/typed-event-emitter"; import { TypedEventEmitter } from "matrix-js-sdk/src/models/typed-event-emitter";
import { User } from "matrix-js-sdk/src/models/user"; import { User } from "matrix-js-sdk/src/models/user";
import { Mocked } from "jest-mock"; import { mocked, Mocked } from "jest-mock";
import { import {
EmojiMapping, EmojiMapping,
ShowSasCallbacks, ShowSasCallbacks,
Verifier,
VerifierEvent,
VerifierEventHandlerMap,
VerificationPhase as Phase, VerificationPhase as Phase,
VerificationRequest, VerificationRequest,
VerificationRequestEvent, VerificationRequestEvent,
Verifier,
VerifierEvent,
VerifierEventHandlerMap,
} from "matrix-js-sdk/src/crypto-api/verification"; } from "matrix-js-sdk/src/crypto-api/verification";
import { Device, MatrixClient } from "matrix-js-sdk/src/matrix";
import VerificationPanel from "../../../../src/components/views/right_panel/VerificationPanel"; import VerificationPanel from "../../../../src/components/views/right_panel/VerificationPanel";
import { stubClient } from "../../../test-utils"; import { flushPromises, stubClient } from "../../../test-utils";
describe("<VerificationPanel />", () => { describe("<VerificationPanel />", () => {
let client: MatrixClient;
beforeEach(() => { beforeEach(() => {
stubClient(); client = stubClient();
}); });
describe("'Ready' phase (dialog mode)", () => { describe("'Ready' phase (dialog mode)", () => {
@ -130,6 +133,51 @@ describe("<VerificationPanel />", () => {
expect(emoji).toHaveTextContent("🦄Unicorn"); expect(emoji).toHaveTextContent("🦄Unicorn");
} }
}); });
describe("'Verify own device' flow", () => {
beforeEach(() => {
Object.defineProperty(mockRequest, "isSelfVerification", { get: () => true });
Object.defineProperty(mockRequest, "otherDeviceId", { get: () => "other_device" });
const otherDeviceDetails = new Device({
algorithms: [],
deviceId: "other_device",
keys: new Map(),
userId: "",
displayName: "my other device",
});
mocked(client.getCrypto()!).getUserDeviceInfo.mockResolvedValue(
new Map([[client.getSafeUserId(), new Map([["other_device", otherDeviceDetails]])]]),
);
});
it("should show 'Waiting for you to verify' after confirming", async () => {
const rendered = renderComponent({
request: mockRequest,
phase: Phase.Started,
});
// wait for the device to be looked up
await act(() => flushPromises());
// fire the ShowSas event
const sasEvent = makeMockSasCallbacks();
mockVerifier.getShowSasCallbacks.mockReturnValue(sasEvent);
act(() => {
mockVerifier.emit(VerifierEvent.ShowSas, sasEvent);
});
// confirm
act(() => {
rendered.getByRole("button", { name: "They match" }).click();
});
expect(rendered.container).toHaveTextContent(
"Waiting for you to verify on your other device, my other device (other_device)…",
);
});
});
}); });
}); });