Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add new verification section to user profile #29200

Draft
wants to merge 12 commits into
base: develop
Choose a base branch
from
25 changes: 0 additions & 25 deletions playwright/e2e/crypto/dehydration.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,13 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
Please see LICENSE files in the repository root for full details.
*/

import { type Locator, type Page } from "@playwright/test";

import { test, expect } from "../../element-web-test";
import { viewRoomSummaryByName } from "../right-panel/utils";
import { isDendrite } from "../../plugins/homeserver/dendrite";
import { completeCreateSecretStorageDialog, createBot, logIntoElement } from "./utils.ts";
import { type Client } from "../../pages/client.ts";

const ROOM_NAME = "Test room";
const NAME = "Alice";

function getMemberTileByName(page: Page, name: string): Locator {
return page.locator(`.mx_MemberTileView, [title="${name}"]`);
}

test.use({
displayName: NAME,
synapseConfig: {
Expand Down Expand Up @@ -70,23 +62,6 @@ test.describe("Dehydration", () => {
// device.
const sessionsTab = await app.settings.openUserSettings("Sessions");
await expect(sessionsTab.getByText("Dehydrated device")).not.toBeVisible();

await app.settings.closeDialog();

// now check that the user info right-panel shows the dehydrated device
// as a feature rather than as a normal device
await app.client.createRoom({ name: ROOM_NAME });

await viewRoomSummaryByName(page, app, ROOM_NAME);

await page.locator(".mx_RightPanel").getByRole("menuitem", { name: "People" }).click();
await expect(page.locator(".mx_MemberListView")).toBeVisible();

await getMemberTileByName(page, NAME).click();
await page.locator(".mx_UserInfo_devices .mx_UserInfo_expand").click();

await expect(page.locator(".mx_UserInfo_devices").getByText("Offline device enabled")).toBeVisible();
await expect(page.locator(".mx_UserInfo_devices").getByText("Dehydrated device")).not.toBeVisible();
});

test("Reset recovery key during login re-creates dehydrated device", async ({
Expand Down
28 changes: 4 additions & 24 deletions playwright/e2e/crypto/event-shields.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
logIntoElement,
logOutOfElement,
verify,
waitForDevices,
} from "./utils";
import { bootstrapCrossSigningForClient } from "../../pages/client.ts";
import { type ElementAppPage } from "../../pages/ElementAppPage.ts";
Expand Down Expand Up @@ -144,25 +145,8 @@ test.describe("Cryptography", function () {
// bob deletes his second device
await bobSecondDevice.evaluate((cli) => cli.logout(true));

// wait for the logout to propagate. Workaround for https://github.com/vector-im/element-web/issues/26263 by repeatedly closing and reopening Bob's user info.
async function awaitOneDevice(iterations = 1) {
const rightPanel = page.locator(".mx_RightPanel");
await rightPanel.getByTestId("base-card-back-button").click();
await rightPanel.getByText("Bob").click();
const sessionCountText = await rightPanel
.locator(".mx_UserInfo_devices")
.getByText(" session", { exact: false })
.textContent();
// cf https://github.com/vector-im/element-web/issues/26279: Element-R uses the wrong text here
if (sessionCountText != "1 session" && sessionCountText != "1 verified session") {
if (iterations >= 10) {
throw new Error(`Bob still has ${sessionCountText} after 10 iterations`);
}
await awaitOneDevice(iterations + 1);
}
}

await awaitOneDevice();
// wait for the logout to propagate.
await waitForDevices(app, bob.credentials.userId, 1);

// close and reopen the room, to get the shield to update.
await app.viewRoomByName("Bob");
Expand Down Expand Up @@ -285,11 +269,7 @@ test.describe("Cryptography", function () {
// Workaround for https://github.com/element-hq/element-web/issues/28640:
// make sure that Alice has seen Bob's identity before she goes offline. We do this by opening
// his user info.
await app.toggleRoomInfoPanel();
const rightPanel = page.locator(".mx_RightPanel");
await rightPanel.getByRole("menuitem", { name: "People" }).click();
await rightPanel.getByRole("button", { name: bob.credentials!.userId }).click();
await expect(rightPanel.locator(".mx_UserInfo_devices")).toContainText("1 session");
await waitForDevices(app, bob.credentials.userId, 1);

// Our app is blocked from syncing while Bob sends his messages.
await app.client.network.goOffline();
Expand Down
27 changes: 11 additions & 16 deletions playwright/e2e/crypto/user-verification.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,8 @@ Please see LICENSE files in the repository root for full details.

import { type Preset, type Visibility } from "matrix-js-sdk/src/matrix";

import type { Page } from "@playwright/test";
import { test, expect } from "../../element-web-test";
import { doTwoWaySasVerification, awaitVerifier } from "./utils";
import { doTwoWaySasVerification, awaitVerifier, waitForDevices } from "./utils";
import { type Client } from "../../pages/client";

test.describe("User verification", () => {
Expand All @@ -33,13 +32,17 @@ test.describe("User verification", () => {
});

test("can receive a verification request when there is no existing DM", async ({
app,
page,
bot: bob,
user: aliceCredentials,
toasts,
room: { roomId: dmRoomId },
}) => {
await waitForDeviceKeys(page);
await waitForDevices(app, bob.credentials.userId, 1);
await expect(page.getByRole("button", { name: "Avatar" })).toBeVisible();
const avatar = page.getByRole("button", { name: "Avatar" });
await avatar.click();

// once Alice has joined, Bob starts the verification
const bobVerificationRequest = await bob.evaluateHandle(
Expand Down Expand Up @@ -84,13 +87,17 @@ test.describe("User verification", () => {
});

test("can abort emoji verification when emoji mismatch", async ({
app,
page,
bot: bob,
user: aliceCredentials,
toasts,
room: { roomId: dmRoomId },
}) => {
await waitForDeviceKeys(page);
await waitForDevices(app, bob.credentials.userId, 1);
await expect(page.getByRole("button", { name: "Avatar" })).toBeVisible();
const avatar = page.getByRole("button", { name: "Avatar" });
await avatar.click();

// once Alice has joined, Bob starts the verification
const bobVerificationRequest = await bob.evaluateHandle(
Expand Down Expand Up @@ -154,15 +161,3 @@ async function createDMRoom(client: Client, userId: string): Promise<string> {
],
});
}

/**
* Wait until we get the other user's device keys.
* In newer rust-crypto versions, the verification request will be ignored if we
* don't have the sender's device keys.
*/
async function waitForDeviceKeys(page: Page): Promise<void> {
await expect(page.getByRole("button", { name: "Avatar" })).toBeVisible();
const avatar = await page.getByRole("button", { name: "Avatar" });
await avatar.click();
await expect(page.getByText("1 session")).toBeVisible();
}
28 changes: 28 additions & 0 deletions playwright/e2e/crypto/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -499,3 +499,31 @@ export async function deleteCachedSecrets(page: Page) {
});
await page.reload();
}

/**
* Wait until the given user has a given number of devices.
* This function will check the device keys ten times and if
* the expected number of devices were not found by then, an
* error is thrown.
*/
export async function waitForDevices(
app: ElementAppPage,
userId: string,
expectedNumberOfDevices: number,
): Promise<void> {
const result = await app.client.evaluate(
async (cli, { userId, expectedNumberOfDevices }) => {
for (let i = 0; i < 10; ++i) {
const userDeviceMap = await cli.getCrypto()?.getUserDeviceInfo([userId], true);
const deviceMap = userDeviceMap?.get(userId);
if (deviceMap.size === expectedNumberOfDevices) return true;
await new Promise((r) => setTimeout(r, 500));
}
return false;
},
{ userId, expectedNumberOfDevices },
);
if (!result) {
throw new Error(`User ${userId} did not have ${expectedNumberOfDevices} devices within ten iterations!`);
}
}
1 change: 0 additions & 1 deletion playwright/e2e/user-view/user-view.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ test.describe("UserView", () => {

const rightPanel = page.locator("#mx_RightPanel");
await expect(rightPanel.getByRole("heading", { name: bot.credentials.displayName, exact: true })).toBeVisible();
await expect(rightPanel.getByText("1 session")).toBeVisible();
await expect(rightPanel).toMatchScreenshot("user-info.png", {
mask: [page.locator(".mx_UserInfo_profile_mxid")],
css: `
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
26 changes: 22 additions & 4 deletions res/css/views/right_panel/_UserInfo.pcss
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,6 @@ Please see LICENSE files in the repository root for full details.
padding: var(--cpd-space-2x) 0 var(--cpd-space-4x);
margin: 0 var(--cpd-space-4x);

.mx_UserInfo_container_verifyButton {
margin-top: $spacing-8;
}

& + .mx_UserInfo_container {
border-top: 1px solid $separator;
}
Expand Down Expand Up @@ -180,6 +176,28 @@ Please see LICENSE files in the repository root for full details.
opacity: 1;
}

.mx_UserInfo_verification {
margin-top: var(--cpd-space-4x);
height: 36px;

.mx_UserInfo_verified_badge {
width: 68px;
height: 20px;

.mx_UserInfo_verified_icon {
flex-shrink: 0;
}

.mx_UserInfo_verified_label {
margin: 0;
}
}

.mx_UserInfo_verification_unavailable {
color: var(--cpd-color-text-secondary);
}
}

.mx_UserInfo_memberDetails {
.mx_UserInfo_profileField {
display: flex;
Expand Down
Loading
Loading