Skip to content
This repository was archived by the owner on Oct 22, 2024. It is now read-only.

Commit a701e3a

Browse files
authored
Add config option to force verification (#29)
* Add config option to force verification If this is set, users will not have the option to skip verification on login (they will still be able to reload and continue unverified, currently). Default off. * Test for complete security dialog * I hadn't set up prettier
1 parent 75918f5 commit a701e3a

File tree

4 files changed

+87
-1
lines changed

4 files changed

+87
-1
lines changed

Diff for: src/IConfigOptions.ts

+2
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ export interface IConfigOptions {
5252
auth_footer_links?: { text: string; url: string }[];
5353
};
5454

55+
force_verification?: boolean; // if true, users must verify new logins
56+
5557
map_style_url?: string; // for location-shared maps
5658

5759
embedded_pages?: {

Diff for: src/components/structures/auth/CompleteSecurity.tsx

+4-1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import SetupEncryptionBody from "./SetupEncryptionBody";
1414
import AccessibleButton from "../../views/elements/AccessibleButton";
1515
import CompleteSecurityBody from "../../views/auth/CompleteSecurityBody";
1616
import AuthPage from "../../views/auth/AuthPage";
17+
import SdkConfig from "../../../SdkConfig";
1718

1819
interface IProps {
1920
onFinished: () => void;
@@ -82,8 +83,10 @@ export default class CompleteSecurity extends React.Component<IProps, IState> {
8283
throw new Error(`Unknown phase ${phase}`);
8384
}
8485

86+
const forceVerification = SdkConfig.get("force_verification") ?? false;
87+
8588
let skipButton;
86-
if (phase === Phase.Intro || phase === Phase.ConfirmReset) {
89+
if (!forceVerification && (phase === Phase.Intro || phase === Phase.ConfirmReset)) {
8790
skipButton = (
8891
<AccessibleButton
8992
onClick={this.onSkipClick}
+79
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
/*
2+
Copyright 2024 New Vector Ltd.
3+
4+
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
5+
Please see LICENSE files in the repository root for full details.
6+
*/
7+
8+
import React from "react";
9+
import { render, screen } from "@testing-library/react";
10+
import { mocked } from "jest-mock";
11+
import EventEmitter from "events";
12+
13+
import CompleteSecurity from "../../../../src/components/structures/auth/CompleteSecurity";
14+
import { stubClient } from "../../../test-utils";
15+
import { Phase, SetupEncryptionStore } from "../../../../src/stores/SetupEncryptionStore";
16+
import SdkConfig from "../../../../src/SdkConfig";
17+
18+
class MockSetupEncryptionStore extends EventEmitter {
19+
public phase: Phase = Phase.Intro;
20+
public lostKeys(): boolean {
21+
return false;
22+
}
23+
24+
public start: () => void = jest.fn();
25+
public stop: () => void = jest.fn();
26+
}
27+
28+
describe("CompleteSecurity", () => {
29+
beforeEach(() => {
30+
const client = stubClient();
31+
const deviceIdToDevice = new Map();
32+
deviceIdToDevice.set("DEVICE_ID", {
33+
deviceId: "DEVICE_ID",
34+
userId: "USER_ID",
35+
});
36+
const userIdToDevices = new Map();
37+
userIdToDevices.set("USER_ID", deviceIdToDevice);
38+
mocked(client.getCrypto()!.getUserDeviceInfo).mockResolvedValue(userIdToDevices);
39+
40+
const mockSetupEncryptionStore = new MockSetupEncryptionStore();
41+
jest.spyOn(SetupEncryptionStore, "sharedInstance").mockReturnValue(
42+
mockSetupEncryptionStore as SetupEncryptionStore,
43+
);
44+
});
45+
46+
afterEach(() => {
47+
jest.restoreAllMocks();
48+
});
49+
50+
it("Renders with a cancel button by default", () => {
51+
render(<CompleteSecurity onFinished={() => {}} />);
52+
53+
expect(screen.getByRole("button", { name: "Skip verification for now" })).toBeInTheDocument();
54+
});
55+
56+
it("Renders with a cancel button if forceVerification false", () => {
57+
jest.spyOn(SdkConfig, "get").mockImplementation((key: string) => {
58+
if (key === "forceVerification") {
59+
return false;
60+
}
61+
});
62+
63+
render(<CompleteSecurity onFinished={() => {}} />);
64+
65+
expect(screen.getByRole("button", { name: "Skip verification for now" })).toBeInTheDocument();
66+
});
67+
68+
it("Renders without a cancel button if forceVerification true", () => {
69+
jest.spyOn(SdkConfig, "get").mockImplementation((key: string) => {
70+
if (key === "force_verification") {
71+
return true;
72+
}
73+
});
74+
75+
render(<CompleteSecurity onFinished={() => {}} />);
76+
77+
expect(screen.queryByRole("button", { name: "Skip verification for now" })).not.toBeInTheDocument();
78+
});
79+
});

Diff for: test/test-utils/test-utils.ts

+2
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ export function createTestClient(): MatrixClient {
108108

109109
secretStorage: {
110110
get: jest.fn(),
111+
isStored: jest.fn().mockReturnValue(false),
111112
},
112113

113114
store: {
@@ -128,6 +129,7 @@ export function createTestClient(): MatrixClient {
128129
getDeviceVerificationStatus: jest.fn(),
129130
resetKeyBackup: jest.fn(),
130131
isEncryptionEnabledInRoom: jest.fn(),
132+
getVerificationRequestsToDeviceInProgress: jest.fn().mockReturnValue([]),
131133
}),
132134

133135
getPushActionsForEvent: jest.fn(),

0 commit comments

Comments
 (0)