diff --git a/src/components/views/settings/E2eAdvancedPanel.js b/src/components/views/settings/E2eAdvancedPanel.js index 0650630901..a8764fa855 100644 --- a/src/components/views/settings/E2eAdvancedPanel.js +++ b/src/components/views/settings/E2eAdvancedPanel.js @@ -19,6 +19,7 @@ import React from 'react'; import * as sdk from '../../../index'; import {_t} from "../../../languageHandler"; import {SettingLevel} from "../../../settings/SettingLevel"; +import SettingsStore from "../../../settings/SettingsStore"; const SETTING_MANUALLY_VERIFY_ALL_SESSIONS = "e2ee.manuallyVerifyAllSessions"; @@ -37,3 +38,7 @@ const E2eAdvancedPanel = props => { }; export default E2eAdvancedPanel; + +export function isE2eAdvancedPanelPossible(): boolean { + return SettingsStore.isEnabled(SETTING_MANUALLY_VERIFY_ALL_SESSIONS); +} diff --git a/src/components/views/settings/tabs/room/SecurityRoomSettingsTab.js b/src/components/views/settings/tabs/room/SecurityRoomSettingsTab.js index 48115146f1..0a0c693158 100644 --- a/src/components/views/settings/tabs/room/SecurityRoomSettingsTab.js +++ b/src/components/views/settings/tabs/room/SecurityRoomSettingsTab.js @@ -24,6 +24,7 @@ import Modal from "../../../../../Modal"; import QuestionDialog from "../../../dialogs/QuestionDialog"; import StyledRadioGroup from '../../../elements/StyledRadioGroup'; import {SettingLevel} from "../../../../../settings/SettingLevel"; +import SettingsStore from "../../../../../settings/SettingsStore"; export default class SecurityRoomSettingsTab extends React.Component { static propTypes = { @@ -340,10 +341,13 @@ export default class SecurityRoomSettingsTab extends React.Component { const canEnableEncryption = !isEncrypted && hasEncryptionPermission; let encryptionSettings = null; - if (isEncrypted) { - encryptionSettings = ; + if (isEncrypted && SettingsStore.isEnabled("blacklistUnverifiedDevices")) { + encryptionSettings = ; } return ( diff --git a/src/components/views/settings/tabs/user/SecurityUserSettingsTab.js b/src/components/views/settings/tabs/user/SecurityUserSettingsTab.js index 9984baeb13..61402e8881 100644 --- a/src/components/views/settings/tabs/user/SecurityUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/SecurityUserSettingsTab.js @@ -32,6 +32,7 @@ import {SettingLevel} from "../../../../../settings/SettingLevel"; import SecureBackupPanel from "../../SecureBackupPanel"; import SettingsStore from "../../../../../settings/SettingsStore"; import {UIFeature} from "../../../../../settings/UIFeature"; +import {isE2eAdvancedPanelPossible} from "../../E2eAdvancedPanel"; export class IgnoredUser extends React.Component { static propTypes = { @@ -219,6 +220,15 @@ export default class SecurityUserSettingsTab extends React.Component { ); } + let noSendUnverifiedSetting; + if (SettingsStore.isEnabled("blacklistUnverifiedDevices")) { + noSendUnverifiedSetting = ; + } + return (
{_t("Cryptography")} @@ -233,8 +243,7 @@ export default class SecurityUserSettingsTab extends React.Component { {importExportButtons} - + {noSendUnverifiedSetting}
); } @@ -355,14 +364,20 @@ export default class SecurityUserSettingsTab extends React.Component { const E2eAdvancedPanel = sdk.getComponent('views.settings.E2eAdvancedPanel'); let advancedSection; if (SettingsStore.getValue(UIFeature.AdvancedSettings)) { - advancedSection = <> -
{_t("Advanced")}
-
- {this._renderIgnoredUsers()} - {this._renderManageInvites()} - -
- ; + const ignoreUsersPanel = this._renderIgnoredUsers(); + const invitesPanel = this._renderManageInvites(); + const e2ePanel = isE2eAdvancedPanelPossible() ? : null; + // only show the section if there's something to show + if (ignoreUsersPanel || invitesPanel || e2ePanel) { + advancedSection = <> +
{_t("Advanced")}
+
+ {ignoreUsersPanel} + {invitesPanel} + {e2ePanel} +
+ ; + } } return ( diff --git a/src/settings/Settings.ts b/src/settings/Settings.ts index 16366aaa01..737c882919 100644 --- a/src/settings/Settings.ts +++ b/src/settings/Settings.ts @@ -34,6 +34,7 @@ import SettingController from "./controllers/SettingController"; import { RightPanelPhases } from "../stores/RightPanelStorePhases"; import UIFeatureController from "./controllers/UIFeatureController"; import { UIFeature } from "./UIFeature"; +import { OrderedMultiController } from "./controllers/OrderedMultiController"; // These are just a bunch of helper arrays to avoid copy/pasting a bunch of times const LEVELS_ROOM_SETTINGS = [ @@ -436,6 +437,7 @@ export const SETTINGS: {[setting: string]: ISetting} = { "room-device": _td('Never send encrypted messages to unverified sessions in this room from this session'), }, default: false, + controller: new UIFeatureController(UIFeature.AdvancedEncryption), }, "urlPreviewsEnabled": { supportedLevels: LEVELS_ROOM_SETTINGS_WITH_ROOM, @@ -591,9 +593,15 @@ export const SETTINGS: {[setting: string]: ISetting} = { supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS, displayName: _td("Manually verify all remote sessions"), default: false, - controller: new PushToMatrixClientController( - MatrixClient.prototype.setCryptoTrustCrossSignedDevices, true, - ), + controller: new OrderedMultiController([ + // Apply the feature controller first to ensure that the setting doesn't + // show up and can't be toggled. PushToMatrixClientController doesn't + // do any overrides anyways. + new UIFeatureController(UIFeature.AdvancedEncryption), + new PushToMatrixClientController( + MatrixClient.prototype.setCryptoTrustCrossSignedDevices, true, + ), + ]), }, "ircDisplayNameWidth": { // We specifically want to have room-device > device so that users may set a device default @@ -612,6 +620,10 @@ export const SETTINGS: {[setting: string]: ISetting} = { supportedLevels: LEVELS_ROOM_OR_ACCOUNT, default: {}, }, + [UIFeature.AdvancedEncryption]: { + supportedLevels: LEVELS_UI_FEATURE, + default: true, + }, [UIFeature.URLPreviews]: { supportedLevels: LEVELS_UI_FEATURE, default: true, diff --git a/src/settings/UIFeature.ts b/src/settings/UIFeature.ts index 57aefa3a78..231752e19c 100644 --- a/src/settings/UIFeature.ts +++ b/src/settings/UIFeature.ts @@ -16,6 +16,7 @@ limitations under the License. // see settings.md for documentation on conventions export enum UIFeature { + AdvancedEncryption = "UIFeature.advancedEncryption", URLPreviews = "UIFeature.urlPreviews", Widgets = "UIFeature.widgets", Voip = "UIFeature.voip", diff --git a/src/settings/controllers/OrderedMultiController.ts b/src/settings/controllers/OrderedMultiController.ts new file mode 100644 index 0000000000..2f093fe25a --- /dev/null +++ b/src/settings/controllers/OrderedMultiController.ts @@ -0,0 +1,54 @@ +/* +Copyright 2020 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import SettingController from "./SettingController"; +import { SettingLevel } from "../SettingLevel"; + +/** + * Allows for multiple controllers to affect a setting. The first controller + * provided to this class which overrides the setting value will affect + * the value - other controllers are not called. Change notification handlers + * are proxied through to all controllers. + * + * Similarly, the first controller which indicates that a setting is disabled + * will be used - other controllers will not be considered. + */ +export class OrderedMultiController extends SettingController { + constructor(public readonly controllers: SettingController[]) { + super(); + } + + public getValueOverride(level: SettingLevel, roomId: string, calculatedValue: any, calculatedAtLevel: SettingLevel): any { + for (const controller of this.controllers) { + const override = controller.getValueOverride(level, roomId, calculatedValue, calculatedAtLevel); + if (override !== undefined && override !== null) return override; + } + return null; // no override + } + + public onChange(level: SettingLevel, roomId: string, newValue: any) { + for (const controller of this.controllers) { + controller.onChange(level, roomId, newValue); + } + } + + public get settingDisabled(): boolean { + for (const controller of this.controllers) { + if (controller.settingDisabled) return true; + } + return false; + } +}