Skip to content

Commit

Permalink
Add config option to force verification (#29)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
dbkr authored Sep 11, 2024
1 parent 75918f5 commit a701e3a
Show file tree
Hide file tree
Showing 4 changed files with 87 additions and 1 deletion.
2 changes: 2 additions & 0 deletions src/IConfigOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ export interface IConfigOptions {
auth_footer_links?: { text: string; url: string }[];
};

force_verification?: boolean; // if true, users must verify new logins

map_style_url?: string; // for location-shared maps

embedded_pages?: {
Expand Down
5 changes: 4 additions & 1 deletion src/components/structures/auth/CompleteSecurity.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import SetupEncryptionBody from "./SetupEncryptionBody";
import AccessibleButton from "../../views/elements/AccessibleButton";
import CompleteSecurityBody from "../../views/auth/CompleteSecurityBody";
import AuthPage from "../../views/auth/AuthPage";
import SdkConfig from "../../../SdkConfig";

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

const forceVerification = SdkConfig.get("force_verification") ?? false;

let skipButton;
if (phase === Phase.Intro || phase === Phase.ConfirmReset) {
if (!forceVerification && (phase === Phase.Intro || phase === Phase.ConfirmReset)) {
skipButton = (
<AccessibleButton
onClick={this.onSkipClick}
Expand Down
79 changes: 79 additions & 0 deletions test/components/structures/auth/CompleteSecurity-test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/*
Copyright 2024 New Vector Ltd.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
Please see LICENSE files in the repository root for full details.
*/

import React from "react";
import { render, screen } from "@testing-library/react";
import { mocked } from "jest-mock";
import EventEmitter from "events";

import CompleteSecurity from "../../../../src/components/structures/auth/CompleteSecurity";
import { stubClient } from "../../../test-utils";
import { Phase, SetupEncryptionStore } from "../../../../src/stores/SetupEncryptionStore";
import SdkConfig from "../../../../src/SdkConfig";

class MockSetupEncryptionStore extends EventEmitter {
public phase: Phase = Phase.Intro;
public lostKeys(): boolean {
return false;
}

public start: () => void = jest.fn();
public stop: () => void = jest.fn();
}

describe("CompleteSecurity", () => {
beforeEach(() => {
const client = stubClient();
const deviceIdToDevice = new Map();
deviceIdToDevice.set("DEVICE_ID", {
deviceId: "DEVICE_ID",
userId: "USER_ID",
});
const userIdToDevices = new Map();
userIdToDevices.set("USER_ID", deviceIdToDevice);
mocked(client.getCrypto()!.getUserDeviceInfo).mockResolvedValue(userIdToDevices);

const mockSetupEncryptionStore = new MockSetupEncryptionStore();
jest.spyOn(SetupEncryptionStore, "sharedInstance").mockReturnValue(
mockSetupEncryptionStore as SetupEncryptionStore,
);
});

afterEach(() => {
jest.restoreAllMocks();
});

it("Renders with a cancel button by default", () => {
render(<CompleteSecurity onFinished={() => {}} />);

expect(screen.getByRole("button", { name: "Skip verification for now" })).toBeInTheDocument();
});

it("Renders with a cancel button if forceVerification false", () => {
jest.spyOn(SdkConfig, "get").mockImplementation((key: string) => {
if (key === "forceVerification") {
return false;
}
});

render(<CompleteSecurity onFinished={() => {}} />);

expect(screen.getByRole("button", { name: "Skip verification for now" })).toBeInTheDocument();
});

it("Renders without a cancel button if forceVerification true", () => {
jest.spyOn(SdkConfig, "get").mockImplementation((key: string) => {
if (key === "force_verification") {
return true;
}
});

render(<CompleteSecurity onFinished={() => {}} />);

expect(screen.queryByRole("button", { name: "Skip verification for now" })).not.toBeInTheDocument();
});
});
2 changes: 2 additions & 0 deletions test/test-utils/test-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ export function createTestClient(): MatrixClient {

secretStorage: {
get: jest.fn(),
isStored: jest.fn().mockReturnValue(false),
},

store: {
Expand All @@ -128,6 +129,7 @@ export function createTestClient(): MatrixClient {
getDeviceVerificationStatus: jest.fn(),
resetKeyBackup: jest.fn(),
isEncryptionEnabledInRoom: jest.fn(),
getVerificationRequestsToDeviceInProgress: jest.fn().mockReturnValue([]),
}),

getPushActionsForEvent: jest.fn(),
Expand Down

0 comments on commit a701e3a

Please sign in to comment.