Remove feature_cross_signing

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
This commit is contained in:
Michael Telatynski 2020-05-27 10:28:25 +01:00
parent 515304d32e
commit 2b432b0d82
25 changed files with 106 additions and 1799 deletions

View file

@ -15,7 +15,6 @@ limitations under the License.
*/ */
import {MatrixClientPeg} from './MatrixClientPeg'; import {MatrixClientPeg} from './MatrixClientPeg';
import SettingsStore from './settings/SettingsStore';
import { import {
hideToast as hideBulkUnverifiedSessionsToast, hideToast as hideBulkUnverifiedSessionsToast,
showToast as showBulkUnverifiedSessionsToast showToast as showBulkUnverifiedSessionsToast
@ -173,10 +172,7 @@ export default class DeviceListener {
async _recheck() { async _recheck() {
const cli = MatrixClientPeg.get(); const cli = MatrixClientPeg.get();
if ( if (!await cli.doesServerSupportUnstableFeature("org.matrix.e2e_cross_signing")) return;
!SettingsStore.getValue("feature_cross_signing") ||
!await cli.doesServerSupportUnstableFeature("org.matrix.e2e_cross_signing")
) return;
if (!cli.isCryptoEnabled()) return; if (!cli.isCryptoEnabled()) return;
// don't recheck until the initial sync is complete: lots of account data events will fire // don't recheck until the initial sync is complete: lots of account data events will fire

View file

@ -1,158 +0,0 @@
/*
Copyright 2017 Vector Creations Ltd
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 * as sdk from './index';
import Modal from './Modal';
import SettingsStore from './settings/SettingsStore';
// TODO: We can remove this once cross-signing is the only way.
// https://github.com/vector-im/riot-web/issues/11908
export default class KeyRequestHandler {
constructor(matrixClient) {
this._matrixClient = matrixClient;
// the user/device for which we currently have a dialog open
this._currentUser = null;
this._currentDevice = null;
// userId -> deviceId -> [keyRequest]
this._pendingKeyRequests = Object.create(null);
}
handleKeyRequest(keyRequest) {
// Ignore own device key requests if cross-signing lab enabled
if (SettingsStore.getValue("feature_cross_signing")) {
return;
}
const userId = keyRequest.userId;
const deviceId = keyRequest.deviceId;
const requestId = keyRequest.requestId;
if (!this._pendingKeyRequests[userId]) {
this._pendingKeyRequests[userId] = Object.create(null);
}
if (!this._pendingKeyRequests[userId][deviceId]) {
this._pendingKeyRequests[userId][deviceId] = [];
}
// check if we already have this request
const requests = this._pendingKeyRequests[userId][deviceId];
if (requests.find((r) => r.requestId === requestId)) {
console.log("Already have this key request, ignoring");
return;
}
requests.push(keyRequest);
if (this._currentUser) {
// ignore for now
console.log("Key request, but we already have a dialog open");
return;
}
this._processNextRequest();
}
handleKeyRequestCancellation(cancellation) {
// Ignore own device key requests if cross-signing lab enabled
if (SettingsStore.getValue("feature_cross_signing")) {
return;
}
// see if we can find the request in the queue
const userId = cancellation.userId;
const deviceId = cancellation.deviceId;
const requestId = cancellation.requestId;
if (userId === this._currentUser && deviceId === this._currentDevice) {
console.log(
"room key request cancellation for the user we currently have a"
+ " dialog open for",
);
// TODO: update the dialog. For now, we just ignore the
// cancellation.
return;
}
if (!this._pendingKeyRequests[userId]) {
return;
}
const requests = this._pendingKeyRequests[userId][deviceId];
if (!requests) {
return;
}
const idx = requests.findIndex((r) => r.requestId === requestId);
if (idx < 0) {
return;
}
console.log("Forgetting room key request");
requests.splice(idx, 1);
if (requests.length === 0) {
delete this._pendingKeyRequests[userId][deviceId];
if (Object.keys(this._pendingKeyRequests[userId]).length === 0) {
delete this._pendingKeyRequests[userId];
}
}
}
_processNextRequest() {
const userId = Object.keys(this._pendingKeyRequests)[0];
if (!userId) {
return;
}
const deviceId = Object.keys(this._pendingKeyRequests[userId])[0];
if (!deviceId) {
return;
}
console.log(`Starting KeyShareDialog for ${userId}:${deviceId}`);
const finished = (r) => {
this._currentUser = null;
this._currentDevice = null;
if (!this._pendingKeyRequests[userId] || !this._pendingKeyRequests[userId][deviceId]) {
// request was removed in the time the dialog was displayed
this._processNextRequest();
return;
}
if (r) {
for (const req of this._pendingKeyRequests[userId][deviceId]) {
req.share();
}
}
delete this._pendingKeyRequests[userId][deviceId];
if (Object.keys(this._pendingKeyRequests[userId]).length === 0) {
delete this._pendingKeyRequests[userId];
}
this._processNextRequest();
};
const KeyShareDialog = sdk.getComponent("dialogs.KeyShareDialog");
Modal.appendTrackedDialog('Key Share', 'Process Next Request', KeyShareDialog, {
matrixClient: this._matrixClient,
userId: userId,
deviceId: deviceId,
onFinished: finished,
});
this._currentUser = userId;
this._currentDevice = deviceId;
}
}

View file

@ -22,7 +22,6 @@ import {MatrixClientPeg} from '../../../../MatrixClientPeg';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import {_t, _td} from '../../../../languageHandler'; import {_t, _td} from '../../../../languageHandler';
import { accessSecretStorage } from '../../../../CrossSigningManager'; import { accessSecretStorage } from '../../../../CrossSigningManager';
import SettingsStore from '../../../../settings/SettingsStore';
import AccessibleButton from "../../../../components/views/elements/AccessibleButton"; import AccessibleButton from "../../../../components/views/elements/AccessibleButton";
import {copyNode} from "../../../../utils/strings"; import {copyNode} from "../../../../utils/strings";
import PassphraseField from "../../../../components/views/auth/PassphraseField"; import PassphraseField from "../../../../components/views/auth/PassphraseField";
@ -67,10 +66,7 @@ export default class CreateKeyBackupDialog extends React.PureComponent {
async componentDidMount() { async componentDidMount() {
const cli = MatrixClientPeg.get(); const cli = MatrixClientPeg.get();
const secureSecretStorage = ( const secureSecretStorage = await cli.doesServerSupportUnstableFeature("org.matrix.e2e_cross_signing");
SettingsStore.getValue("feature_cross_signing") &&
await cli.doesServerSupportUnstableFeature("org.matrix.e2e_cross_signing")
);
this.setState({ secureSecretStorage }); this.setState({ secureSecretStorage });
// If we're using secret storage, skip ahead to the backing up step, as // If we're using secret storage, skip ahead to the backing up step, as

View file

@ -49,7 +49,6 @@ import PageTypes from '../../PageTypes';
import { getHomePageUrl } from '../../utils/pages'; import { getHomePageUrl } from '../../utils/pages';
import createRoom from "../../createRoom"; import createRoom from "../../createRoom";
import KeyRequestHandler from '../../KeyRequestHandler';
import { _t, getCurrentLanguage } from '../../languageHandler'; import { _t, getCurrentLanguage } from '../../languageHandler';
import SettingsStore, { SettingLevel } from "../../settings/SettingsStore"; import SettingsStore, { SettingLevel } from "../../settings/SettingsStore";
import ThemeController from "../../settings/controllers/ThemeController"; import ThemeController from "../../settings/controllers/ThemeController";
@ -1471,16 +1470,6 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
cli.on("Session.logged_out", () => dft.stop()); cli.on("Session.logged_out", () => dft.stop());
cli.on("Event.decrypted", (e, err) => dft.eventDecrypted(e, err)); cli.on("Event.decrypted", (e, err) => dft.eventDecrypted(e, err));
// TODO: We can remove this once cross-signing is the only way.
// https://github.com/vector-im/riot-web/issues/11908
const krh = new KeyRequestHandler(cli);
cli.on("crypto.roomKeyRequest", (req) => {
krh.handleKeyRequest(req);
});
cli.on("crypto.roomKeyRequestCancellation", (req) => {
krh.handleKeyRequestCancellation(req);
});
cli.on("Room", (room) => { cli.on("Room", (room) => {
if (MatrixClientPeg.get().isCryptoEnabled()) { if (MatrixClientPeg.get().isCryptoEnabled()) {
const blacklistEnabled = SettingsStore.getValueAt( const blacklistEnabled = SettingsStore.getValueAt(
@ -1551,13 +1540,6 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
}); });
cli.on("crypto.verification.request", request => { cli.on("crypto.verification.request", request => {
const isFlagOn = SettingsStore.getValue("feature_cross_signing");
if (!isFlagOn && !request.channel.deviceId) {
request.cancel({code: "m.invalid_message", reason: "This client has cross-signing disabled"});
return;
}
if (request.verifier) { if (request.verifier) {
const IncomingSasDialog = sdk.getComponent("views.dialogs.IncomingSasDialog"); const IncomingSasDialog = sdk.getComponent("views.dialogs.IncomingSasDialog");
Modal.createTrackedDialog('Incoming Verification', '', IncomingSasDialog, { Modal.createTrackedDialog('Incoming Verification', '', IncomingSasDialog, {
@ -1600,9 +1582,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
// be aware of will be signalled through the room shield // be aware of will be signalled through the room shield
// changing colour. More advanced behaviour will come once // changing colour. More advanced behaviour will come once
// we implement more settings. // we implement more settings.
cli.setGlobalErrorOnUnknownDevices( cli.setGlobalErrorOnUnknownDevices(false);
!SettingsStore.getValue("feature_cross_signing"),
);
} }
} }
@ -1956,18 +1936,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
return setLoggedInPromise; return setLoggedInPromise;
} }
// Test for the master cross-signing key in SSSS as a quick proxy for if (await cli.doesServerSupportUnstableFeature("org.matrix.e2e_cross_signing")) {
// whether cross-signing has been set up on the account.
const masterKeyInStorage = !!cli.getAccountData("m.cross_signing.master");
if (masterKeyInStorage) {
// Auto-enable cross-signing for the new session when key found in
// secret storage.
SettingsStore.setValue("feature_cross_signing", null, SettingLevel.DEVICE, true);
this.setStateForNewView({ view: Views.COMPLETE_SECURITY });
} else if (
SettingsStore.getValue("feature_cross_signing") &&
await cli.doesServerSupportUnstableFeature("org.matrix.e2e_cross_signing")
) {
// This will only work if the feature is set to 'enable' in the config, // This will only work if the feature is set to 'enable' in the config,
// since it's too early in the lifecycle for users to have turned the // since it's too early in the lifecycle for users to have turned the
// labs flag on. // labs flag on.

View file

@ -189,16 +189,45 @@ export default class RightPanel extends React.Component {
} }
} }
onCloseRoomMemberInfo = () => {
// XXX: There are three different ways of 'closing' this panel depending on what state
// things are in... this knows far more than it should do about the state of the rest
// of the app and is generally a bit silly.
if (this.props.user) {
// If we have a user prop then we're displaying a user from the 'user' page type
// in LoggedInView, so need to change the page type to close the panel (we switch
// to the home page which is not obviously the correct thing to do, but I'm not sure
// anything else is - we could hide the close button altogether?)
dis.dispatch({
action: "view_home_page",
});
} else {
// Otherwise we have got our user from RoomViewStore which means we're being shown
// within a room, so go back to the member panel if we were in the encryption panel,
// or the member list if we were in the member panel... phew.
dis.dispatch({
action: Action.ViewUser,
member: this.state.phase === RIGHT_PANEL_PHASES.EncryptionPanel ?
this.state.member : null,
});
}
};
onCloseGroupMemberInfo = () => {
dis.dispatch({
action: Action.ViewUser,
member: null,
});
};
render() { render() {
const MemberList = sdk.getComponent('rooms.MemberList'); const MemberList = sdk.getComponent('rooms.MemberList');
const MemberInfo = sdk.getComponent('rooms.MemberInfo');
const UserInfo = sdk.getComponent('right_panel.UserInfo'); const UserInfo = sdk.getComponent('right_panel.UserInfo');
const ThirdPartyMemberInfo = sdk.getComponent('rooms.ThirdPartyMemberInfo'); const ThirdPartyMemberInfo = sdk.getComponent('rooms.ThirdPartyMemberInfo');
const NotificationPanel = sdk.getComponent('structures.NotificationPanel'); const NotificationPanel = sdk.getComponent('structures.NotificationPanel');
const FilePanel = sdk.getComponent('structures.FilePanel'); const FilePanel = sdk.getComponent('structures.FilePanel');
const GroupMemberList = sdk.getComponent('groups.GroupMemberList'); const GroupMemberList = sdk.getComponent('groups.GroupMemberList');
const GroupMemberInfo = sdk.getComponent('groups.GroupMemberInfo');
const GroupRoomList = sdk.getComponent('groups.GroupRoomList'); const GroupRoomList = sdk.getComponent('groups.GroupRoomList');
const GroupRoomInfo = sdk.getComponent('groups.GroupRoomInfo'); const GroupRoomInfo = sdk.getComponent('groups.GroupRoomInfo');
@ -220,71 +249,25 @@ export default class RightPanel extends React.Component {
break; break;
case RIGHT_PANEL_PHASES.RoomMemberInfo: case RIGHT_PANEL_PHASES.RoomMemberInfo:
case RIGHT_PANEL_PHASES.EncryptionPanel: case RIGHT_PANEL_PHASES.EncryptionPanel:
if (SettingsStore.getValue("feature_cross_signing")) { panel = <UserInfo
const onClose = () => { user={this.state.member}
// XXX: There are three different ways of 'closing' this panel depending on what state roomId={this.props.roomId}
// things are in... this knows far more than it should do about the state of the rest key={this.props.roomId || this.state.member.userId}
// of the app and is generally a bit silly. onClose={this.onCloseRoomMemberInfo}
if (this.props.user) { phase={this.state.phase}
// If we have a user prop then we're displaying a user from the 'user' page type verificationRequest={this.state.verificationRequest}
// in LoggedInView, so need to change the page type to close the panel (we switch verificationRequestPromise={this.state.verificationRequestPromise}
// to the home page which is not obviously the correct thing to do, but I'm not sure />;
// anything else is - we could hide the close button altogether?)
dis.dispatch({
action: "view_home_page",
});
} else {
// Otherwise we have got our user from RoomViewStore which means we're being shown
// within a room, so go back to the member panel if we were in the encryption panel,
// or the member list if we were in the member panel... phew.
dis.dispatch({
action: Action.ViewUser,
member: this.state.phase === RIGHT_PANEL_PHASES.EncryptionPanel ?
this.state.member : null,
});
}
};
panel = <UserInfo
user={this.state.member}
roomId={this.props.roomId}
key={this.props.roomId || this.state.member.userId}
onClose={onClose}
phase={this.state.phase}
verificationRequest={this.state.verificationRequest}
verificationRequestPromise={this.state.verificationRequestPromise}
/>;
} else {
panel = <MemberInfo
member={this.state.member}
key={this.props.roomId || this.state.member.userId}
/>;
}
break; break;
case RIGHT_PANEL_PHASES.Room3pidMemberInfo: case RIGHT_PANEL_PHASES.Room3pidMemberInfo:
panel = <ThirdPartyMemberInfo event={this.state.event} key={this.props.roomId} />; panel = <ThirdPartyMemberInfo event={this.state.event} key={this.props.roomId} />;
break; break;
case RIGHT_PANEL_PHASES.GroupMemberInfo: case RIGHT_PANEL_PHASES.GroupMemberInfo:
if (SettingsStore.getValue("feature_cross_signing")) { panel = <UserInfo
const onClose = () => { user={this.state.member}
dis.dispatch({ groupId={this.props.groupId}
action: Action.ViewUser, key={this.state.member.userId}
member: null, onClose={this.onCloseGroupMemberInfo} />;
});
};
panel = <UserInfo
user={this.state.member}
groupId={this.props.groupId}
key={this.state.member.userId}
onClose={onClose} />;
} else {
panel = (
<GroupMemberInfo
groupMember={this.state.member}
groupId={this.props.groupId}
key={this.state.member.user_id}
/>
);
}
break; break;
case RIGHT_PANEL_PHASES.GroupRoomInfo: case RIGHT_PANEL_PHASES.GroupRoomInfo:
panel = <GroupRoomInfo panel = <GroupRoomInfo

View file

@ -854,15 +854,6 @@ export default createReactClass({
}); });
return; return;
} }
if (!SettingsStore.getValue("feature_cross_signing")) {
room.hasUnverifiedDevices().then((hasUnverifiedDevices) => {
this.setState({
e2eStatus: hasUnverifiedDevices ? "warning" : "verified",
});
});
debuglog("e2e check is warning/verified only as cross-signing is off");
return;
}
/* At this point, the user has encryption on and cross-signing on */ /* At this point, the user has encryption on and cross-signing on */
this.setState({ this.setState({

View file

@ -24,7 +24,6 @@ import withValidation from '../elements/Validation';
import { _t } from '../../../languageHandler'; import { _t } from '../../../languageHandler';
import {MatrixClientPeg} from '../../../MatrixClientPeg'; import {MatrixClientPeg} from '../../../MatrixClientPeg';
import {Key} from "../../../Keyboard"; import {Key} from "../../../Keyboard";
import SettingsStore from "../../../settings/SettingsStore";
export default createReactClass({ export default createReactClass({
displayName: 'CreateRoomDialog', displayName: 'CreateRoomDialog',
@ -66,7 +65,7 @@ export default createReactClass({
createOpts.creation_content = {'m.federate': false}; createOpts.creation_content = {'m.federate': false};
} }
if (!this.state.isPublic && SettingsStore.getValue("feature_cross_signing")) { if (!this.state.isPublic) {
opts.encryption = this.state.isEncrypted; opts.encryption = this.state.isEncrypted;
} }
@ -193,7 +192,7 @@ export default createReactClass({
} }
let e2eeSection; let e2eeSection;
if (!this.state.isPublic && SettingsStore.getValue("feature_cross_signing")) { if (!this.state.isPublic) {
e2eeSection = <React.Fragment> e2eeSection = <React.Fragment>
<LabelledToggleSwitch <LabelledToggleSwitch
label={ _t("Enable end-to-end encryption")} label={ _t("Enable end-to-end encryption")}

View file

@ -119,7 +119,7 @@ export default class DeviceVerifyDialog extends React.Component {
const client = MatrixClientPeg.get(); const client = MatrixClientPeg.get();
const verifyingOwnDevice = this.props.userId === client.getUserId(); const verifyingOwnDevice = this.props.userId === client.getUserId();
try { try {
if (!verifyingOwnDevice && SettingsStore.getValue("feature_cross_signing")) { if (!verifyingOwnDevice) {
const roomId = await ensureDMExistsAndOpen(this.props.userId); const roomId = await ensureDMExistsAndOpen(this.props.userId);
// throws upon cancellation before having started // throws upon cancellation before having started
const request = await client.requestVerificationDM( const request = await client.requestVerificationDM(
@ -131,7 +131,7 @@ export default class DeviceVerifyDialog extends React.Component {
} else { } else {
this._verifier = request.verifier; this._verifier = request.verifier;
} }
} else if (verifyingOwnDevice && SettingsStore.getValue("feature_cross_signing")) { } else if (verifyingOwnDevice) {
this._request = await client.requestVerification(this.props.userId, [ this._request = await client.requestVerification(this.props.userId, [
verificationMethods.SAS, verificationMethods.SAS,
SHOW_QR_CODE_METHOD, SHOW_QR_CODE_METHOD,

View file

@ -576,16 +576,14 @@ export default class InviteDialog extends React.PureComponent {
const createRoomOptions = {inlineErrors: true}; const createRoomOptions = {inlineErrors: true};
if (SettingsStore.getValue("feature_cross_signing")) { // Check whether all users have uploaded device keys before.
// Check whether all users have uploaded device keys before. // If so, enable encryption in the new room.
// If so, enable encryption in the new room. const has3PidMembers = targets.some(t => t instanceof ThreepidMember);
const has3PidMembers = targets.some(t => t instanceof ThreepidMember); if (!has3PidMembers) {
if (!has3PidMembers) { const client = MatrixClientPeg.get();
const client = MatrixClientPeg.get(); const allHaveDeviceKeys = await canEncryptToAllUsers(client, targetIds);
const allHaveDeviceKeys = await canEncryptToAllUsers(client, targetIds); if (allHaveDeviceKeys) {
if (allHaveDeviceKeys) { createRoomOptions.encryption = true;
createRoomOptions.encryption = true;
}
} }
} }

View file

@ -90,21 +90,7 @@ export default class RestoreKeyBackupDialog extends React.PureComponent {
_onResetRecoveryClick = () => { _onResetRecoveryClick = () => {
this.props.onFinished(false); this.props.onFinished(false);
accessSecretStorage(() => {}, /* forceReset = */ true);
if (SettingsStore.getValue("feature_cross_signing")) {
// If cross-signing is enabled, we reset the SSSS recovery passphrase (and cross-signing keys)
this.props.onFinished(false);
accessSecretStorage(() => {}, /* forceReset = */ true);
} else {
Modal.createTrackedDialogAsync('Key Backup', 'Key Backup',
import('../../../../async-components/views/dialogs/keybackup/CreateKeyBackupDialog'),
{
onFinished: () => {
this._loadBackupStatus();
},
}, null, /* priority = */ false, /* static = */ true,
);
}
} }
_onRecoveryKeyChange = (e) => { _onRecoveryKeyChange = (e) => {

View file

@ -1,208 +0,0 @@
/*
Copyright 2017 Vector Creations Ltd
Copyright 2017 New Vector Ltd
Copyright 2019 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 React from 'react';
import PropTypes from 'prop-types';
import createReactClass from 'create-react-class';
import dis from '../../../dispatcher/dispatcher';
import Modal from '../../../Modal';
import * as sdk from '../../../index';
import { _t } from '../../../languageHandler';
import { GroupMemberType } from '../../../groups';
import GroupStore from '../../../stores/GroupStore';
import AccessibleButton from '../elements/AccessibleButton';
import MatrixClientContext from "../../../contexts/MatrixClientContext";
import AutoHideScrollbar from "../../structures/AutoHideScrollbar";
import {Action} from "../../../dispatcher/actions";
export default createReactClass({
displayName: 'GroupMemberInfo',
statics: {
contextType: MatrixClientContext,
},
propTypes: {
groupId: PropTypes.string,
groupMember: GroupMemberType,
isInvited: PropTypes.bool,
},
getInitialState: function() {
return {
removingUser: false,
isUserPrivilegedInGroup: null,
};
},
componentDidMount: function() {
this._unmounted = false;
this._initGroupStore(this.props.groupId);
},
// TODO: [REACT-WARNING] Replace with appropriate lifecycle event
UNSAFE_componentWillReceiveProps(newProps) {
if (newProps.groupId !== this.props.groupId) {
this._unregisterGroupStore(this.props.groupId);
this._initGroupStore(newProps.groupId);
}
},
componentWillUnmount() {
this._unmounted = true;
this._unregisterGroupStore(this.props.groupId);
},
_initGroupStore(groupId) {
GroupStore.registerListener(groupId, this.onGroupStoreUpdated);
},
_unregisterGroupStore(groupId) {
GroupStore.unregisterListener(this.onGroupStoreUpdated);
},
onGroupStoreUpdated: function() {
if (this._unmounted) return;
this.setState({
isUserInvited: GroupStore.getGroupInvitedMembers(this.props.groupId).some(
(m) => m.userId === this.props.groupMember.userId,
),
isUserPrivilegedInGroup: GroupStore.isUserPrivileged(this.props.groupId),
});
},
_onKick: function() {
const ConfirmUserActionDialog = sdk.getComponent("dialogs.ConfirmUserActionDialog");
Modal.createDialog(ConfirmUserActionDialog, {
matrixClient: this.context,
groupMember: this.props.groupMember,
action: this.state.isUserInvited ? _t('Disinvite') : _t('Remove from community'),
title: this.state.isUserInvited ? _t('Disinvite this user from community?')
: _t('Remove this user from community?'),
danger: true,
onFinished: (proceed) => {
if (!proceed) return;
this.setState({removingUser: true});
this.context.removeUserFromGroup(
this.props.groupId, this.props.groupMember.userId,
).then(() => {
// return to the user list
dis.dispatch({
action: Action.ViewUser,
member: null,
});
}).catch((e) => {
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
Modal.createTrackedDialog('Failed to remove user from group', '', ErrorDialog, {
title: _t('Error'),
description: this.state.isUserInvited ?
_t('Failed to withdraw invitation') :
_t('Failed to remove user from community'),
});
}).finally(() => {
this.setState({removingUser: false});
});
},
});
},
_onCancel: function(e) {
// Go back to the user list
dis.dispatch({
action: Action.ViewUser,
member: null,
});
},
onRoomTileClick(roomId) {
dis.dispatch({
action: 'view_room',
room_id: roomId,
});
},
render: function() {
if (this.state.removingUser) {
const Spinner = sdk.getComponent("elements.Spinner");
return <div className="mx_MemberInfo">
<Spinner />
</div>;
}
let adminTools;
if (this.state.isUserPrivilegedInGroup) {
const kickButton = (
<AccessibleButton className="mx_MemberInfo_field"
onClick={this._onKick}>
{ this.state.isUserInvited ? _t('Disinvite') : _t('Remove from community') }
</AccessibleButton>
);
// No make/revoke admin API yet
/*const opLabel = this.state.isTargetMod ? _t("Revoke Moderator") : _t("Make Moderator");
giveModButton = <AccessibleButton className="mx_MemberInfo_field" onClick={this.onModToggle}>
{giveOpLabel}
</AccessibleButton>;*/
if (kickButton) {
adminTools =
<div className="mx_MemberInfo_adminTools">
<h3>{ _t("Admin Tools") }</h3>
<div className="mx_MemberInfo_buttons">
{ kickButton }
</div>
</div>;
}
}
const avatarUrl = this.props.groupMember.avatarUrl;
let avatarElement;
if (avatarUrl) {
const httpUrl = this.context.mxcUrlToHttp(avatarUrl, 800, 800);
avatarElement = (<div className="mx_MemberInfo_avatar">
<img src={httpUrl} />
</div>);
}
const groupMemberName = (
this.props.groupMember.displayname || this.props.groupMember.userId
);
return (
<div className="mx_MemberInfo" role="tabpanel">
<AutoHideScrollbar>
<AccessibleButton className="mx_MemberInfo_cancel" onClick={this._onCancel}>
<img src={require("../../../../res/img/cancel.svg")} width="18" height="18" className="mx_filterFlipColor" />
</AccessibleButton>
{ avatarElement }
<h2>{ groupMemberName }</h2>
<div className="mx_MemberInfo_profile">
<div className="mx_MemberInfo_profileField">
{ this.props.groupMember.userId }
</div>
</div>
{ adminTools }
</AutoHideScrollbar>
</div>
);
},
});

View file

@ -64,10 +64,6 @@ const _disambiguateDevices = (devices) => {
}; };
export const getE2EStatus = (cli, userId, devices) => { export const getE2EStatus = (cli, userId, devices) => {
if (!SettingsStore.getValue("feature_cross_signing")) {
const hasUnverifiedDevice = devices.some((device) => device.isUnverified());
return hasUnverifiedDevice ? "warning" : "verified";
}
const isMe = userId === cli.getUserId(); const isMe = userId === cli.getUserId();
const userTrust = cli.checkUserTrust(userId); const userTrust = cli.checkUserTrust(userId);
if (!userTrust.isCrossSigningVerified()) { if (!userTrust.isCrossSigningVerified()) {
@ -112,17 +108,15 @@ async function openDMForUser(matrixClient, userId) {
dmUserId: userId, dmUserId: userId,
}; };
if (SettingsStore.getValue("feature_cross_signing")) { // Check whether all users have uploaded device keys before.
// Check whether all users have uploaded device keys before. // If so, enable encryption in the new room.
// If so, enable encryption in the new room. const usersToDevicesMap = await matrixClient.downloadKeys([userId]);
const usersToDevicesMap = await matrixClient.downloadKeys([userId]); const allHaveDeviceKeys = Object.values(usersToDevicesMap).every(devices => {
const allHaveDeviceKeys = Object.values(usersToDevicesMap).every(devices => { // `devices` is an object of the form { deviceId: deviceInfo, ... }.
// `devices` is an object of the form { deviceId: deviceInfo, ... }. return Object.keys(devices).length > 0;
return Object.keys(devices).length > 0; });
}); if (allHaveDeviceKeys) {
if (allHaveDeviceKeys) { createRoomOptions.encryption = true;
createRoomOptions.encryption = true;
}
} }
createRoom(createRoomOptions); createRoom(createRoomOptions);
@ -167,9 +161,7 @@ function DeviceItem({userId, device}) {
// cross-signing so that other users can then safely trust you. // cross-signing so that other users can then safely trust you.
// For other people's devices, the more general verified check that // For other people's devices, the more general verified check that
// includes locally verified devices can be used. // includes locally verified devices can be used.
const isVerified = (isMe && SettingsStore.getValue("feature_cross_signing")) ? const isVerified = isMe ? deviceTrust.isCrossSigningVerified() : deviceTrust.isVerified();
deviceTrust.isCrossSigningVerified() :
deviceTrust.isVerified();
const classes = classNames("mx_UserInfo_device", { const classes = classNames("mx_UserInfo_device", {
mx_UserInfo_device_verified: isVerified, mx_UserInfo_device_verified: isVerified,
@ -248,9 +240,7 @@ function DevicesSection({devices, userId, loading}) {
// cross-signing so that other users can then safely trust you. // cross-signing so that other users can then safely trust you.
// For other people's devices, the more general verified check that // For other people's devices, the more general verified check that
// includes locally verified devices can be used. // includes locally verified devices can be used.
const isVerified = (isMe && SettingsStore.getValue("feature_cross_signing")) ? const isVerified = isMe ? deviceTrust.isCrossSigningVerified() : deviceTrust.isVerified();
deviceTrust.isCrossSigningVerified() :
deviceTrust.isVerified();
if (isVerified) { if (isVerified) {
expandSectionDevices.push(device); expandSectionDevices.push(device);
@ -1309,8 +1299,7 @@ const BasicUserInfo = ({room, member, groupId, devices, isRoomEncrypted}) => {
const userTrust = cli.checkUserTrust(member.userId); const userTrust = cli.checkUserTrust(member.userId);
const userVerified = userTrust.isCrossSigningVerified(); const userVerified = userTrust.isCrossSigningVerified();
const isMe = member.userId === cli.getUserId(); const isMe = member.userId === cli.getUserId();
const canVerify = SettingsStore.getValue("feature_cross_signing") && const canVerify = homeserverSupportsCrossSigning && !userVerified && !isMe;
homeserverSupportsCrossSigning && !userVerified && !isMe;
const setUpdating = (updating) => { const setUpdating = (updating) => {
setPendingUpdateCount(count => count + (updating ? 1 : -1)); setPendingUpdateCount(count => count + (updating ? 1 : -1));

View file

@ -20,7 +20,6 @@ import PropTypes from "prop-types";
import classNames from 'classnames'; import classNames from 'classnames';
import {_t, _td} from '../../../languageHandler'; import {_t, _td} from '../../../languageHandler';
import {useSettingValue} from "../../../hooks/useSettings";
import AccessibleButton from "../elements/AccessibleButton"; import AccessibleButton from "../elements/AccessibleButton";
import Tooltip from "../elements/Tooltip"; import Tooltip from "../elements/Tooltip";
@ -42,15 +41,6 @@ const crossSigningRoomTitles = {
[E2E_STATE.VERIFIED]: _td("Everyone in this room is verified"), [E2E_STATE.VERIFIED]: _td("Everyone in this room is verified"),
}; };
const legacyUserTitles = {
[E2E_STATE.WARNING]: _td("Some sessions for this user are not trusted"),
[E2E_STATE.VERIFIED]: _td("All sessions for this user are trusted"),
};
const legacyRoomTitles = {
[E2E_STATE.WARNING]: _td("Some sessions in this encrypted room are not trusted"),
[E2E_STATE.VERIFIED]: _td("All sessions in this encrypted room are trusted"),
};
const E2EIcon = ({isUser, status, className, size, onClick, hideTooltip}) => { const E2EIcon = ({isUser, status, className, size, onClick, hideTooltip}) => {
const [hover, setHover] = useState(false); const [hover, setHover] = useState(false);
@ -62,15 +52,10 @@ const E2EIcon = ({isUser, status, className, size, onClick, hideTooltip}) => {
}, className); }, className);
let e2eTitle; let e2eTitle;
const crossSigning = useSettingValue("feature_cross_signing"); if (isUser) {
if (crossSigning && isUser) {
e2eTitle = crossSigningUserTitles[status]; e2eTitle = crossSigningUserTitles[status];
} else if (crossSigning && !isUser) { } else {
e2eTitle = crossSigningRoomTitles[status]; e2eTitle = crossSigningRoomTitles[status];
} else if (!crossSigning && isUser) {
e2eTitle = legacyUserTitles[status];
} else if (!crossSigning && !isUser) {
e2eTitle = legacyRoomTitles[status];
} }
let style; let style;

View file

@ -325,15 +325,6 @@ export default createReactClass({
return; return;
} }
// If cross-signing is off, the old behaviour is to scream at the user
// as if they've done something wrong, which they haven't
if (!SettingsStore.getValue("feature_cross_signing")) {
this.setState({
verified: E2E_STATE.WARNING,
}, this.props.onHeightChanged);
return;
}
if (!this.context.checkUserTrust(mxEvent.getSender()).isCrossSigningVerified()) { if (!this.context.checkUserTrust(mxEvent.getSender()).isCrossSigningVerified()) {
this.setState({ this.setState({
verified: E2E_STATE.NORMAL, verified: E2E_STATE.NORMAL,

File diff suppressed because it is too large Load diff

View file

@ -57,21 +57,19 @@ export default createReactClass({
} }
} }
if (SettingsStore.getValue("feature_cross_signing")) { const { roomId } = this.props.member;
const { roomId } = this.props.member; if (roomId) {
if (roomId) { const isRoomEncrypted = cli.isRoomEncrypted(roomId);
const isRoomEncrypted = cli.isRoomEncrypted(roomId); this.setState({
this.setState({ isRoomEncrypted,
isRoomEncrypted, });
}); if (isRoomEncrypted) {
if (isRoomEncrypted) { cli.on("userTrustStatusChanged", this.onUserTrustStatusChanged);
cli.on("userTrustStatusChanged", this.onUserTrustStatusChanged); cli.on("deviceVerificationChanged", this.onDeviceVerificationChanged);
cli.on("deviceVerificationChanged", this.onDeviceVerificationChanged); this.updateE2EStatus();
this.updateE2EStatus(); } else {
} else { // Listen for room to become encrypted
// Listen for room to become encrypted cli.on("RoomState.events", this.onRoomStateEvents);
cli.on("RoomState.events", this.onRoomStateEvents);
}
} }
} }
}, },

View file

@ -281,33 +281,17 @@ export default class MessageComposer extends React.Component {
} }
renderPlaceholderText() { renderPlaceholderText() {
if (SettingsStore.getValue("feature_cross_signing")) { if (this.state.isQuoting) {
if (this.state.isQuoting) { if (this.props.e2eStatus) {
if (this.props.e2eStatus) { return _t('Send an encrypted reply…');
return _t('Send an encrypted reply…');
} else {
return _t('Send a reply…');
}
} else { } else {
if (this.props.e2eStatus) { return _t('Send a reply…');
return _t('Send an encrypted message…');
} else {
return _t('Send a message…');
}
} }
} else { } else {
if (this.state.isQuoting) { if (this.props.e2eStatus) {
if (this.props.e2eStatus) { return _t('Send an encrypted message…');
return _t('Send an encrypted reply…');
} else {
return _t('Send a reply (unencrypted)…');
}
} else { } else {
if (this.props.e2eStatus) { return _t('Send a message…');
return _t('Send an encrypted message…');
} else {
return _t('Send a message (unencrypted)…');
}
} }
} }
} }

View file

@ -168,10 +168,8 @@ export default createReactClass({
const joinRule = joinRules && joinRules.getContent().join_rule; const joinRule = joinRules && joinRules.getContent().join_rule;
let privateIcon; let privateIcon;
// Don't show an invite-only icon for DMs. Users know they're invite-only. // Don't show an invite-only icon for DMs. Users know they're invite-only.
if (!dmUserId && SettingsStore.getValue("feature_cross_signing")) { if (!dmUserId && joinRule === "invite") {
if (joinRule == "invite") { privateIcon = <InviteOnlyIcon />;
privateIcon = <InviteOnlyIcon />;
}
} }
if (this.props.onCancelClick) { if (this.props.onCancelClick) {

View file

@ -155,9 +155,6 @@ export default createReactClass({
if (!cli.isRoomEncrypted(this.props.room.roomId)) { if (!cli.isRoomEncrypted(this.props.room.roomId)) {
return; return;
} }
if (!SettingsStore.getValue("feature_cross_signing")) {
return;
}
/* At this point, the user has encryption on and cross-signing on */ /* At this point, the user has encryption on and cross-signing on */
this.setState({ this.setState({
@ -515,10 +512,8 @@ export default createReactClass({
} }
let privateIcon = null; let privateIcon = null;
if (SettingsStore.getValue("feature_cross_signing")) { if (this.state.joinRule === "invite" && !dmUserId) {
if (this.state.joinRule == "invite" && !dmUserId) { privateIcon = <InviteOnlyIcon collapsedPanel={this.props.collapsed} />;
privateIcon = <InviteOnlyIcon collapsedPanel={this.props.collapsed} />;
}
} }
let e2eIcon = null; let e2eIcon = null;

View file

@ -194,6 +194,8 @@ export default class CrossSigningPanel extends React.PureComponent {
</div> </div>
); );
} }
// TODO: determine how better to expose this to users in addition to prompts at login/toast
let bootstrapButton; let bootstrapButton;
if ( if (
(!enabledForAccount || !crossSigningPublicKeysOnDevice) && (!enabledForAccount || !crossSigningPublicKeysOnDevice) &&

View file

@ -316,7 +316,7 @@ export default class KeyBackupPanel extends React.PureComponent {
trustedLocally = _t("This backup is trusted because it has been restored on this session"); trustedLocally = _t("This backup is trusted because it has been restored on this session");
} }
let buttonRow = ( const buttonRow = (
<div className="mx_KeyBackupPanel_buttonRow"> <div className="mx_KeyBackupPanel_buttonRow">
<AccessibleButton kind="primary" onClick={this._restoreBackup}> <AccessibleButton kind="primary" onClick={this._restoreBackup}>
{restoreButtonCaption} {restoreButtonCaption}
@ -326,13 +326,6 @@ export default class KeyBackupPanel extends React.PureComponent {
</AccessibleButton> </AccessibleButton>
</div> </div>
); );
if (this.state.backupKeyStored && !SettingsStore.getValue("feature_cross_signing")) {
buttonRow = <p> {_t(
"Backup key stored in secret storage, but this feature is not " +
"enabled on this session. Please enable cross-signing in Labs to " +
"modify key backup state.",
)}</p>;
}
return <div> return <div>
<div>{clientBackupStatus}</div> <div>{clientBackupStatus}</div>

View file

@ -306,9 +306,7 @@ export default class SecurityUserSettingsTab extends React.Component {
// in having advanced details here once all flows are implemented, we // in having advanced details here once all flows are implemented, we
// can remove this. // can remove this.
const CrossSigningPanel = sdk.getComponent('views.settings.CrossSigningPanel'); const CrossSigningPanel = sdk.getComponent('views.settings.CrossSigningPanel');
let crossSigning; const crossSigning = (
if (SettingsStore.getValue("feature_cross_signing")) {
crossSigning = (
<div className='mx_SettingsTab_section'> <div className='mx_SettingsTab_section'>
<span className="mx_SettingsTab_subheading">{_t("Cross-signing")}</span> <span className="mx_SettingsTab_subheading">{_t("Cross-signing")}</span>
<div className='mx_SettingsTab_subsectionText'> <div className='mx_SettingsTab_subsectionText'>
@ -316,7 +314,6 @@ export default class SecurityUserSettingsTab extends React.Component {
</div> </div>
</div> </div>
); );
}
const E2eAdvancedPanel = sdk.getComponent('views.settings.E2eAdvancedPanel'); const E2eAdvancedPanel = sdk.getComponent('views.settings.E2eAdvancedPanel');

View file

@ -23,7 +23,6 @@ import dis from "./dispatcher/dispatcher";
import * as Rooms from "./Rooms"; import * as Rooms from "./Rooms";
import DMRoomMap from "./utils/DMRoomMap"; import DMRoomMap from "./utils/DMRoomMap";
import {getAddressType} from "./UserAddress"; import {getAddressType} from "./UserAddress";
import SettingsStore from "./settings/SettingsStore";
/** /**
* Create a new room, and switch to it. * Create a new room, and switch to it.
@ -226,10 +225,7 @@ export async function ensureDMExists(client, userId) {
if (existingDMRoom) { if (existingDMRoom) {
roomId = existingDMRoom.roomId; roomId = existingDMRoom.roomId;
} else { } else {
let encryption; const encryption = canEncryptToAllUsers(client, [userId]);
if (SettingsStore.getValue("feature_cross_signing")) {
encryption = canEncryptToAllUsers(client, [userId]);
}
roomId = await createRoom({encryption, dmUserId: userId, spinner: false, andView: false}); roomId = await createRoom({encryption, dmUserId: userId, spinner: false, andView: false});
await _waitForMember(client, roomId, userId); await _waitForMember(client, roomId, userId);
} }

View file

@ -164,13 +164,6 @@ export const SETTINGS = {
supportedLevels: ['account'], supportedLevels: ['account'],
default: null, default: null,
}, },
"feature_cross_signing": {
// XXX: We shouldn't be using the feature prefix for non-feature settings. There is an exception
// for this case though as we're converting a feature to a setting for a temporary safety net.
displayName: _td("Enable cross-signing to verify per-user instead of per-session"),
supportedLevels: ['device', 'config'], // we shouldn't use LEVELS_FEATURE for non-features, so copy it here.
default: true,
},
"feature_bridge_state": { "feature_bridge_state": {
isFeature: true, isFeature: true,
supportedLevels: LEVELS_FEATURE, supportedLevels: LEVELS_FEATURE,

View file

@ -22,12 +22,11 @@ import { _t } from './languageHandler';
import {RIGHT_PANEL_PHASES} from "./stores/RightPanelStorePhases"; import {RIGHT_PANEL_PHASES} from "./stores/RightPanelStorePhases";
import {findDMForUser} from './createRoom'; import {findDMForUser} from './createRoom';
import {accessSecretStorage} from './CrossSigningManager'; import {accessSecretStorage} from './CrossSigningManager';
import SettingsStore from './settings/SettingsStore';
import {verificationMethods} from 'matrix-js-sdk/src/crypto'; import {verificationMethods} from 'matrix-js-sdk/src/crypto';
async function enable4SIfNeeded() { async function enable4SIfNeeded() {
const cli = MatrixClientPeg.get(); const cli = MatrixClientPeg.get();
if (!cli.isCryptoEnabled() || !SettingsStore.getValue("feature_cross_signing")) { if (!cli.isCryptoEnabled()) {
return false; return false;
} }
const usk = cli.getCrossSigningId("user_signing"); const usk = cli.getCrossSigningId("user_signing");