Skip to content
This repository has been archived by the owner on Sep 11, 2024. It is now read-only.

[WIP] Human Edition of Element Web prototype #8228

Closed
wants to merge 39 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
dbc7f50
Wording around microphone and camera device permissions
hughns Apr 4, 2022
de5b9a5
Nasty hack to ignore missing translations without having to rebuild t…
hughns Apr 4, 2022
101c0d5
Wording updates for Verification => Setup secure messaging
hughns Apr 4, 2022
13bf565
Settings refactor
hughns Apr 4, 2022
8dcc40a
Move Discovery settings into Privacy panel
hughns Apr 4, 2022
d1abcab
Only prompt to setup cross-signing when you access an encrypted room
hughns Apr 4, 2022
1430593
Prompt user to setup cross-signing when decryption fails on an event …
hughns Apr 4, 2022
96135f7
Move backup in to Advanced and Search to top level of Secure Messaging
hughns Apr 4, 2022
2ccee9c
Secure Messaging settings layout
hughns Apr 4, 2022
d9248da
Fudge tests to pass
hughns Apr 4, 2022
2226263
Fix i8n + test
hughns Apr 4, 2022
2d59f1b
Lint fixes
hughns Apr 4, 2022
7ad2489
Ignore eslint warnings
hughns Apr 4, 2022
8367f06
Account panel devices wording
hughns Apr 4, 2022
baabe41
Wording for new login popup
hughns Apr 4, 2022
6f77b76
Setup => Set up when used as a verb
hughns Apr 5, 2022
b65d2d8
Merge branch 'develop' into hughns/human-edition
hughns Apr 6, 2022
51f04ee
Additional merge fix
hughns Apr 6, 2022
927406a
More Encrpytion => Secure messaging
hughns Apr 6, 2022
9f23605
Merge branch 'develop' into hughns/human-edition
hughns Apr 8, 2022
ffb4020
Fix merge
hughns Apr 8, 2022
3c43bdb
Setup cross signing and recovery after registration
hughns Apr 10, 2022
80faf81
Don't log recovery key to console
hughns Apr 15, 2022
da6e1a4
Allow insecure passwords for testing
hughns Apr 15, 2022
09bee43
Store 4s recovery key after entered for verification
hughns Apr 15, 2022
19cd361
Prompt for saving recovery key if needed during logout
hughns Apr 15, 2022
5a5251f
Session => Device
hughns Apr 15, 2022
d95de4f
More 4S Security Key => Recovery key
hughns Apr 15, 2022
da283d7
Security key => Recovery key
hughns Apr 15, 2022
ca69917
Trust level slider
hughns Apr 18, 2022
74765d0
Allow device renaming in Account panel
hughns Apr 19, 2022
6406b9a
Secure setup modal title
hughns Apr 19, 2022
c2f404a
Take account of whether user has other devices whilst logging out
hughns Apr 19, 2022
8e2c384
Improve layout of secure messaging devices
hughns Apr 19, 2022
738eb36
More wording on saving recovery key
hughns Apr 19, 2022
b5919fc
Allow removal of recovery key from device
hughns May 4, 2022
b45c336
More "setup"=>"set up"
hughns May 4, 2022
54da80a
"room key" => "message key"
hughns May 4, 2022
27f6e20
Update src/SlashCommands.tsx
hughns May 4, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
"start:all": "echo THIS IS FOR LEGACY PURPOSES ONLY. && yarn start:build",
"start:build": "babel src -w -s -d lib --verbose --extensions \".ts,.js\"",
"lint": "yarn lint:types && yarn lint:js && yarn lint:style",
"lint:js": "eslint --max-warnings 0 src test",
"lint:js": "eslint src test",
"lint:js-fix": "eslint --fix src test",
"lint:types": "tsc --noEmit --jsx react",
"lint:style": "stylelint \"res/css/**/*.scss\"",
Expand Down
1 change: 1 addition & 0 deletions res/css/_components.scss
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,7 @@
@import "./views/settings/_CryptographyPanel.scss";
@import "./views/settings/_DevicesPanel.scss";
@import "./views/settings/_E2eAdvancedPanel.scss";
@import "./views/settings/_E2eTrustPanel.scss";
@import "./views/settings/_EmailAddresses.scss";
@import "./views/settings/_FontScalingPanel.scss";
@import "./views/settings/_ImageSizePanel.scss";
Expand Down
28 changes: 28 additions & 0 deletions res/css/views/settings/_E2eTrustPanel.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
Copyright 2022 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.
*/

.mx_E2eTrustPanel {
.mx_Slider {
margin: 0 20px;
.mx_Slider_selectionDot {
display: none;
}
.mx_Slider_label {
margin-top: 8px;
color: $primary-content;
}
}
}
16 changes: 13 additions & 3 deletions src/DeviceListener.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,14 @@ export default class DeviceListener {
// The set of device IDs we're currently displaying toasts for
private displayingToastsForDeviceIds = new Set<string>();

private hasViewedEncryptedRoom = false;

public async viewingEncryptedRoom() {
this.hasViewedEncryptedRoom = true;
this.dismissedThisDeviceToast = false;
return this.recheck();
}

static sharedInstance() {
if (!window.mxDeviceListener) window.mxDeviceListener = new DeviceListener();
return window.mxDeviceListener;
Expand Down Expand Up @@ -211,9 +219,11 @@ export default class DeviceListener {
// If we're in the middle of a secret storage operation, we're likely
// modifying the state involved here, so don't add new toasts to setup.
if (isSecretStorageBeingAccessed()) return false;
// Show setup toasts once the user is in at least one encrypted room.
const cli = MatrixClientPeg.get();
return cli && cli.getRooms().some(r => cli.isRoomEncrypted(r.roomId));
// // Show setup toasts once the user is in at least one encrypted room.
// const cli = MatrixClientPeg.get();
// return cli && cli.getRooms().some(r => cli.isRoomEncrypted(r.roomId));
// Show setup toasts once the user tries to view an encrypted room
return this.hasViewedEncryptedRoom;
}

private async recheck() {
Expand Down
6 changes: 3 additions & 3 deletions src/SlashCommands.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1010,14 +1010,14 @@ export const Commands = [
if (device.getFingerprint() === fingerprint) {
throw newTranslatableError('Session already verified!');
} else {
throw newTranslatableError('WARNING: Session already verified, but keys do NOT MATCH!');
throw newTranslatableError('WARNING: Device already verified, but keys do NOT MATCH!');
}
}

if (device.getFingerprint() !== fingerprint) {
const fprint = device.getFingerprint();
throw newTranslatableError(
'WARNING: KEY VERIFICATION FAILED! The signing key for %(userId)s and session' +
'WARNING: KEY VERIFICATION FAILED! The signing key for %(userId)s and device' +
' %(deviceId)s is "%(fprint)s" which does not match the provided key ' +
'"%(fingerprint)s". This could mean your communications are being intercepted!',
{
Expand All @@ -1038,7 +1038,7 @@ export const Commands = [
<p>
{
_t('The signing key you provided matches the signing key you received ' +
'from %(userId)s\'s session %(deviceId)s. Session marked as verified.',
'from %(userId)s\'s device %(deviceId)s. Device marked as verified.',
{ userId, deviceId })
}
</p>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -373,12 +373,12 @@ export default class CreateKeyBackupDialog extends React.PureComponent<IProps, I
let introText;
if (this.state.copied) {
introText = _t(
"Your Security Key has been <b>copied to your clipboard</b>, paste it to:",
"Your recovery key has been <b>copied to your clipboard</b>, paste it to:",
{}, { b: s => <b>{ s }</b> },
);
} else if (this.state.downloaded) {
introText = _t(
"Your Security Key is in your <b>Downloads</b> folder.",
"Your recovery key is in your <b>Downloads</b> folder.",
{}, { b: s => <b>{ s }</b> },
);
}
Expand Down Expand Up @@ -419,7 +419,7 @@ export default class CreateKeyBackupDialog extends React.PureComponent<IProps, I
return <div>
{ _t(
"Without setting up Secure Message Recovery, you won't be able to restore your " +
"encrypted message history if you log out or use another session.",
"encrypted message history if you log out or use another device.",
) }
<DialogButtons primaryButton={_t('Set up Secure Message Recovery')}
onPrimaryButtonClick={this.onSetUpClick}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -501,9 +501,9 @@ export default class CreateSecretStorageDialog extends React.PureComponent<IProp
>
<div className="mx_CreateSecretStorageDialog_optionTitle">
<span className="mx_CreateSecretStorageDialog_optionIcon mx_CreateSecretStorageDialog_optionIcon_secureBackup" />
{ _t("Generate a Security Key") }
{ _t("Generate a secure messaging recovery key") }
</div>
<div>{ _t("We'll generate a Security Key for you to store somewhere safe, like a password manager or a safe.") }</div>
<div>{ _t("We'll generate a secure messaging recovery key for you to store somewhere safe, like a password manager or a safe.") }</div>
</StyledRadioButton>
);
}
Expand All @@ -522,7 +522,7 @@ export default class CreateSecretStorageDialog extends React.PureComponent<IProp
<span className="mx_CreateSecretStorageDialog_optionIcon mx_CreateSecretStorageDialog_optionIcon_securePhrase" />
{ _t("Enter a Security Phrase") }
</div>
<div>{ _t("Use a secret phrase only you know, and optionally save a Security Key to use for backup.") }</div>
<div>{ _t("Use a secret phrase only you know, and optionally save a recovery key to use for backup.") }</div>
</StyledRadioButton>
);
}
Expand Down Expand Up @@ -586,7 +586,7 @@ export default class CreateSecretStorageDialog extends React.PureComponent<IProp

return <form onSubmit={this.onMigrateFormSubmit}>
<p>{ _t(
"Upgrade this session to allow it to verify other sessions, " +
"Upgrade this device to allow it to verify other devices, " +
"granting them access to encrypted messages and marking them " +
"as trusted for other users.",
) }</p>
Expand Down Expand Up @@ -718,7 +718,7 @@ export default class CreateSecretStorageDialog extends React.PureComponent<IProp

return <div>
<p>{ _t(
"Store your Security Key somewhere safe, like a password manager or a safe, " +
"Store your recovery key somewhere safe, like a password manager or a safe, " +
"as it's used to safeguard your encrypted data.",
) }</p>
<div className="mx_CreateSecretStorageDialog_primaryContainer mx_CreateSecretStorageDialog_recoveryKeyPrimarycontainer">
Expand Down Expand Up @@ -799,7 +799,7 @@ export default class CreateSecretStorageDialog extends React.PureComponent<IProp
case Phase.ConfirmSkip:
return _t('Are you sure?');
case Phase.ShowKey:
return _t('Save your Security Key');
return _t('Save your recovery key');
case Phase.Storing:
return _t('Setting up keys');
default:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,13 +122,13 @@ export default class ImportE2eKeysDialog extends React.Component<IProps, IState>
return (
<BaseDialog className='mx_importE2eKeysDialog'
onFinished={this.props.onFinished}
title={_t("Import room keys")}
title={_t("Import message keys")}
>
<form onSubmit={this.onFormSubmit}>
<div className="mx_Dialog_content">
<p>
{ _t(
'This process allows you to import encryption keys ' +
'This process allows you to import message encryption keys ' +
'that you had previously exported from another Matrix ' +
'client. You will then be able to decrypt any ' +
'messages that the other client could decrypt.',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ export default class NewRecoveryMethodDialog extends React.PureComponent<IProps>
content = <div>
{ newMethodDetected }
<p>{ _t(
"This session is encrypting history using the new recovery method.",
"This device is encrypting history using the new recovery method.",
) }</p>
{ hackWarning }
<DialogButtons
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,12 @@ export default class RecoveryMethodRemovedDialog extends React.PureComponent<IPr
>
<div>
<p>{ _t(
"This session has detected that your Security Phrase and key " +
"This device has detected that your Security Phrase and key " +
"for Secure Messages have been removed.",
) }</p>
<p>{ _t(
"If you did this accidentally, you can setup Secure Messages on " +
"this session which will re-encrypt this session's message " +
"If you did this accidentally, you can set up Secure Messages on " +
"this device which will re-encrypt this device's message " +
"history with a new recovery method.",
) }</p>
<p className="warning">{ _t(
Expand Down
43 changes: 30 additions & 13 deletions src/components/structures/MatrixChat.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,6 @@ import { showToast as showMobileGuideToast } from '../../toasts/MobileGuideToast
import { shouldUseLoginForWelcome } from "../../utils/pages";
import RoomListStore from "../../stores/room-list/RoomListStore";
import { RoomUpdateCause } from "../../stores/room-list/models";
import SecurityCustomisations from "../../customisations/Security";
import Spinner from "../views/elements/Spinner";
import QuestionDialog from "../views/dialogs/QuestionDialog";
import UserSettingsDialog from '../views/dialogs/UserSettingsDialog';
Expand Down Expand Up @@ -375,18 +374,36 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
return;
}

const crossSigningIsSetUp = cli.getStoredCrossSigningForUser(cli.getUserId());
if (crossSigningIsSetUp) {
if (SecurityCustomisations.SHOW_ENCRYPTION_SETUP_UI === false) {
this.onLoggedIn();
} else {
this.setStateForNewView({ view: Views.COMPLETE_SECURITY });
}
} else if (await cli.doesServerSupportUnstableFeature("org.matrix.e2e_cross_signing")) {
this.setStateForNewView({ view: Views.E2E_SETUP });
} else {
this.onLoggedIn();
if (MatrixClientPeg.currentUserIsJustRegistered()) {
// auto generate a recovery key
const recoveryKey = await cli.createRecoveryKeyFromPassphrase();

// use grace period for auth:
await cli.crypto.bootstrapCrossSigning({
setupNewCrossSigning: true,
authUploadDeviceSigningKeys: async (makeRequest) => { await makeRequest(undefined); },
});

await cli.bootstrapSecretStorage({
setupNewKeyBackup: true,
setupNewSecretStorage: true,
createSecretStorageKey: () => Promise.resolve(recoveryKey),
});
}
// const crossSigningIsSetUp = cli.getStoredCrossSigningForUser(cli.getUserId());
// if (crossSigningIsSetUp) {
// if (SecurityCustomisations.SHOW_ENCRYPTION_SETUP_UI === false) {
// this.onLoggedIn();
// } else {
// this.setStateForNewView({ view: Views.COMPLETE_SECURITY });
// }
// } else if (await cli.doesServerSupportUnstableFeature("org.matrix.e2e_cross_signing")) {
// this.setStateForNewView({ view: Views.E2E_SETUP });
// } else {
// this.onLoggedIn();
// }
this.onLoggedIn();

this.setState({ pendingInitialSync: false });
}

Expand Down Expand Up @@ -1496,7 +1513,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {

Modal.createTrackedDialog('Signed out', '', ErrorDialog, {
title: _t('Signed Out'),
description: _t('For security, this session has been signed out. Please sign in again.'),
description: _t('For security, this device has been signed out. Please sign in again.'),
});

dis.dispatch({
Expand Down
3 changes: 3 additions & 0 deletions src/components/structures/RoomView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ import { DoAfterSyncPreparedPayload } from '../../dispatcher/payloads/DoAfterSyn
import FileDropTarget from './FileDropTarget';
import Measured from '../views/elements/Measured';
import { FocusComposerPayload } from '../../dispatcher/payloads/FocusComposerPayload';
import DeviceListener from '../../DeviceListener';
import { haveRendererForEvent } from "../../events/EventTileFactory";

const DEBUG = false;
Expand Down Expand Up @@ -1139,6 +1140,8 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
e2eStatus = await shieldStatusForRoom(this.context, room);
}

await DeviceListener.sharedInstance().viewingEncryptedRoom();

if (this.unmounted) return;
this.setState({ e2eStatus });
}
Expand Down
25 changes: 15 additions & 10 deletions src/components/structures/UserMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -316,17 +316,12 @@ export default class UserMenu extends React.Component<IProps, IState> {
this.setState({ contextMenuPosition: null }); // also close the menu
};

private onSignOutClick = async (ev: ButtonEvent) => {
private onSignOutClick = (ev: ButtonEvent) => {
ev.preventDefault();
ev.stopPropagation();

const cli = MatrixClientPeg.get();
if (!cli || !cli.isCryptoEnabled() || !(await cli.exportRoomKeys())?.length) {
// log out without user prompt if they have no local megolm sessions
dis.dispatch({ action: 'logout' });
} else {
Modal.createTrackedDialog('Logout from LeftPanel', '', LogoutDialog);
}
// always open dialog which will handle confirmation if needed
Modal.createTrackedDialog('Logout from LeftPanel', '', LogoutDialog, { noConfirm: true });

this.setState({ contextMenuPosition: null }); // also close the menu
};
Expand Down Expand Up @@ -439,8 +434,18 @@ export default class UserMenu extends React.Component<IProps, IState> {
/>
<IconizedContextMenuOption
iconClassName="mx_UserMenu_iconLock"
label={_t("Security & Privacy")}
onClick={(e) => this.onSettingsOpen(e, UserTab.Security)}
label={_t("Account")}
onClick={(e) => this.onSettingsOpen(e, UserTab.Account)}
/>
<IconizedContextMenuOption
iconClassName="mx_UserMenu_iconLock"
label={_t("Secure messaging")}
onClick={(e) => this.onSettingsOpen(e, UserTab.SecureMessaging)}
/>
<IconizedContextMenuOption
iconClassName="mx_UserMenu_iconLock"
label={_t("Privacy")}
onClick={(e) => this.onSettingsOpen(e, UserTab.Privacy)}
/>
<IconizedContextMenuOption
iconClassName="mx_UserMenu_iconSettings"
Expand Down
8 changes: 4 additions & 4 deletions src/components/structures/auth/CompleteSecurity.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,8 @@ export default class CompleteSecurity extends React.Component<IProps, IState> {
icon = <span className="mx_CompleteSecurity_headerIcon mx_E2EIcon_warning" />;
title = _t("Unable to verify this device");
} else {
icon = <span className="mx_CompleteSecurity_headerIcon mx_E2EIcon_warning" />;
title = _t("Verify this device");
icon = <span className="mx_CompleteSecurity_headerIcon mx_E2EIcon_normal" />;
title = _t("Secure messaging setup");
}
} else if (phase === Phase.Done) {
icon = <span className="mx_CompleteSecurity_headerIcon mx_E2EIcon_verified" />;
Expand All @@ -79,11 +79,11 @@ export default class CompleteSecurity extends React.Component<IProps, IState> {
icon = <span className="mx_CompleteSecurity_headerIcon mx_E2EIcon_warning" />;
title = _t("Are you sure?");
} else if (phase === Phase.Busy) {
icon = <span className="mx_CompleteSecurity_headerIcon mx_E2EIcon_warning" />;
icon = <span className="mx_CompleteSecurity_headerIcon mx_E2EIcon_normal" />;
title = _t("Verify this device");
} else if (phase === Phase.ConfirmReset) {
icon = <span className="mx_CompleteSecurity_headerIcon mx_E2EIcon_warning" />;
title = _t("Really reset verification keys?");
title = _t("Are you sure?");
} else if (phase === Phase.Finished) {
// SetupEncryptionBody will take care of calling onFinished, we don't need to do anything
} else {
Expand Down
4 changes: 2 additions & 2 deletions src/components/structures/auth/ForgotPassword.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -180,8 +180,8 @@ export default class ForgotPassword extends React.Component<IProps, IState> {
<div>
{ _t(
"Changing your password will reset any end-to-end encryption keys " +
"on all of your sessions, making encrypted chat history unreadable. Set up " +
"Key Backup or export your room keys from another session before resetting your " +
"on all of your devices, making encrypted chat history unreadable. Set up " +
"Key Backup or export your message keys from another device before resetting your " +
"password.",
) }
</div>,
Expand Down
Loading