Merge pull request #6958 from andybalaam/separate-cryptography-setting-component
Break 'Cryptography' settings into a separate component
This commit is contained in:
commit
f05e35bd94
8 changed files with 179 additions and 102 deletions
|
@ -244,6 +244,7 @@
|
|||
@import "./views/rooms/_WhoIsTypingTile.scss";
|
||||
@import "./views/settings/_AvatarSetting.scss";
|
||||
@import "./views/settings/_CrossSigningPanel.scss";
|
||||
@import "./views/settings/_CryptographyPanel.scss";
|
||||
@import "./views/settings/_DevicesPanel.scss";
|
||||
@import "./views/settings/_E2eAdvancedPanel.scss";
|
||||
@import "./views/settings/_EmailAddresses.scss";
|
||||
|
|
22
res/css/views/settings/_CryptographyPanel.scss
Normal file
22
res/css/views/settings/_CryptographyPanel.scss
Normal file
|
@ -0,0 +1,22 @@
|
|||
.mx_CryptographyPanel_sessionInfo {
|
||||
display: table;
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.mx_CryptographyPanel_sessionInfo > li {
|
||||
display: table-row;
|
||||
}
|
||||
|
||||
.mx_CryptographyPanel_sessionInfo > li > label,
|
||||
.mx_CryptographyPanel_sessionInfo > li > span {
|
||||
display: table-cell;
|
||||
padding-right: 1em;
|
||||
}
|
||||
|
||||
.mx_CryptographyPanel_importExportButtons .mx_AccessibleButton {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.mx_CryptographyPanel_importExportButtons {
|
||||
margin-bottom: 15px;
|
||||
}
|
|
@ -14,33 +14,10 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
.mx_SecurityUserSettingsTab_deviceInfo {
|
||||
display: table;
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.mx_SecurityUserSettingsTab_deviceInfo > li {
|
||||
display: table-row;
|
||||
}
|
||||
|
||||
.mx_SecurityUserSettingsTab_deviceInfo > li > label,
|
||||
.mx_SecurityUserSettingsTab_deviceInfo > li > span {
|
||||
display: table-cell;
|
||||
padding-right: 1em;
|
||||
}
|
||||
|
||||
.mx_SecurityUserSettingsTab_importExportButtons .mx_AccessibleButton {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.mx_SecurityUserSettingsTab_bulkOptions .mx_AccessibleButton {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.mx_SecurityUserSettingsTab_importExportButtons {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.mx_SecurityUserSettingsTab_ignoredUser {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
|
110
src/components/views/settings/CryptographyPanel.tsx
Normal file
110
src/components/views/settings/CryptographyPanel.tsx
Normal file
|
@ -0,0 +1,110 @@
|
|||
/*
|
||||
Copyright 2021 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 { MatrixClientPeg } from '../../../MatrixClientPeg';
|
||||
import { _t } from '../../../languageHandler';
|
||||
import Modal from '../../../Modal';
|
||||
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
||||
import AccessibleButton from "../elements/AccessibleButton";
|
||||
import * as FormattingUtils from "../../../utils/FormattingUtils";
|
||||
import SettingsStore from "../../../settings/SettingsStore";
|
||||
import SettingsFlag from "../elements/SettingsFlag";
|
||||
import { SettingLevel } from "../../../settings/SettingLevel";
|
||||
|
||||
interface IProps {
|
||||
}
|
||||
|
||||
interface IState {
|
||||
}
|
||||
|
||||
@replaceableComponent("views.settings.CryptographyPanel")
|
||||
export default class CryptographyPanel extends React.Component<IProps, IState> {
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
public render(): JSX.Element {
|
||||
const client = MatrixClientPeg.get();
|
||||
const deviceId = client.deviceId;
|
||||
let identityKey = client.getDeviceEd25519Key();
|
||||
if (!identityKey) {
|
||||
identityKey = _t("<not supported>");
|
||||
} else {
|
||||
identityKey = FormattingUtils.formatCryptoKey(identityKey);
|
||||
}
|
||||
|
||||
let importExportButtons = null;
|
||||
if (client.isCryptoEnabled()) {
|
||||
importExportButtons = (
|
||||
<div className='mx_CryptographyPanel_importExportButtons'>
|
||||
<AccessibleButton kind='primary' onClick={this.onExportE2eKeysClicked}>
|
||||
{ _t("Export E2E room keys") }
|
||||
</AccessibleButton>
|
||||
<AccessibleButton kind='primary' onClick={this.onImportE2eKeysClicked}>
|
||||
{ _t("Import E2E room keys") }
|
||||
</AccessibleButton>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
let noSendUnverifiedSetting;
|
||||
if (SettingsStore.isEnabled("blacklistUnverifiedDevices")) {
|
||||
noSendUnverifiedSetting = <SettingsFlag
|
||||
name='blacklistUnverifiedDevices'
|
||||
level={SettingLevel.DEVICE}
|
||||
onChange={this.updateBlacklistDevicesFlag}
|
||||
/>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='mx_SettingsTab_section mx_CryptographyPanel'>
|
||||
<span className='mx_SettingsTab_subheading'>{ _t("Cryptography") }</span>
|
||||
<ul className='mx_SettingsTab_subsectionText mx_CryptographyPanel_sessionInfo'>
|
||||
<li>
|
||||
<label>{ _t("Session ID:") }</label>
|
||||
<span><code>{ deviceId }</code></span>
|
||||
</li>
|
||||
<li>
|
||||
<label>{ _t("Session key:") }</label>
|
||||
<span><code><b>{ identityKey }</b></code></span>
|
||||
</li>
|
||||
</ul>
|
||||
{ importExportButtons }
|
||||
{ noSendUnverifiedSetting }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
private onExportE2eKeysClicked = (): void => {
|
||||
Modal.createTrackedDialogAsync('Export E2E Keys', '',
|
||||
import('../../../async-components/views/dialogs/security/ExportE2eKeysDialog'),
|
||||
{ matrixClient: MatrixClientPeg.get() },
|
||||
);
|
||||
};
|
||||
|
||||
private onImportE2eKeysClicked = (): void => {
|
||||
Modal.createTrackedDialogAsync('Import E2E Keys', '',
|
||||
import('../../../async-components/views/dialogs/security/ImportE2eKeysDialog'),
|
||||
{ matrixClient: MatrixClientPeg.get() },
|
||||
);
|
||||
};
|
||||
|
||||
private updateBlacklistDevicesFlag = (checked): void => {
|
||||
MatrixClientPeg.get().setGlobalBlacklistUnverifiedDevices(checked);
|
||||
};
|
||||
}
|
|
@ -21,10 +21,8 @@ import { sleep } from "matrix-js-sdk/src/utils";
|
|||
import { _t } from "../../../../../languageHandler";
|
||||
import SdkConfig from "../../../../../SdkConfig";
|
||||
import { MatrixClientPeg } from "../../../../../MatrixClientPeg";
|
||||
import * as FormattingUtils from "../../../../../utils/FormattingUtils";
|
||||
import AccessibleButton from "../../../elements/AccessibleButton";
|
||||
import Analytics from "../../../../../Analytics";
|
||||
import Modal from "../../../../../Modal";
|
||||
import dis from "../../../../../dispatcher/dispatcher";
|
||||
import { privateShouldBeEncrypted } from "../../../../../createRoom";
|
||||
import { SettingLevel } from "../../../../../settings/SettingLevel";
|
||||
|
@ -37,6 +35,7 @@ import { replaceableComponent } from "../../../../../utils/replaceableComponent"
|
|||
import { PosthogAnalytics } from "../../../../../PosthogAnalytics";
|
||||
import { ActionPayload } from "../../../../../dispatcher/payloads";
|
||||
import { Room } from "matrix-js-sdk/src/models/room";
|
||||
import CryptographyPanel from "../../CryptographyPanel";
|
||||
import DevicesPanel from "../../DevicesPanel";
|
||||
import SettingsFlag from "../../../elements/SettingsFlag";
|
||||
import CrossSigningPanel from "../../CrossSigningPanel";
|
||||
|
@ -112,30 +111,12 @@ export default class SecurityUserSettingsTab extends React.Component<IProps, ISt
|
|||
dis.unregister(this.dispatcherRef);
|
||||
}
|
||||
|
||||
private updateBlacklistDevicesFlag = (checked): void => {
|
||||
MatrixClientPeg.get().setGlobalBlacklistUnverifiedDevices(checked);
|
||||
};
|
||||
|
||||
private updateAnalytics = (checked: boolean): void => {
|
||||
checked ? Analytics.enable() : Analytics.disable();
|
||||
CountlyAnalytics.instance.enable(/* anonymous = */ !checked);
|
||||
PosthogAnalytics.instance.updateAnonymityFromSettings(MatrixClientPeg.get().getUserId());
|
||||
};
|
||||
|
||||
private onExportE2eKeysClicked = (): void => {
|
||||
Modal.createTrackedDialogAsync('Export E2E Keys', '',
|
||||
import('../../../../../async-components/views/dialogs/security/ExportE2eKeysDialog'),
|
||||
{ matrixClient: MatrixClientPeg.get() },
|
||||
);
|
||||
};
|
||||
|
||||
private onImportE2eKeysClicked = (): void => {
|
||||
Modal.createTrackedDialogAsync('Import E2E Keys', '',
|
||||
import('../../../../../async-components/views/dialogs/security/ImportE2eKeysDialog'),
|
||||
{ matrixClient: MatrixClientPeg.get() },
|
||||
);
|
||||
};
|
||||
|
||||
private onGoToUserProfileClick = (): void => {
|
||||
dis.dispatch({
|
||||
action: 'view_user_info',
|
||||
|
@ -211,58 +192,6 @@ export default class SecurityUserSettingsTab extends React.Component<IProps, ISt
|
|||
this.manageInvites(false);
|
||||
};
|
||||
|
||||
private renderCurrentDeviceInfo(): JSX.Element {
|
||||
const client = MatrixClientPeg.get();
|
||||
const deviceId = client.deviceId;
|
||||
let identityKey = client.getDeviceEd25519Key();
|
||||
if (!identityKey) {
|
||||
identityKey = _t("<not supported>");
|
||||
} else {
|
||||
identityKey = FormattingUtils.formatCryptoKey(identityKey);
|
||||
}
|
||||
|
||||
let importExportButtons = null;
|
||||
if (client.isCryptoEnabled()) {
|
||||
importExportButtons = (
|
||||
<div className='mx_SecurityUserSettingsTab_importExportButtons'>
|
||||
<AccessibleButton kind='primary' onClick={this.onExportE2eKeysClicked}>
|
||||
{ _t("Export E2E room keys") }
|
||||
</AccessibleButton>
|
||||
<AccessibleButton kind='primary' onClick={this.onImportE2eKeysClicked}>
|
||||
{ _t("Import E2E room keys") }
|
||||
</AccessibleButton>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
let noSendUnverifiedSetting;
|
||||
if (SettingsStore.isEnabled("blacklistUnverifiedDevices")) {
|
||||
noSendUnverifiedSetting = <SettingsFlag
|
||||
name='blacklistUnverifiedDevices'
|
||||
level={SettingLevel.DEVICE}
|
||||
onChange={this.updateBlacklistDevicesFlag}
|
||||
/>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='mx_SettingsTab_section'>
|
||||
<span className='mx_SettingsTab_subheading'>{ _t("Cryptography") }</span>
|
||||
<ul className='mx_SettingsTab_subsectionText mx_SecurityUserSettingsTab_deviceInfo'>
|
||||
<li>
|
||||
<label>{ _t("Session ID:") }</label>
|
||||
<span><code>{ deviceId }</code></span>
|
||||
</li>
|
||||
<li>
|
||||
<label>{ _t("Session key:") }</label>
|
||||
<span><code><b>{ identityKey }</b></code></span>
|
||||
</li>
|
||||
</ul>
|
||||
{ importExportButtons }
|
||||
{ noSendUnverifiedSetting }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
private renderIgnoredUsers(): JSX.Element {
|
||||
const { waitingUnignored, ignoredUserIds } = this.state;
|
||||
|
||||
|
@ -418,7 +347,7 @@ export default class SecurityUserSettingsTab extends React.Component<IProps, ISt
|
|||
{ secureBackup }
|
||||
{ eventIndex }
|
||||
{ crossSigning }
|
||||
{ this.renderCurrentDeviceInfo() }
|
||||
<CryptographyPanel />
|
||||
</div>
|
||||
{ privacySection }
|
||||
{ advancedSection }
|
||||
|
|
|
@ -1128,6 +1128,11 @@
|
|||
"User signing private key:": "User signing private key:",
|
||||
"Homeserver feature support:": "Homeserver feature support:",
|
||||
"exists": "exists",
|
||||
"<not supported>": "<not supported>",
|
||||
"Import E2E room keys": "Import E2E room keys",
|
||||
"Cryptography": "Cryptography",
|
||||
"Session ID:": "Session ID:",
|
||||
"Session key:": "Session key:",
|
||||
"Your homeserver does not support session management.": "Your homeserver does not support session management.",
|
||||
"Unable to load session list": "Unable to load session list",
|
||||
"Confirm deleting these sessions by using Single Sign On to prove your identity.|other": "Confirm deleting these sessions by using Single Sign On to prove your identity.",
|
||||
|
@ -1393,11 +1398,6 @@
|
|||
"Read Marker lifetime (ms)": "Read Marker lifetime (ms)",
|
||||
"Read Marker off-screen lifetime (ms)": "Read Marker off-screen lifetime (ms)",
|
||||
"Unignore": "Unignore",
|
||||
"<not supported>": "<not supported>",
|
||||
"Import E2E room keys": "Import E2E room keys",
|
||||
"Cryptography": "Cryptography",
|
||||
"Session ID:": "Session ID:",
|
||||
"Session key:": "Session key:",
|
||||
"You have no ignored users.": "You have no ignored users.",
|
||||
"Bulk options": "Bulk options",
|
||||
"Accept all %(invitedRooms)s invites": "Accept all %(invitedRooms)s invites",
|
||||
|
|
38
test/components/views/settings/CryptographyPanel-test.tsx
Normal file
38
test/components/views/settings/CryptographyPanel-test.tsx
Normal file
|
@ -0,0 +1,38 @@
|
|||
import '../../../skinned-sdk';
|
||||
import * as TestUtils from '../../../test-utils';
|
||||
|
||||
import { MatrixClientPeg } from '../../../../src/MatrixClientPeg';
|
||||
import React, { ReactElement } from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
|
||||
import { MatrixClient } from 'matrix-js-sdk';
|
||||
import CryptographyPanel from '../../../../src/components/views/settings/CryptographyPanel';
|
||||
|
||||
describe('CryptographyPanel', () => {
|
||||
it('shows the session ID and key', () => {
|
||||
const sessionId = "ABCDEFGHIJ";
|
||||
const sessionKey = "AbCDeFghIJK7L/m4nOPqRSTUVW4xyzaBCDef6gHIJkl";
|
||||
const sessionKeyFormatted = "<b>AbCD eFgh IJK7 L/m4 nOPq RSTU VW4x yzaB CDef 6gHI Jkl</b>";
|
||||
|
||||
TestUtils.stubClient();
|
||||
const client: MatrixClient = MatrixClientPeg.get();
|
||||
client.deviceId = sessionId;
|
||||
client.getDeviceEd25519Key = () => sessionKey;
|
||||
|
||||
// When we render the CryptographyPanel
|
||||
const rendered = render(<CryptographyPanel />);
|
||||
|
||||
// Then it displays info about the user's session
|
||||
const codes = rendered.querySelectorAll("code");
|
||||
expect(codes.length).toEqual(2);
|
||||
expect(codes[0].innerHTML).toEqual(sessionId);
|
||||
expect(codes[1].innerHTML).toEqual(sessionKeyFormatted);
|
||||
});
|
||||
});
|
||||
|
||||
function render(component: ReactElement<CryptographyPanel>): HTMLDivElement {
|
||||
const parentDiv = document.createElement('div');
|
||||
document.body.appendChild(parentDiv);
|
||||
ReactDOM.render(component, parentDiv);
|
||||
return parentDiv;
|
||||
}
|
|
@ -44,7 +44,7 @@ module.exports.enableLazyLoading = async function(session) {
|
|||
module.exports.getE2EDeviceFromSettings = async function(session) {
|
||||
session.log.step(`gets e2e device/key from settings`);
|
||||
await openSettings(session, "security");
|
||||
const deviceAndKey = await session.queryAll(".mx_SettingsTab_section .mx_SecurityUserSettingsTab_deviceInfo code");
|
||||
const deviceAndKey = await session.queryAll(".mx_SettingsTab_section .mx_CryptographyPanel code");
|
||||
assert.equal(deviceAndKey.length, 2);
|
||||
const id = await (await deviceAndKey[0].getProperty("innerText")).jsonValue();
|
||||
const key = await (await deviceAndKey[1].getProperty("innerText")).jsonValue();
|
||||
|
|
Loading…
Reference in a new issue