Merge pull request #4605 from matrix-org/t3chguy/e2eedefault
Add .well-known option to control default e2ee behaviour
This commit is contained in:
@@ -22,13 +22,13 @@ import {
|
||||
import {
|
||||
hideToast as hideSetupEncryptionToast,
|
||||
Kind as SetupKind,
|
||||
Kind,
|
||||
showToast as showSetupEncryptionToast
|
||||
} from "./toasts/SetupEncryptionToast";
|
||||
import {
|
||||
hideToast as hideUnverifiedSessionsToast,
|
||||
showToast as showUnverifiedSessionsToast
|
||||
} from "./toasts/UnverifiedSessionToast";
|
||||
import {privateShouldBeEncrypted} from "./createRoom";
|
||||
|
||||
const KEY_BACKUP_POLL_INTERVAL = 5 * 60 * 1000;
|
||||
|
||||
@@ -169,6 +169,14 @@ export default class DeviceListener {
|
||||
return this.keyBackupInfo;
|
||||
}
|
||||
|
||||
private shouldShowSetupEncryptionToast() {
|
||||
// In a default configuration, show the toasts. If the well-known config causes e2ee default to be false
|
||||
// then do not show the toasts until user is in at least one encrypted room.
|
||||
if (privateShouldBeEncrypted()) return true;
|
||||
const cli = MatrixClientPeg.get();
|
||||
return cli && cli.getRooms().some(r => cli.isRoomEncrypted(r.roomId));
|
||||
}
|
||||
|
||||
async _recheck() {
|
||||
const cli = MatrixClientPeg.get();
|
||||
|
||||
@@ -184,7 +192,7 @@ export default class DeviceListener {
|
||||
|
||||
if (this.dismissedThisDeviceToast || crossSigningReady) {
|
||||
hideSetupEncryptionToast();
|
||||
} else {
|
||||
} else if (this.shouldShowSetupEncryptionToast()) {
|
||||
// make sure our keys are finished downloading
|
||||
await cli.downloadKeys([cli.getUserId()]);
|
||||
// cross signing isn't enabled - nag to enable it
|
||||
@@ -196,10 +204,10 @@ export default class DeviceListener {
|
||||
const backupInfo = await this._getKeyBackupInfo();
|
||||
if (backupInfo) {
|
||||
// No cross-signing on account but key backup available (upgrade encryption)
|
||||
showSetupEncryptionToast(Kind.UPGRADE_ENCRYPTION);
|
||||
showSetupEncryptionToast(SetupKind.UPGRADE_ENCRYPTION);
|
||||
} else {
|
||||
// No cross-signing or key backup on account (set up encryption)
|
||||
showSetupEncryptionToast(Kind.SET_UP_ENCRYPTION);
|
||||
showSetupEncryptionToast(SetupKind.SET_UP_ENCRYPTION);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -622,7 +622,7 @@ async function startMatrixClient(startSyncing=true) {
|
||||
}
|
||||
|
||||
// Now that we have a MatrixClientPeg, update the Jitsi info
|
||||
await Jitsi.getInstance().update();
|
||||
await Jitsi.getInstance().start();
|
||||
|
||||
// dispatch that we finished starting up to wire up any other bits
|
||||
// of the matrix client that cannot be set prior to starting up.
|
||||
|
||||
@@ -49,6 +49,7 @@ export interface IOpts {
|
||||
initialSyncLimit?: number;
|
||||
pendingEventOrdering?: "detached" | "chronological";
|
||||
lazyLoadMembers?: boolean;
|
||||
clientWellKnownPollPeriod?: number;
|
||||
}
|
||||
|
||||
export interface IMatrixClientPeg {
|
||||
@@ -209,6 +210,7 @@ class _MatrixClientPeg implements IMatrixClientPeg {
|
||||
// the react sdk doesn't work without this, so don't allow
|
||||
opts.pendingEventOrdering = "detached";
|
||||
opts.lazyLoadMembers = true;
|
||||
opts.clientWellKnownPollPeriod = 2 * 60 * 60; // 2 hours
|
||||
|
||||
// Connect the matrix client to the dispatcher and setting handlers
|
||||
MatrixActionCreators.start(this.matrixClient);
|
||||
|
||||
@@ -24,6 +24,7 @@ import withValidation from '../elements/Validation';
|
||||
import { _t } from '../../../languageHandler';
|
||||
import {MatrixClientPeg} from '../../../MatrixClientPeg';
|
||||
import {Key} from "../../../Keyboard";
|
||||
import {privateShouldBeEncrypted} from "../../../createRoom";
|
||||
|
||||
export default createReactClass({
|
||||
displayName: 'CreateRoomDialog',
|
||||
@@ -36,7 +37,7 @@ export default createReactClass({
|
||||
const config = SdkConfig.get();
|
||||
return {
|
||||
isPublic: this.props.defaultPublic || false,
|
||||
isEncrypted: true,
|
||||
isEncrypted: privateShouldBeEncrypted(),
|
||||
name: "",
|
||||
topic: "",
|
||||
alias: "",
|
||||
@@ -193,6 +194,13 @@ export default createReactClass({
|
||||
|
||||
let e2eeSection;
|
||||
if (!this.state.isPublic) {
|
||||
let microcopy;
|
||||
if (privateShouldBeEncrypted()) {
|
||||
microcopy = _t("You can’t disable this later. Bridges & most bots won’t work yet.");
|
||||
} else {
|
||||
microcopy = _t("Your server admin has disabled end-to-end encryption by default " +
|
||||
"in private rooms & Direct Messages.");
|
||||
}
|
||||
e2eeSection = <React.Fragment>
|
||||
<LabelledToggleSwitch
|
||||
label={ _t("Enable end-to-end encryption")}
|
||||
@@ -200,7 +208,7 @@ export default createReactClass({
|
||||
value={this.state.isEncrypted}
|
||||
className='mx_CreateRoomDialog_e2eSwitch' // for end-to-end tests
|
||||
/>
|
||||
<p>{ _t("You can’t disable this later. Bridges & most bots won’t work yet.") }</p>
|
||||
<p>{ microcopy }</p>
|
||||
</React.Fragment>;
|
||||
}
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ import dis from "../../../dispatcher/dispatcher";
|
||||
import IdentityAuthClient from "../../../IdentityAuthClient";
|
||||
import Modal from "../../../Modal";
|
||||
import {humanizeTime} from "../../../utils/humanize";
|
||||
import createRoom, {canEncryptToAllUsers} from "../../../createRoom";
|
||||
import createRoom, {canEncryptToAllUsers, privateShouldBeEncrypted} from "../../../createRoom";
|
||||
import {inviteMultipleToRoom} from "../../../RoomInvite";
|
||||
import {Key} from "../../../Keyboard";
|
||||
import {Action} from "../../../dispatcher/actions";
|
||||
@@ -575,14 +575,16 @@ export default class InviteDialog extends React.PureComponent {
|
||||
|
||||
const createRoomOptions = {inlineErrors: true};
|
||||
|
||||
// Check whether all users have uploaded device keys before.
|
||||
// If so, enable encryption in the new room.
|
||||
const has3PidMembers = targets.some(t => t instanceof ThreepidMember);
|
||||
if (!has3PidMembers) {
|
||||
const client = MatrixClientPeg.get();
|
||||
const allHaveDeviceKeys = await canEncryptToAllUsers(client, targetIds);
|
||||
if (allHaveDeviceKeys) {
|
||||
createRoomOptions.encryption = true;
|
||||
if (privateShouldBeEncrypted()) {
|
||||
// Check whether all users have uploaded device keys before.
|
||||
// If so, enable encryption in the new room.
|
||||
const has3PidMembers = targets.some(t => t instanceof ThreepidMember);
|
||||
if (!has3PidMembers) {
|
||||
const client = MatrixClientPeg.get();
|
||||
const allHaveDeviceKeys = await canEncryptToAllUsers(client, targetIds);
|
||||
if (allHaveDeviceKeys) {
|
||||
createRoomOptions.encryption = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ import dis from '../../../dispatcher/dispatcher';
|
||||
import Modal from '../../../Modal';
|
||||
import * as sdk from '../../../index';
|
||||
import { _t } from '../../../languageHandler';
|
||||
import createRoom from '../../../createRoom';
|
||||
import createRoom, {privateShouldBeEncrypted} from '../../../createRoom';
|
||||
import DMRoomMap from '../../../utils/DMRoomMap';
|
||||
import AccessibleButton from '../elements/AccessibleButton';
|
||||
import SdkConfig from '../../../SdkConfig';
|
||||
@@ -108,15 +108,17 @@ async function openDMForUser(matrixClient, userId) {
|
||||
dmUserId: userId,
|
||||
};
|
||||
|
||||
// Check whether all users have uploaded device keys before.
|
||||
// If so, enable encryption in the new room.
|
||||
const usersToDevicesMap = await matrixClient.downloadKeys([userId]);
|
||||
const allHaveDeviceKeys = Object.values(usersToDevicesMap).every(devices => {
|
||||
// `devices` is an object of the form { deviceId: deviceInfo, ... }.
|
||||
return Object.keys(devices).length > 0;
|
||||
});
|
||||
if (allHaveDeviceKeys) {
|
||||
createRoomOptions.encryption = true;
|
||||
if (privateShouldBeEncrypted()) {
|
||||
// Check whether all users have uploaded device keys before.
|
||||
// If so, enable encryption in the new room.
|
||||
const usersToDevicesMap = await matrixClient.downloadKeys([userId]);
|
||||
const allHaveDeviceKeys = Object.values(usersToDevicesMap).every(devices => {
|
||||
// `devices` is an object of the form { deviceId: deviceInfo, ... }.
|
||||
return Object.keys(devices).length > 0;
|
||||
});
|
||||
if (allHaveDeviceKeys) {
|
||||
createRoomOptions.encryption = true;
|
||||
}
|
||||
}
|
||||
|
||||
createRoom(createRoomOptions);
|
||||
|
||||
@@ -26,6 +26,7 @@ import Modal from "../../../../../Modal";
|
||||
import * as sdk from "../../../../..";
|
||||
import {sleep} from "../../../../../utils/promise";
|
||||
import dis from "../../../../../dispatcher/dispatcher";
|
||||
import {privateShouldBeEncrypted} from "../../../../../createRoom";
|
||||
|
||||
export class IgnoredUser extends React.Component {
|
||||
static propTypes = {
|
||||
@@ -317,8 +318,17 @@ export default class SecurityUserSettingsTab extends React.Component {
|
||||
|
||||
const E2eAdvancedPanel = sdk.getComponent('views.settings.E2eAdvancedPanel');
|
||||
|
||||
let warning;
|
||||
if (!privateShouldBeEncrypted()) {
|
||||
warning = <div className="mx_SecurityUserSettingsTab_warning">
|
||||
{ _t("Your server admin has disabled end-to-end encryption by default " +
|
||||
"in private rooms & Direct Messages.") }
|
||||
</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="mx_SettingsTab mx_SecurityUserSettingsTab">
|
||||
{warning}
|
||||
<div className="mx_SettingsTab_heading">{_t("Security & Privacy")}</div>
|
||||
<div className="mx_SettingsTab_section">
|
||||
<span className="mx_SettingsTab_subheading">{_t("Where you’re logged in")}</span>
|
||||
|
||||
@@ -24,6 +24,8 @@ import * as Rooms from "./Rooms";
|
||||
import DMRoomMap from "./utils/DMRoomMap";
|
||||
import {getAddressType} from "./UserAddress";
|
||||
|
||||
const E2EE_WK_KEY = "im.vector.riot.e2ee";
|
||||
|
||||
/**
|
||||
* Create a new room, and switch to it.
|
||||
*
|
||||
@@ -225,9 +227,22 @@ export async function ensureDMExists(client, userId) {
|
||||
if (existingDMRoom) {
|
||||
roomId = existingDMRoom.roomId;
|
||||
} else {
|
||||
const encryption = canEncryptToAllUsers(client, [userId]);
|
||||
let encryption;
|
||||
if (privateShouldBeEncrypted()) {
|
||||
encryption = canEncryptToAllUsers(client, [userId]);
|
||||
}
|
||||
roomId = await createRoom({encryption, dmUserId: userId, spinner: false, andView: false});
|
||||
await _waitForMember(client, roomId, userId);
|
||||
}
|
||||
return roomId;
|
||||
}
|
||||
|
||||
export function privateShouldBeEncrypted() {
|
||||
const clientWellKnown = MatrixClientPeg.get().getClientWellKnown();
|
||||
if (clientWellKnown && clientWellKnown[E2EE_WK_KEY]) {
|
||||
const defaultDisabled = clientWellKnown[E2EE_WK_KEY]["default"] === false;
|
||||
return !defaultDisabled;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -871,6 +871,7 @@
|
||||
"Key backup": "Key backup",
|
||||
"Message search": "Message search",
|
||||
"Cross-signing": "Cross-signing",
|
||||
"Your server admin has disabled end-to-end encryption by default in private rooms & Direct Messages.": "Your server admin has disabled end-to-end encryption by default in private rooms & Direct Messages.",
|
||||
"Security & Privacy": "Security & Privacy",
|
||||
"Where you’re logged in": "Where you’re logged in",
|
||||
"Manage the names of and sign out of your sessions below or <a>verify them in your User Profile</a>.": "Manage the names of and sign out of your sessions below or <a>verify them in your User Profile</a>.",
|
||||
@@ -1559,8 +1560,8 @@
|
||||
"Please enter a name for the room": "Please enter a name for the room",
|
||||
"Set a room address to easily share your room with other people.": "Set a room address to easily share your room with other people.",
|
||||
"This room is private, and can only be joined by invitation.": "This room is private, and can only be joined by invitation.",
|
||||
"Enable end-to-end encryption": "Enable end-to-end encryption",
|
||||
"You can’t disable this later. Bridges & most bots won’t work yet.": "You can’t disable this later. Bridges & most bots won’t work yet.",
|
||||
"Enable end-to-end encryption": "Enable end-to-end encryption",
|
||||
"Create a public room": "Create a public room",
|
||||
"Create a private room": "Create a private room",
|
||||
"Name": "Name",
|
||||
|
||||
@@ -21,10 +21,8 @@ import {IntegrationManagerInstance, KIND_ACCOUNT, KIND_CONFIG, KIND_HOMESERVER}
|
||||
import type {MatrixClient, MatrixEvent, Room} from "matrix-js-sdk";
|
||||
import WidgetUtils from "../utils/WidgetUtils";
|
||||
import {MatrixClientPeg} from "../MatrixClientPeg";
|
||||
import {AutoDiscovery} from "matrix-js-sdk";
|
||||
import SettingsStore from "../settings/SettingsStore";
|
||||
|
||||
const HS_MANAGERS_REFRESH_INTERVAL = 8 * 60 * 60 * 1000; // 8 hours
|
||||
const KIND_PREFERENCE = [
|
||||
// Ordered: first is most preferred, last is least preferred.
|
||||
KIND_ACCOUNT,
|
||||
@@ -44,7 +42,6 @@ export class IntegrationManagers {
|
||||
|
||||
_managers: IntegrationManagerInstance[] = [];
|
||||
_client: MatrixClient;
|
||||
_wellknownRefreshTimerId: number = null;
|
||||
_primaryManager: IntegrationManagerInstance;
|
||||
|
||||
constructor() {
|
||||
@@ -55,20 +52,19 @@ export class IntegrationManagers {
|
||||
this.stopWatching();
|
||||
this._client = MatrixClientPeg.get();
|
||||
this._client.on("accountData", this._onAccountData);
|
||||
this._client.on("WellKnown.client", this._setupHomeserverManagers);
|
||||
this._compileManagers();
|
||||
setInterval(() => this._setupHomeserverManagers(), HS_MANAGERS_REFRESH_INTERVAL);
|
||||
}
|
||||
|
||||
stopWatching(): void {
|
||||
if (!this._client) return;
|
||||
this._client.removeListener("accountData", this._onAccountData);
|
||||
if (this._wellknownRefreshTimerId !== null) clearInterval(this._wellknownRefreshTimerId);
|
||||
this._client.removeListener("WellKnown.client", this._setupHomeserverManagers);
|
||||
}
|
||||
|
||||
_compileManagers() {
|
||||
this._managers = [];
|
||||
this._setupConfiguredManager();
|
||||
this._setupHomeserverManagers();
|
||||
this._setupAccountManagers();
|
||||
}
|
||||
|
||||
@@ -82,39 +78,31 @@ export class IntegrationManagers {
|
||||
}
|
||||
}
|
||||
|
||||
async _setupHomeserverManagers() {
|
||||
if (!MatrixClientPeg.get()) return;
|
||||
try {
|
||||
console.log("Updating homeserver-configured integration managers...");
|
||||
const homeserverDomain = MatrixClientPeg.getHomeserverName();
|
||||
const discoveryResponse = await AutoDiscovery.getRawClientConfig(homeserverDomain);
|
||||
if (discoveryResponse && discoveryResponse['m.integrations']) {
|
||||
let managers = discoveryResponse['m.integrations']['managers'];
|
||||
if (!Array.isArray(managers)) managers = []; // make it an array so we can wipe the HS managers
|
||||
async _setupHomeserverManagers(discoveryResponse) {
|
||||
console.log("Updating homeserver-configured integration managers...");
|
||||
if (discoveryResponse && discoveryResponse['m.integrations']) {
|
||||
let managers = discoveryResponse['m.integrations']['managers'];
|
||||
if (!Array.isArray(managers)) managers = []; // make it an array so we can wipe the HS managers
|
||||
|
||||
console.log(`Homeserver has ${managers.length} integration managers`);
|
||||
console.log(`Homeserver has ${managers.length} integration managers`);
|
||||
|
||||
// Clear out any known managers for the homeserver
|
||||
// TODO: Log out of the scalar clients
|
||||
this._managers = this._managers.filter(m => m.kind !== KIND_HOMESERVER);
|
||||
// Clear out any known managers for the homeserver
|
||||
// TODO: Log out of the scalar clients
|
||||
this._managers = this._managers.filter(m => m.kind !== KIND_HOMESERVER);
|
||||
|
||||
// Now add all the managers the homeserver wants us to have
|
||||
for (const hsManager of managers) {
|
||||
if (!hsManager["api_url"]) continue;
|
||||
this._managers.push(new IntegrationManagerInstance(
|
||||
KIND_HOMESERVER,
|
||||
hsManager["api_url"],
|
||||
hsManager["ui_url"], // optional
|
||||
));
|
||||
}
|
||||
|
||||
this._primaryManager = null; // reset primary
|
||||
} else {
|
||||
console.log("Homeserver has no integration managers");
|
||||
// Now add all the managers the homeserver wants us to have
|
||||
for (const hsManager of managers) {
|
||||
if (!hsManager["api_url"]) continue;
|
||||
this._managers.push(new IntegrationManagerInstance(
|
||||
KIND_HOMESERVER,
|
||||
hsManager["api_url"],
|
||||
hsManager["ui_url"], // optional
|
||||
));
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
// Errors during discovery are non-fatal
|
||||
|
||||
this._primaryManager = null; // reset primary
|
||||
} else {
|
||||
console.log("Homeserver has no integration managers");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,10 +16,8 @@ limitations under the License.
|
||||
|
||||
import SdkConfig from "../SdkConfig";
|
||||
import {MatrixClientPeg} from "../MatrixClientPeg";
|
||||
import {AutoDiscovery} from "matrix-js-sdk/src/autodiscovery";
|
||||
|
||||
const JITSI_WK_PROPERTY = "im.vector.riot.jitsi";
|
||||
const JITSI_WK_CHECK_INTERVAL = 2 * 60 * 60 * 1000; // 2 hours, arbitrarily selected
|
||||
|
||||
export interface JitsiWidgetData {
|
||||
conferenceId: string;
|
||||
@@ -36,39 +34,27 @@ export class Jitsi {
|
||||
return this.domain || 'jitsi.riot.im';
|
||||
}
|
||||
|
||||
constructor() {
|
||||
// We rely on the first call to be an .update() instead of doing one here. Doing one
|
||||
// here could result in duplicate calls to the homeserver.
|
||||
|
||||
// Start a timer to update the server info regularly
|
||||
setInterval(() => this.update(), JITSI_WK_CHECK_INTERVAL);
|
||||
public start() {
|
||||
const cli = MatrixClientPeg.get();
|
||||
cli.on("WellKnown.client", this.update);
|
||||
// call update initially in case we missed the first WellKnown.client event and for if no well-known present
|
||||
this.update(cli.getClientWellKnown());
|
||||
}
|
||||
|
||||
public async update(): Promise<any> {
|
||||
private update = async (discoveryResponse): Promise<any> => {
|
||||
// Start with a default of the config's domain
|
||||
let domain = (SdkConfig.get()['jitsi'] || {})['preferredDomain'] || 'jitsi.riot.im';
|
||||
|
||||
// Now request the .well-known config to see if it changed
|
||||
if (MatrixClientPeg.get()) {
|
||||
try {
|
||||
console.log("Attempting to get Jitsi conference information from homeserver");
|
||||
|
||||
const homeserverDomain = MatrixClientPeg.getHomeserverName();
|
||||
const discoveryResponse = await AutoDiscovery.getRawClientConfig(homeserverDomain);
|
||||
if (discoveryResponse && discoveryResponse[JITSI_WK_PROPERTY]) {
|
||||
const wkPreferredDomain = discoveryResponse[JITSI_WK_PROPERTY]['preferredDomain'];
|
||||
if (wkPreferredDomain) domain = wkPreferredDomain;
|
||||
}
|
||||
} catch (e) {
|
||||
// These are non-fatal errors
|
||||
console.error(e);
|
||||
}
|
||||
console.log("Attempting to get Jitsi conference information from homeserver");
|
||||
if (discoveryResponse && discoveryResponse[JITSI_WK_PROPERTY]) {
|
||||
const wkPreferredDomain = discoveryResponse[JITSI_WK_PROPERTY]['preferredDomain'];
|
||||
if (wkPreferredDomain) domain = wkPreferredDomain;
|
||||
}
|
||||
|
||||
// Put the result into memory for us to use later
|
||||
this.domain = domain;
|
||||
console.log("Jitsi conference domain:", this.preferredDomain);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Parses the given URL into the data needed for a Jitsi widget, if the widget
|
||||
|
||||
Reference in New Issue
Block a user