Merge pull request #4118 from matrix-org/jryans/hs-xsign-check
Check for cross-signing homeserver support
This commit is contained in:
commit
64dc776e43
6 changed files with 92 additions and 42 deletions
|
@ -53,7 +53,6 @@ function selectText(target) {
|
||||||
*/
|
*/
|
||||||
export default class CreateKeyBackupDialog extends React.PureComponent {
|
export default class CreateKeyBackupDialog extends React.PureComponent {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
secureSecretStorage: PropTypes.bool,
|
|
||||||
onFinished: PropTypes.func.isRequired,
|
onFinished: PropTypes.func.isRequired,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,7 +64,7 @@ export default class CreateKeyBackupDialog extends React.PureComponent {
|
||||||
this._setZxcvbnResultTimeout = null;
|
this._setZxcvbnResultTimeout = null;
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
secureSecretStorage: props.secureSecretStorage,
|
secureSecretStorage: null,
|
||||||
phase: PHASE_PASSPHRASE,
|
phase: PHASE_PASSPHRASE,
|
||||||
passPhrase: '',
|
passPhrase: '',
|
||||||
passPhraseConfirm: '',
|
passPhraseConfirm: '',
|
||||||
|
@ -73,23 +72,20 @@ export default class CreateKeyBackupDialog extends React.PureComponent {
|
||||||
downloaded: false,
|
downloaded: false,
|
||||||
zxcvbnResult: null,
|
zxcvbnResult: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (this.state.secureSecretStorage === undefined) {
|
|
||||||
this.state.secureSecretStorage =
|
|
||||||
SettingsStore.isFeatureEnabled("feature_cross_signing");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async componentDidMount() {
|
||||||
|
const cli = MatrixClientPeg.get();
|
||||||
|
const secureSecretStorage = (
|
||||||
|
SettingsStore.isFeatureEnabled("feature_cross_signing") &&
|
||||||
|
await cli.doesServerSupportUnstableFeature("org.matrix.e2e_cross_signing")
|
||||||
|
);
|
||||||
|
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
|
||||||
// `accessSecretStorage` will handle passphrases as needed.
|
// `accessSecretStorage` will handle passphrases as needed.
|
||||||
if (this.state.secureSecretStorage) {
|
if (secureSecretStorage) {
|
||||||
this.state.phase = PHASE_BACKINGUP;
|
this.setState({ phase: PHASE_BACKINGUP });
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
// If we're using secret storage, skip ahead to the backing up step, as
|
|
||||||
// `accessSecretStorage` will handle passphrases as needed.
|
|
||||||
if (this.state.secureSecretStorage) {
|
|
||||||
this._createBackup();
|
this._createBackup();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,6 +42,7 @@ import {textualPowerLevel} from '../../../Roles';
|
||||||
import MatrixClientContext from "../../../contexts/MatrixClientContext";
|
import MatrixClientContext from "../../../contexts/MatrixClientContext";
|
||||||
import {RIGHT_PANEL_PHASES} from "../../../stores/RightPanelStorePhases";
|
import {RIGHT_PANEL_PHASES} from "../../../stores/RightPanelStorePhases";
|
||||||
import EncryptionPanel from "./EncryptionPanel";
|
import EncryptionPanel from "./EncryptionPanel";
|
||||||
|
import { useAsyncMemo } from '../../../hooks/useAsyncMemo';
|
||||||
|
|
||||||
const _disambiguateDevices = (devices) => {
|
const _disambiguateDevices = (devices) => {
|
||||||
const names = Object.create(null);
|
const names = Object.create(null);
|
||||||
|
@ -916,6 +917,12 @@ const useIsSynapseAdmin = (cli) => {
|
||||||
return isAdmin;
|
return isAdmin;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const useHomeserverSupportsCrossSigning = (cli) => {
|
||||||
|
return useAsyncMemo(async () => {
|
||||||
|
return cli.doesServerSupportUnstableFeature("org.matrix.e2e_cross_signing");
|
||||||
|
}, [cli], false);
|
||||||
|
};
|
||||||
|
|
||||||
function useRoomPermissions(cli, room, user) {
|
function useRoomPermissions(cli, room, user) {
|
||||||
const [roomPermissions, setRoomPermissions] = useState({
|
const [roomPermissions, setRoomPermissions] = useState({
|
||||||
// modifyLevelMax is the max PL we can set this user to, typically min(their PL, our PL) && canSetPL
|
// modifyLevelMax is the max PL we can set this user to, typically min(their PL, our PL) && canSetPL
|
||||||
|
@ -1315,13 +1322,16 @@ const BasicUserInfo = ({room, member, groupId, devices, isRoomEncrypted}) => {
|
||||||
text = _t("Messages in this room are end-to-end encrypted.");
|
text = _t("Messages in this room are end-to-end encrypted.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let verifyButton;
|
||||||
|
const homeserverSupportsCrossSigning = useHomeserverSupportsCrossSigning(cli);
|
||||||
|
if (
|
||||||
|
SettingsStore.isFeatureEnabled("feature_cross_signing") &&
|
||||||
|
homeserverSupportsCrossSigning
|
||||||
|
) {
|
||||||
const userTrust = cli.checkUserTrust(member.userId);
|
const userTrust = cli.checkUserTrust(member.userId);
|
||||||
const userVerified = SettingsStore.isFeatureEnabled("feature_cross_signing") ?
|
const userVerified = userTrust.isCrossSigningVerified();
|
||||||
userTrust.isCrossSigningVerified() :
|
|
||||||
userTrust.isVerified();
|
|
||||||
const isMe = member.userId === cli.getUserId();
|
const isMe = member.userId === cli.getUserId();
|
||||||
|
|
||||||
let verifyButton;
|
|
||||||
if (isRoomEncrypted && !userVerified && !isMe) {
|
if (isRoomEncrypted && !userVerified && !isMe) {
|
||||||
verifyButton = (
|
verifyButton = (
|
||||||
<AccessibleButton className="mx_UserInfo_field" onClick={() => verifyUser(member)}>
|
<AccessibleButton className="mx_UserInfo_field" onClick={() => verifyUser(member)}>
|
||||||
|
@ -1329,6 +1339,7 @@ const BasicUserInfo = ({room, member, groupId, devices, isRoomEncrypted}) => {
|
||||||
</AccessibleButton>
|
</AccessibleButton>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let devicesSection;
|
let devicesSection;
|
||||||
if (isRoomEncrypted) {
|
if (isRoomEncrypted) {
|
||||||
|
|
|
@ -72,11 +72,14 @@ export default class CrossSigningPanel extends React.PureComponent {
|
||||||
const crossSigningPublicKeysOnDevice = crossSigning.getId();
|
const crossSigningPublicKeysOnDevice = crossSigning.getId();
|
||||||
const crossSigningPrivateKeysInStorage = await crossSigning.isStoredInSecretStorage(secretStorage);
|
const crossSigningPrivateKeysInStorage = await crossSigning.isStoredInSecretStorage(secretStorage);
|
||||||
const secretStorageKeyInAccount = await secretStorage.hasKey();
|
const secretStorageKeyInAccount = await secretStorage.hasKey();
|
||||||
|
const homeserverSupportsCrossSigning =
|
||||||
|
await cli.doesServerSupportUnstableFeature("org.matrix.e2e_cross_signing");
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
crossSigningPublicKeysOnDevice,
|
crossSigningPublicKeysOnDevice,
|
||||||
crossSigningPrivateKeysInStorage,
|
crossSigningPrivateKeysInStorage,
|
||||||
secretStorageKeyInAccount,
|
secretStorageKeyInAccount,
|
||||||
|
homeserverSupportsCrossSigning,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -120,6 +123,7 @@ export default class CrossSigningPanel extends React.PureComponent {
|
||||||
crossSigningPublicKeysOnDevice,
|
crossSigningPublicKeysOnDevice,
|
||||||
crossSigningPrivateKeysInStorage,
|
crossSigningPrivateKeysInStorage,
|
||||||
secretStorageKeyInAccount,
|
secretStorageKeyInAccount,
|
||||||
|
homeserverSupportsCrossSigning,
|
||||||
} = this.state;
|
} = this.state;
|
||||||
|
|
||||||
let errorSection;
|
let errorSection;
|
||||||
|
@ -127,13 +131,19 @@ export default class CrossSigningPanel extends React.PureComponent {
|
||||||
errorSection = <div className="error">{error.toString()}</div>;
|
errorSection = <div className="error">{error.toString()}</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const enabled = (
|
// Whether the various keys exist on your account (but not necessarily
|
||||||
|
// on this device).
|
||||||
|
const enabledForAccount = (
|
||||||
crossSigningPrivateKeysInStorage &&
|
crossSigningPrivateKeysInStorage &&
|
||||||
secretStorageKeyInAccount
|
secretStorageKeyInAccount
|
||||||
);
|
);
|
||||||
|
|
||||||
let summarisedStatus;
|
let summarisedStatus;
|
||||||
if (enabled && crossSigningPublicKeysOnDevice) {
|
if (!homeserverSupportsCrossSigning) {
|
||||||
|
summarisedStatus = <p>{_t(
|
||||||
|
"Your homeserver does not support cross-signing.",
|
||||||
|
)}</p>;
|
||||||
|
} else if (enabledForAccount && crossSigningPublicKeysOnDevice) {
|
||||||
summarisedStatus = <p>✅ {_t(
|
summarisedStatus = <p>✅ {_t(
|
||||||
"Cross-signing and secret storage are enabled.",
|
"Cross-signing and secret storage are enabled.",
|
||||||
)}</p>;
|
)}</p>;
|
||||||
|
@ -149,18 +159,18 @@ export default class CrossSigningPanel extends React.PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
let bootstrapButton;
|
let bootstrapButton;
|
||||||
if (!enabled) {
|
if (enabledForAccount) {
|
||||||
bootstrapButton = <div className="mx_CrossSigningPanel_buttonRow">
|
bootstrapButton = (
|
||||||
<AccessibleButton kind="primary" onClick={this._bootstrapSecureSecretStorage}>
|
|
||||||
{_t("Bootstrap cross-signing and secret storage")}
|
|
||||||
</AccessibleButton>
|
|
||||||
</div>;
|
|
||||||
} else {
|
|
||||||
bootstrapButton = <div className="mx_CrossSigningPanel_buttonRow">
|
|
||||||
<AccessibleButton kind="danger" onClick={this._destroySecureSecretStorage}>
|
<AccessibleButton kind="danger" onClick={this._destroySecureSecretStorage}>
|
||||||
{_t("Reset cross-signing and secret storage")}
|
{_t("Reset cross-signing and secret storage")}
|
||||||
</AccessibleButton>
|
</AccessibleButton>
|
||||||
</div>;
|
);
|
||||||
|
} else if (!enabledForAccount && homeserverSupportsCrossSigning) {
|
||||||
|
bootstrapButton = (
|
||||||
|
<AccessibleButton kind="primary" onClick={this._bootstrapSecureSecretStorage}>
|
||||||
|
{_t("Bootstrap cross-signing and secret storage")}
|
||||||
|
</AccessibleButton>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -181,11 +191,17 @@ export default class CrossSigningPanel extends React.PureComponent {
|
||||||
<td>{_t("Secret storage public key:")}</td>
|
<td>{_t("Secret storage public key:")}</td>
|
||||||
<td>{secretStorageKeyInAccount ? _t("in account data") : _t("not found")}</td>
|
<td>{secretStorageKeyInAccount ? _t("in account data") : _t("not found")}</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{_t("Homeserver feature support:")}</td>
|
||||||
|
<td>{homeserverSupportsCrossSigning ? _t("exists") : _t("not found")}</td>
|
||||||
|
</tr>
|
||||||
</tbody></table>
|
</tbody></table>
|
||||||
</details>
|
</details>
|
||||||
{errorSection}
|
{errorSection}
|
||||||
|
<div className="mx_CrossSigningPanel_buttonRow">
|
||||||
{bootstrapButton}
|
{bootstrapButton}
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -127,7 +127,6 @@ export default class KeyBackupPanel extends React.PureComponent {
|
||||||
Modal.createTrackedDialogAsync('Key Backup', 'Key Backup',
|
Modal.createTrackedDialogAsync('Key Backup', 'Key Backup',
|
||||||
import('../../../async-components/views/dialogs/keybackup/CreateKeyBackupDialog'),
|
import('../../../async-components/views/dialogs/keybackup/CreateKeyBackupDialog'),
|
||||||
{
|
{
|
||||||
secureSecretStorage: SettingsStore.isFeatureEnabled("feature_cross_signing"),
|
|
||||||
onFinished: () => {
|
onFinished: () => {
|
||||||
this._loadBackupStatus();
|
this._loadBackupStatus();
|
||||||
},
|
},
|
||||||
|
|
25
src/hooks/useAsyncMemo.js
Normal file
25
src/hooks/useAsyncMemo.js
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
/*
|
||||||
|
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 { useState, useEffect } from 'react';
|
||||||
|
|
||||||
|
export const useAsyncMemo = (fn, deps, initialValue) => {
|
||||||
|
const [value, setValue] = useState(initialValue);
|
||||||
|
useEffect(() => {
|
||||||
|
fn().then(setValue);
|
||||||
|
}, deps); // eslint-disable-line react-hooks/exhaustive-deps
|
||||||
|
return value;
|
||||||
|
};
|
|
@ -562,11 +562,12 @@
|
||||||
"New Password": "New Password",
|
"New Password": "New Password",
|
||||||
"Confirm password": "Confirm password",
|
"Confirm password": "Confirm password",
|
||||||
"Change Password": "Change Password",
|
"Change Password": "Change Password",
|
||||||
|
"Your homeserver does not support cross-signing.": "Your homeserver does not support cross-signing.",
|
||||||
"Cross-signing and secret storage are enabled.": "Cross-signing and secret storage are enabled.",
|
"Cross-signing and secret storage are enabled.": "Cross-signing and secret storage are enabled.",
|
||||||
"Your account has a cross-signing identity in secret storage, but it is not yet trusted by this session.": "Your account has a cross-signing identity in secret storage, but it is not yet trusted by this session.",
|
"Your account has a cross-signing identity in secret storage, but it is not yet trusted by this session.": "Your account has a cross-signing identity in secret storage, but it is not yet trusted by this session.",
|
||||||
"Cross-signing and secret storage are not yet set up.": "Cross-signing and secret storage are not yet set up.",
|
"Cross-signing and secret storage are not yet set up.": "Cross-signing and secret storage are not yet set up.",
|
||||||
"Bootstrap cross-signing and secret storage": "Bootstrap cross-signing and secret storage",
|
|
||||||
"Reset cross-signing and secret storage": "Reset cross-signing and secret storage",
|
"Reset cross-signing and secret storage": "Reset cross-signing and secret storage",
|
||||||
|
"Bootstrap cross-signing and secret storage": "Bootstrap cross-signing and secret storage",
|
||||||
"Cross-signing public keys:": "Cross-signing public keys:",
|
"Cross-signing public keys:": "Cross-signing public keys:",
|
||||||
"in memory": "in memory",
|
"in memory": "in memory",
|
||||||
"not found": "not found",
|
"not found": "not found",
|
||||||
|
@ -574,6 +575,8 @@
|
||||||
"in secret storage": "in secret storage",
|
"in secret storage": "in secret storage",
|
||||||
"Secret storage public key:": "Secret storage public key:",
|
"Secret storage public key:": "Secret storage public key:",
|
||||||
"in account data": "in account data",
|
"in account data": "in account data",
|
||||||
|
"Homeserver feature support:": "Homeserver feature support:",
|
||||||
|
"exists": "exists",
|
||||||
"Your homeserver does not support session management.": "Your homeserver does not support session management.",
|
"Your homeserver does not support session management.": "Your homeserver does not support session management.",
|
||||||
"Unable to load session list": "Unable to load session list",
|
"Unable to load session list": "Unable to load session list",
|
||||||
"Authentication": "Authentication",
|
"Authentication": "Authentication",
|
||||||
|
|
Loading…
Reference in a new issue