Apply prettier formatting

This commit is contained in:
Michael Weimann 2022-12-12 12:24:14 +01:00
parent 1cac306093
commit 526645c791
No known key found for this signature in database
GPG key ID: 53F535A266BB9584
1576 changed files with 65385 additions and 62478 deletions

View file

@ -14,13 +14,13 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import React from 'react';
import React from "react";
import BaseDialog from "../../../../components/views/dialogs/BaseDialog";
import Spinner from "../../../../components/views/elements/Spinner";
import DialogButtons from "../../../../components/views/elements/DialogButtons";
import dis from "../../../../dispatcher/dispatcher";
import { _t } from '../../../../languageHandler';
import { _t } from "../../../../languageHandler";
import SettingsStore from "../../../../settings/SettingsStore";
import EventIndexPeg from "../../../../indexing/EventIndexPeg";
import { Action } from "../../../../dispatcher/actions";
@ -50,7 +50,7 @@ export default class DisableEventIndexDialog extends React.Component<IProps, ISt
disabling: true,
});
await SettingsStore.setValue('enableEventIndexing', null, SettingLevel.DEVICE, false);
await SettingsStore.setValue("enableEventIndexing", null, SettingLevel.DEVICE, false);
await EventIndexPeg.deleteEventIndex();
this.props.onFinished(true);
dis.fire(Action.ViewUserSettings);
@ -59,10 +59,10 @@ export default class DisableEventIndexDialog extends React.Component<IProps, ISt
public render(): React.ReactNode {
return (
<BaseDialog onFinished={this.props.onFinished} title={_t("Are you sure?")}>
{ _t("If disabled, messages from encrypted rooms won't appear in search results.") }
{ this.state.disabling ? <Spinner /> : <div /> }
{_t("If disabled, messages from encrypted rooms won't appear in search results.")}
{this.state.disabling ? <Spinner /> : <div />}
<DialogButtons
primaryButton={_t('Disable')}
primaryButton={_t("Disable")}
onPrimaryButtonClick={this.onDisable}
primaryButtonClass="danger"
cancelButtonClass="warning"

View file

@ -14,16 +14,16 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import React from 'react';
import React from "react";
import { _t } from '../../../../languageHandler';
import SdkConfig from '../../../../SdkConfig';
import { _t } from "../../../../languageHandler";
import SdkConfig from "../../../../SdkConfig";
import SettingsStore from "../../../../settings/SettingsStore";
import Modal from '../../../../Modal';
import Modal from "../../../../Modal";
import { formatBytes, formatCountLong } from "../../../../utils/FormattingUtils";
import EventIndexPeg from "../../../../indexing/EventIndexPeg";
import { SettingLevel } from "../../../../settings/SettingLevel";
import Field from '../../../../components/views/elements/Field';
import Field from "../../../../components/views/elements/Field";
import BaseDialog from "../../../../components/views/dialogs/BaseDialog";
import DialogButtons from "../../../../components/views/elements/DialogButtons";
import { IDialogProps } from "../../../../components/views/dialogs/IDialogProps";
@ -52,8 +52,7 @@ export default class ManageEventIndexDialog extends React.Component<IProps, ISta
crawlingRoomsCount: 0,
roomCount: 0,
currentRoom: null,
crawlerSleepTime:
SettingsStore.getValueAt(SettingLevel.DEVICE, 'crawlerSleepTime'),
crawlerSleepTime: SettingsStore.getValueAt(SettingLevel.DEVICE, "crawlerSleepTime"),
};
}
@ -149,43 +148,48 @@ export default class ManageEventIndexDialog extends React.Component<IProps, ISta
if (this.state.currentRoom === null) {
crawlerState = _t("Not currently indexing messages for any room.");
} else {
crawlerState = (
_t("Currently indexing: %(currentRoom)s", { currentRoom: this.state.currentRoom })
);
crawlerState = _t("Currently indexing: %(currentRoom)s", { currentRoom: this.state.currentRoom });
}
const doneRooms = Math.max(0, (this.state.roomCount - this.state.crawlingRoomsCount));
const doneRooms = Math.max(0, this.state.roomCount - this.state.crawlingRoomsCount);
const eventIndexingSettings = (
<div>
{ _t(
{_t(
"%(brand)s is securely caching encrypted messages locally for them " +
"to appear in search results:",
"to appear in search results:",
{ brand },
) }
<div className='mx_SettingsTab_subsectionText'>
{ crawlerState }<br />
{ _t("Space used:") } { formatBytes(this.state.eventIndexSize, 0) }<br />
{ _t("Indexed messages:") } { formatCountLong(this.state.eventCount) }<br />
{ _t("Indexed rooms:") } { _t("%(doneRooms)s out of %(totalRooms)s", {
)}
<div className="mx_SettingsTab_subsectionText">
{crawlerState}
<br />
{_t("Space used:")} {formatBytes(this.state.eventIndexSize, 0)}
<br />
{_t("Indexed messages:")} {formatCountLong(this.state.eventCount)}
<br />
{_t("Indexed rooms:")}{" "}
{_t("%(doneRooms)s out of %(totalRooms)s", {
doneRooms: formatCountLong(doneRooms),
totalRooms: formatCountLong(this.state.roomCount),
}) } <br />
})}{" "}
<br />
<Field
label={_t('Message downloading sleep time(ms)')}
type='number'
label={_t("Message downloading sleep time(ms)")}
type="number"
value={this.state.crawlerSleepTime.toString()}
onChange={this.onCrawlerSleepTimeChange} />
onChange={this.onCrawlerSleepTimeChange}
/>
</div>
</div>
);
return (
<BaseDialog className='mx_ManageEventIndexDialog'
<BaseDialog
className="mx_ManageEventIndexDialog"
onFinished={this.props.onFinished}
title={_t("Message search")}
>
{ eventIndexingSettings }
{eventIndexingSettings}
<DialogButtons
primaryButton={_t("Done")}
onPrimaryButtonClick={this.props.onFinished}

View file

@ -15,14 +15,14 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import React, { createRef } from 'react';
import FileSaver from 'file-saver';
import React, { createRef } from "react";
import FileSaver from "file-saver";
import { IPreparedKeyBackupVersion } from "matrix-js-sdk/src/crypto/backup";
import { logger } from "matrix-js-sdk/src/logger";
import { MatrixClientPeg } from '../../../../MatrixClientPeg';
import { _t, _td } from '../../../../languageHandler';
import { accessSecretStorage } from '../../../../SecurityManager';
import { MatrixClientPeg } from "../../../../MatrixClientPeg";
import { _t, _td } from "../../../../languageHandler";
import { accessSecretStorage } from "../../../../SecurityManager";
import AccessibleButton from "../../../../components/views/elements/AccessibleButton";
import { copyNode } from "../../../../utils/strings";
import PassphraseField from "../../../../components/views/auth/PassphraseField";
@ -73,9 +73,9 @@ export default class CreateKeyBackupDialog extends React.PureComponent<IProps, I
this.state = {
secureSecretStorage: null,
phase: Phase.Passphrase,
passPhrase: '',
passPhrase: "",
passPhraseValid: false,
passPhraseConfirm: '',
passPhraseConfirm: "",
copied: false,
downloaded: false,
};
@ -106,9 +106,9 @@ export default class CreateKeyBackupDialog extends React.PureComponent<IProps, I
private onDownloadClick = (): void => {
const blob = new Blob([this.keyBackupInfo.recovery_key], {
type: 'text/plain;charset=us-ascii',
type: "text/plain;charset=us-ascii",
});
FileSaver.saveAs(blob, 'security-key.txt');
FileSaver.saveAs(blob, "security-key.txt");
this.setState({
downloaded: true,
@ -126,16 +126,13 @@ export default class CreateKeyBackupDialog extends React.PureComponent<IProps, I
try {
if (secureSecretStorage) {
await accessSecretStorage(async () => {
info = await MatrixClientPeg.get().prepareKeyBackupVersion(
null /* random key */,
{ secureSecretStorage: true },
);
info = await MatrixClientPeg.get().prepareKeyBackupVersion(null /* random key */, {
secureSecretStorage: true,
});
info = await MatrixClientPeg.get().createKeyBackupVersion(info);
});
} else {
info = await MatrixClientPeg.get().createKeyBackupVersion(
this.keyBackupInfo,
);
info = await MatrixClientPeg.get().createKeyBackupVersion(this.keyBackupInfo);
}
await MatrixClientPeg.get().scheduleAllGroupSessionsForBackup();
this.setState({
@ -206,9 +203,9 @@ export default class CreateKeyBackupDialog extends React.PureComponent<IProps, I
private onSetAgainClick = (): void => {
this.setState({
passPhrase: '',
passPhrase: "",
passPhraseValid: false,
passPhraseConfirm: '',
passPhraseConfirm: "",
phase: Phase.Passphrase,
});
};
@ -238,49 +235,56 @@ export default class CreateKeyBackupDialog extends React.PureComponent<IProps, I
};
private renderPhasePassPhrase(): JSX.Element {
return <form onSubmit={this.onPassPhraseNextClick}>
<p>{ _t(
"<b>Warning</b>: You should only set up key backup from a trusted computer.", {},
{ b: sub => <b>{ sub }</b> },
) }</p>
<p>{ _t(
"We'll store an encrypted copy of your keys on our server. " +
"Secure your backup with a Security Phrase.",
) }</p>
<p>{ _t("For maximum security, this should be different from your account password.") }</p>
return (
<form onSubmit={this.onPassPhraseNextClick}>
<p>
{_t(
"<b>Warning</b>: You should only set up key backup from a trusted computer.",
{},
{ b: (sub) => <b>{sub}</b> },
)}
</p>
<p>
{_t(
"We'll store an encrypted copy of your keys on our server. " +
"Secure your backup with a Security Phrase.",
)}
</p>
<p>{_t("For maximum security, this should be different from your account password.")}</p>
<div className="mx_CreateKeyBackupDialog_primaryContainer">
<div className="mx_CreateKeyBackupDialog_passPhraseContainer">
<PassphraseField
className="mx_CreateKeyBackupDialog_passPhraseInput"
onChange={this.onPassPhraseChange}
minScore={PASSWORD_MIN_SCORE}
value={this.state.passPhrase}
onValidate={this.onPassPhraseValidate}
fieldRef={this.passphraseField}
autoFocus={true}
label={_td("Enter a Security Phrase")}
labelEnterPassword={_td("Enter a Security Phrase")}
labelStrongPassword={_td("Great! This Security Phrase looks strong enough.")}
labelAllowedButUnsafe={_td("Great! This Security Phrase looks strong enough.")}
/>
<div className="mx_CreateKeyBackupDialog_primaryContainer">
<div className="mx_CreateKeyBackupDialog_passPhraseContainer">
<PassphraseField
className="mx_CreateKeyBackupDialog_passPhraseInput"
onChange={this.onPassPhraseChange}
minScore={PASSWORD_MIN_SCORE}
value={this.state.passPhrase}
onValidate={this.onPassPhraseValidate}
fieldRef={this.passphraseField}
autoFocus={true}
label={_td("Enter a Security Phrase")}
labelEnterPassword={_td("Enter a Security Phrase")}
labelStrongPassword={_td("Great! This Security Phrase looks strong enough.")}
labelAllowedButUnsafe={_td("Great! This Security Phrase looks strong enough.")}
/>
</div>
</div>
</div>
<DialogButtons
primaryButton={_t('Next')}
onPrimaryButtonClick={this.onPassPhraseNextClick}
hasCancel={false}
disabled={!this.state.passPhraseValid}
/>
<DialogButtons
primaryButton={_t("Next")}
onPrimaryButtonClick={this.onPassPhraseNextClick}
hasCancel={false}
disabled={!this.state.passPhraseValid}
/>
<details>
<summary>{ _t("Advanced") }</summary>
<AccessibleButton kind='primary' onClick={this.onSkipPassPhraseClick}>
{ _t("Set up with a Security Key") }
</AccessibleButton>
</details>
</form>;
<details>
<summary>{_t("Advanced")}</summary>
<AccessibleButton kind="primary" onClick={this.onSkipPassPhraseClick}>
{_t("Set up with a Security Key")}
</AccessibleButton>
</details>
</form>
);
}
private renderPhasePassPhraseConfirm(): JSX.Element {
@ -303,68 +307,71 @@ export default class CreateKeyBackupDialog extends React.PureComponent<IProps, I
let passPhraseMatch = null;
if (matchText) {
passPhraseMatch = <div className="mx_CreateKeyBackupDialog_passPhraseMatch">
<div>{ matchText }</div>
<AccessibleButton kind="link" onClick={this.onSetAgainClick}>
{ changeText }
</AccessibleButton>
</div>;
}
return <form onSubmit={this.onPassPhraseConfirmNextClick}>
<p>{ _t(
"Enter your Security Phrase a second time to confirm it.",
) }</p>
<div className="mx_CreateKeyBackupDialog_primaryContainer">
<div className="mx_CreateKeyBackupDialog_passPhraseContainer">
<div>
<input type="password"
onChange={this.onPassPhraseConfirmChange}
value={this.state.passPhraseConfirm}
className="mx_CreateKeyBackupDialog_passPhraseInput"
placeholder={_t("Repeat your Security Phrase...")}
autoFocus={true}
/>
</div>
{ passPhraseMatch }
passPhraseMatch = (
<div className="mx_CreateKeyBackupDialog_passPhraseMatch">
<div>{matchText}</div>
<AccessibleButton kind="link" onClick={this.onSetAgainClick}>
{changeText}
</AccessibleButton>
</div>
</div>
<DialogButtons
primaryButton={_t('Next')}
onPrimaryButtonClick={this.onPassPhraseConfirmNextClick}
hasCancel={false}
disabled={this.state.passPhrase !== this.state.passPhraseConfirm}
/>
</form>;
);
}
return (
<form onSubmit={this.onPassPhraseConfirmNextClick}>
<p>{_t("Enter your Security Phrase a second time to confirm it.")}</p>
<div className="mx_CreateKeyBackupDialog_primaryContainer">
<div className="mx_CreateKeyBackupDialog_passPhraseContainer">
<div>
<input
type="password"
onChange={this.onPassPhraseConfirmChange}
value={this.state.passPhraseConfirm}
className="mx_CreateKeyBackupDialog_passPhraseInput"
placeholder={_t("Repeat your Security Phrase...")}
autoFocus={true}
/>
</div>
{passPhraseMatch}
</div>
</div>
<DialogButtons
primaryButton={_t("Next")}
onPrimaryButtonClick={this.onPassPhraseConfirmNextClick}
hasCancel={false}
disabled={this.state.passPhrase !== this.state.passPhraseConfirm}
/>
</form>
);
}
private renderPhaseShowKey(): JSX.Element {
return <div>
<p>{ _t(
"Your Security Key is a safety net - you can use it to restore " +
"access to your encrypted messages if you forget your Security Phrase.",
) }</p>
<p>{ _t(
"Keep a copy of it somewhere secure, like a password manager or even a safe.",
) }</p>
<div className="mx_CreateKeyBackupDialog_primaryContainer">
<div className="mx_CreateKeyBackupDialog_recoveryKeyHeader">
{ _t("Your Security Key") }
</div>
<div className="mx_CreateKeyBackupDialog_recoveryKeyContainer">
<div className="mx_CreateKeyBackupDialog_recoveryKey">
<code ref={this.recoveryKeyNode}>{ this.keyBackupInfo.recovery_key }</code>
</div>
<div className="mx_CreateKeyBackupDialog_recoveryKeyButtons">
<button className="mx_Dialog_primary" onClick={this.onCopyClick}>
{ _t("Copy") }
</button>
<button className="mx_Dialog_primary" onClick={this.onDownloadClick}>
{ _t("Download") }
</button>
return (
<div>
<p>
{_t(
"Your Security Key is a safety net - you can use it to restore " +
"access to your encrypted messages if you forget your Security Phrase.",
)}
</p>
<p>{_t("Keep a copy of it somewhere secure, like a password manager or even a safe.")}</p>
<div className="mx_CreateKeyBackupDialog_primaryContainer">
<div className="mx_CreateKeyBackupDialog_recoveryKeyHeader">{_t("Your Security Key")}</div>
<div className="mx_CreateKeyBackupDialog_recoveryKeyContainer">
<div className="mx_CreateKeyBackupDialog_recoveryKey">
<code ref={this.recoveryKeyNode}>{this.keyBackupInfo.recovery_key}</code>
</div>
<div className="mx_CreateKeyBackupDialog_recoveryKeyButtons">
<button className="mx_Dialog_primary" onClick={this.onCopyClick}>
{_t("Copy")}
</button>
<button className="mx_Dialog_primary" onClick={this.onDownloadClick}>
{_t("Download")}
</button>
</div>
</div>
</div>
</div>
</div>;
);
}
private renderPhaseKeepItSafe(): JSX.Element {
@ -372,77 +379,81 @@ export default class CreateKeyBackupDialog extends React.PureComponent<IProps, I
if (this.state.copied) {
introText = _t(
"Your Security Key has been <b>copied to your clipboard</b>, paste it to:",
{}, { b: s => <b>{ s }</b> },
{},
{ b: (s) => <b>{s}</b> },
);
} else if (this.state.downloaded) {
introText = _t(
"Your Security Key is in your <b>Downloads</b> folder.",
{}, { b: s => <b>{ s }</b> },
);
introText = _t("Your Security Key is in your <b>Downloads</b> folder.", {}, { b: (s) => <b>{s}</b> });
}
return <div>
{ introText }
<ul>
<li>{ _t("<b>Print it</b> and store it somewhere safe", {}, { b: s => <b>{ s }</b> }) }</li>
<li>{ _t("<b>Save it</b> on a USB key or backup drive", {}, { b: s => <b>{ s }</b> }) }</li>
<li>{ _t("<b>Copy it</b> to your personal cloud storage", {}, { b: s => <b>{ s }</b> }) }</li>
</ul>
<DialogButtons primaryButton={_t("Continue")}
onPrimaryButtonClick={this.createBackup}
hasCancel={false}>
<button onClick={this.onKeepItSafeBackClick}>{ _t("Back") }</button>
</DialogButtons>
</div>;
return (
<div>
{introText}
<ul>
<li>{_t("<b>Print it</b> and store it somewhere safe", {}, { b: (s) => <b>{s}</b> })}</li>
<li>{_t("<b>Save it</b> on a USB key or backup drive", {}, { b: (s) => <b>{s}</b> })}</li>
<li>{_t("<b>Copy it</b> to your personal cloud storage", {}, { b: (s) => <b>{s}</b> })}</li>
</ul>
<DialogButtons
primaryButton={_t("Continue")}
onPrimaryButtonClick={this.createBackup}
hasCancel={false}
>
<button onClick={this.onKeepItSafeBackClick}>{_t("Back")}</button>
</DialogButtons>
</div>
);
}
private renderBusyPhase(): JSX.Element {
return <div>
<Spinner />
</div>;
return (
<div>
<Spinner />
</div>
);
}
private renderPhaseDone(): JSX.Element {
return <div>
<p>{ _t(
"Your keys are being backed up (the first backup could take a few minutes).",
) }</p>
<DialogButtons primaryButton={_t('OK')}
onPrimaryButtonClick={this.onDone}
hasCancel={false}
/>
</div>;
return (
<div>
<p>{_t("Your keys are being backed up (the first backup could take a few minutes).")}</p>
<DialogButtons primaryButton={_t("OK")} onPrimaryButtonClick={this.onDone} hasCancel={false} />
</div>
);
}
private renderPhaseOptOutConfirm(): JSX.Element {
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.",
) }
<DialogButtons primaryButton={_t('Set up Secure Message Recovery')}
onPrimaryButtonClick={this.onSetUpClick}
hasCancel={false}
>
<button onClick={this.onCancel}>I understand, continue without</button>
</DialogButtons>
</div>;
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.",
)}
<DialogButtons
primaryButton={_t("Set up Secure Message Recovery")}
onPrimaryButtonClick={this.onSetUpClick}
hasCancel={false}
>
<button onClick={this.onCancel}>I understand, continue without</button>
</DialogButtons>
</div>
);
}
private titleForPhase(phase: Phase): string {
switch (phase) {
case Phase.Passphrase:
return _t('Secure your backup with a Security Phrase');
return _t("Secure your backup with a Security Phrase");
case Phase.PassphraseConfirm:
return _t('Confirm your Security Phrase');
return _t("Confirm your Security Phrase");
case Phase.OptOutConfirm:
return _t('Warning!');
return _t("Warning!");
case Phase.ShowKey:
case Phase.KeepItSafe:
return _t('Make a copy of your Security Key');
return _t("Make a copy of your Security Key");
case Phase.BackingUp:
return _t('Starting backup...');
return _t("Starting backup...");
case Phase.Done:
return _t('Success!');
return _t("Success!");
default:
return _t("Create key backup");
}
@ -451,15 +462,17 @@ export default class CreateKeyBackupDialog extends React.PureComponent<IProps, I
public render(): JSX.Element {
let content;
if (this.state.error) {
content = <div>
<p>{ _t("Unable to create key backup") }</p>
<DialogButtons
primaryButton={_t('Retry')}
onPrimaryButtonClick={this.createBackup}
hasCancel={true}
onCancel={this.onCancel}
/>
</div>;
content = (
<div>
<p>{_t("Unable to create key backup")}</p>
<DialogButtons
primaryButton={_t("Retry")}
onPrimaryButtonClick={this.createBackup}
hasCancel={true}
onCancel={this.onCancel}
/>
</div>
);
} else {
switch (this.state.phase) {
case Phase.Passphrase:
@ -487,14 +500,13 @@ export default class CreateKeyBackupDialog extends React.PureComponent<IProps, I
}
return (
<BaseDialog className='mx_CreateKeyBackupDialog'
<BaseDialog
className="mx_CreateKeyBackupDialog"
onFinished={this.props.onFinished}
title={this.titleForPhase(this.state.phase)}
hasCancel={[Phase.Passphrase, Phase.Done].includes(this.state.phase)}
>
<div>
{ content }
</div>
<div>{content}</div>
</BaseDialog>
);
}

View file

@ -15,8 +15,8 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import React, { createRef } from 'react';
import FileSaver from 'file-saver';
import React, { createRef } from "react";
import FileSaver from "file-saver";
import { logger } from "matrix-js-sdk/src/logger";
import { IKeyBackupInfo } from "matrix-js-sdk/src/crypto/keybackup";
import { TrustInfo } from "matrix-js-sdk/src/crypto/backup";
@ -24,14 +24,14 @@ import { CrossSigningKeys } from "matrix-js-sdk/src/matrix";
import { IRecoveryKey } from "matrix-js-sdk/src/crypto/api";
import { CryptoEvent } from "matrix-js-sdk/src/crypto";
import { MatrixClientPeg } from '../../../../MatrixClientPeg';
import { _t, _td } from '../../../../languageHandler';
import Modal from '../../../../Modal';
import { promptForBackupPassphrase } from '../../../../SecurityManager';
import { MatrixClientPeg } from "../../../../MatrixClientPeg";
import { _t, _td } from "../../../../languageHandler";
import Modal from "../../../../Modal";
import { promptForBackupPassphrase } from "../../../../SecurityManager";
import { copyNode } from "../../../../utils/strings";
import { SSOAuthEntry } from "../../../../components/views/auth/InteractiveAuthEntryComponents";
import PassphraseField from "../../../../components/views/auth/PassphraseField";
import StyledRadioButton from '../../../../components/views/elements/StyledRadioButton';
import StyledRadioButton from "../../../../components/views/elements/StyledRadioButton";
import AccessibleButton from "../../../../components/views/elements/AccessibleButton";
import DialogButtons from "../../../../components/views/elements/DialogButtons";
import InlineSpinner from "../../../../components/views/elements/InlineSpinner";
@ -40,7 +40,7 @@ import {
getSecureBackupSetupMethods,
isSecureBackupRequired,
SecureBackupSetupMethod,
} from '../../../../utils/WellKnownUtils';
} from "../../../../utils/WellKnownUtils";
import SecurityCustomisations from "../../../../customisations/Security";
import { IDialogProps } from "../../../../components/views/dialogs/IDialogProps";
import Field from "../../../../components/views/elements/Field";
@ -129,9 +129,9 @@ export default class CreateSecretStorageDialog extends React.PureComponent<IProp
this.state = {
phase: Phase.Loading,
passPhrase: '',
passPhrase: "",
passPhraseValid: false,
passPhraseConfirm: '',
passPhraseConfirm: "",
copied: false,
downloaded: false,
setPassphrase: false,
@ -169,16 +169,15 @@ export default class CreateSecretStorageDialog extends React.PureComponent<IProp
this.fetchBackupInfo();
}
private async fetchBackupInfo(): Promise<{ backupInfo: IKeyBackupInfo, backupSigStatus: TrustInfo }> {
private async fetchBackupInfo(): Promise<{ backupInfo: IKeyBackupInfo; backupSigStatus: TrustInfo }> {
try {
const backupInfo = await MatrixClientPeg.get().getKeyBackupVersion();
const backupSigStatus = (
const backupSigStatus =
// we may not have started crypto yet, in which case we definitely don't trust the backup
MatrixClientPeg.get().isCryptoEnabled() && (await MatrixClientPeg.get().isKeyBackupTrusted(backupInfo))
);
MatrixClientPeg.get().isCryptoEnabled() && (await MatrixClientPeg.get().isKeyBackupTrusted(backupInfo));
const { forceReset } = this.props;
const phase = (backupInfo && !forceReset) ? Phase.Migrate : Phase.ChooseKeyPassphrase;
const phase = backupInfo && !forceReset ? Phase.Migrate : Phase.ChooseKeyPassphrase;
this.setState({
phase,
@ -207,8 +206,8 @@ export default class CreateSecretStorageDialog extends React.PureComponent<IProp
logger.log("uploadDeviceSigningKeys advertised no flows!");
return;
}
const canUploadKeysWithPasswordOnly = error.data.flows.some(f => {
return f.stages.length === 1 && f.stages[0] === 'm.login.password';
const canUploadKeysWithPasswordOnly = error.data.flows.some((f) => {
return f.stages.length === 1 && f.stages[0] === "m.login.password";
});
this.setState({
canUploadKeysWithPasswordOnly,
@ -228,8 +227,7 @@ export default class CreateSecretStorageDialog extends React.PureComponent<IProp
private onChooseKeyPassphraseFormSubmit = async (): Promise<void> => {
if (this.state.passPhraseKeySelected === SecureBackupSetupMethod.Key) {
this.recoveryKey =
await MatrixClientPeg.get().createRecoveryKeyFromPassphrase();
this.recoveryKey = await MatrixClientPeg.get().createRecoveryKeyFromPassphrase();
this.setState({
copied: false,
downloaded: false,
@ -265,9 +263,9 @@ export default class CreateSecretStorageDialog extends React.PureComponent<IProp
private onDownloadClick = (): void => {
const blob = new Blob([this.recoveryKey.encodedPrivateKey], {
type: 'text/plain;charset=us-ascii',
type: "text/plain;charset=us-ascii",
});
FileSaver.saveAs(blob, 'security-key.txt');
FileSaver.saveAs(blob, "security-key.txt");
this.setState({
downloaded: true,
@ -277,9 +275,9 @@ export default class CreateSecretStorageDialog extends React.PureComponent<IProp
private doBootstrapUIAuth = async (makeRequest: (authData: any) => Promise<{}>): Promise<void> => {
if (this.state.canUploadKeysWithPasswordOnly && this.state.accountPassword) {
await makeRequest({
type: 'm.login.password',
type: "m.login.password",
identifier: {
type: 'm.id.user',
type: "m.id.user",
user: MatrixClientPeg.get().getUserId(),
},
// TODO: Remove `user` once servers support proper UIA
@ -367,7 +365,7 @@ export default class CreateSecretStorageDialog extends React.PureComponent<IProp
} catch (e) {
if (this.state.canUploadKeysWithPasswordOnly && e.httpStatus === 401 && e.data.flows) {
this.setState({
accountPassword: '',
accountPassword: "",
accountPasswordCorrect: false,
phase: Phase.Migrate,
});
@ -385,20 +383,22 @@ export default class CreateSecretStorageDialog extends React.PureComponent<IProp
private restoreBackup = async (): Promise<void> => {
// It's possible we'll need the backup key later on for bootstrapping,
// so let's stash it here, rather than prompting for it twice.
const keyCallback = k => this.backupKey = k;
const keyCallback = (k) => (this.backupKey = k);
const { finished } = Modal.createDialog(RestoreKeyBackupDialog, {
showSummary: false,
keyCallback,
}, null, /* priority = */ false, /* static = */ false);
const { finished } = Modal.createDialog(
RestoreKeyBackupDialog,
{
showSummary: false,
keyCallback,
},
null,
/* priority = */ false,
/* static = */ false,
);
await finished;
const { backupSigStatus } = await this.fetchBackupInfo();
if (
backupSigStatus.usable &&
this.state.canUploadKeysWithPasswordOnly &&
this.state.accountPassword
) {
if (backupSigStatus.usable && this.state.canUploadKeysWithPasswordOnly && this.state.accountPassword) {
this.bootstrapSecretStorage();
}
};
@ -439,8 +439,7 @@ export default class CreateSecretStorageDialog extends React.PureComponent<IProp
if (this.state.passPhrase !== this.state.passPhraseConfirm) return;
this.recoveryKey =
await MatrixClientPeg.get().createRecoveryKeyFromPassphrase(this.state.passPhrase);
this.recoveryKey = await MatrixClientPeg.get().createRecoveryKeyFromPassphrase(this.state.passPhrase);
this.setState({
copied: false,
downloaded: false,
@ -451,9 +450,9 @@ export default class CreateSecretStorageDialog extends React.PureComponent<IProp
private onSetAgainClick = (): void => {
this.setState({
passPhrase: '',
passPhrase: "",
passPhraseValid: false,
passPhraseConfirm: '',
passPhraseConfirm: "",
phase: Phase.Passphrase,
});
};
@ -494,9 +493,13 @@ 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 Security 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 Security Key for you to store somewhere safe, like a password manager or a safe.") }</div>
</StyledRadioButton>
);
}
@ -513,9 +516,11 @@ export default class CreateSecretStorageDialog extends React.PureComponent<IProp
>
<div className="mx_CreateSecretStorageDialog_optionTitle">
<span className="mx_CreateSecretStorageDialog_optionIcon mx_CreateSecretStorageDialog_optionIcon_securePhrase" />
{ _t("Enter a Security Phrase") }
{_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 Security Key to use for backup.") }</div>
</StyledRadioButton>
);
}
@ -527,22 +532,26 @@ export default class CreateSecretStorageDialog extends React.PureComponent<IProp
? this.renderOptionPassphrase()
: null;
return <form onSubmit={this.onChooseKeyPassphraseFormSubmit}>
<p className="mx_CreateSecretStorageDialog_centeredBody">{ _t(
"Safeguard against losing access to encrypted messages & data by " +
"backing up encryption keys on your server.",
) }</p>
<div className="mx_CreateSecretStorageDialog_primaryContainer" role="radiogroup">
{ optionKey }
{ optionPassphrase }
</div>
<DialogButtons
primaryButton={_t("Continue")}
onPrimaryButtonClick={this.onChooseKeyPassphraseFormSubmit}
onCancel={this.onCancelClick}
hasCancel={this.state.canSkip}
/>
</form>;
return (
<form onSubmit={this.onChooseKeyPassphraseFormSubmit}>
<p className="mx_CreateSecretStorageDialog_centeredBody">
{_t(
"Safeguard against losing access to encrypted messages & data by " +
"backing up encryption keys on your server.",
)}
</p>
<div className="mx_CreateSecretStorageDialog_primaryContainer" role="radiogroup">
{optionKey}
{optionPassphrase}
</div>
<DialogButtons
primaryButton={_t("Continue")}
onPrimaryButtonClick={this.onChooseKeyPassphraseFormSubmit}
onCancel={this.onCancelClick}
hasCancel={this.state.canSkip}
/>
</form>
);
}
private renderPhaseMigrate(): JSX.Element {
@ -555,83 +564,94 @@ export default class CreateSecretStorageDialog extends React.PureComponent<IProp
let authPrompt;
let nextCaption = _t("Next");
if (this.state.canUploadKeysWithPasswordOnly) {
authPrompt = <div>
<div>{ _t("Enter your account password to confirm the upgrade:") }</div>
<div><Field
type="password"
label={_t("Password")}
value={this.state.accountPassword}
onChange={this.onAccountPasswordChange}
forceValidity={this.state.accountPasswordCorrect === false ? false : null}
autoFocus={true}
/></div>
</div>;
authPrompt = (
<div>
<div>{_t("Enter your account password to confirm the upgrade:")}</div>
<div>
<Field
type="password"
label={_t("Password")}
value={this.state.accountPassword}
onChange={this.onAccountPasswordChange}
forceValidity={this.state.accountPasswordCorrect === false ? false : null}
autoFocus={true}
/>
</div>
</div>
);
} else if (!this.state.backupSigStatus.usable) {
authPrompt = <div>
<div>{ _t("Restore your key backup to upgrade your encryption") }</div>
</div>;
authPrompt = (
<div>
<div>{_t("Restore your key backup to upgrade your encryption")}</div>
</div>
);
nextCaption = _t("Restore");
} else {
authPrompt = <p>
{ _t("You'll need to authenticate with the server to confirm the upgrade.") }
</p>;
authPrompt = <p>{_t("You'll need to authenticate with the server to confirm the upgrade.")}</p>;
}
return <form onSubmit={this.onMigrateFormSubmit}>
<p>{ _t(
"Upgrade this session to allow it to verify other sessions, " +
"granting them access to encrypted messages and marking them " +
"as trusted for other users.",
) }</p>
<div>{ authPrompt }</div>
<DialogButtons
primaryButton={nextCaption}
onPrimaryButtonClick={this.onMigrateFormSubmit}
hasCancel={false}
primaryDisabled={this.state.canUploadKeysWithPasswordOnly && !this.state.accountPassword}
>
<button type="button" className="danger" onClick={this.onCancelClick}>
{ _t('Skip') }
</button>
</DialogButtons>
</form>;
return (
<form onSubmit={this.onMigrateFormSubmit}>
<p>
{_t(
"Upgrade this session to allow it to verify other sessions, " +
"granting them access to encrypted messages and marking them " +
"as trusted for other users.",
)}
</p>
<div>{authPrompt}</div>
<DialogButtons
primaryButton={nextCaption}
onPrimaryButtonClick={this.onMigrateFormSubmit}
hasCancel={false}
primaryDisabled={this.state.canUploadKeysWithPasswordOnly && !this.state.accountPassword}
>
<button type="button" className="danger" onClick={this.onCancelClick}>
{_t("Skip")}
</button>
</DialogButtons>
</form>
);
}
private renderPhasePassPhrase(): JSX.Element {
return <form onSubmit={this.onPassPhraseNextClick}>
<p>{ _t(
"Enter a security phrase only you know, as it's used to safeguard your data. " +
"To be secure, you shouldn't re-use your account password.",
) }</p>
return (
<form onSubmit={this.onPassPhraseNextClick}>
<p>
{_t(
"Enter a security phrase only you know, as it's used to safeguard your data. " +
"To be secure, you shouldn't re-use your account password.",
)}
</p>
<div className="mx_CreateSecretStorageDialog_passPhraseContainer">
<PassphraseField
className="mx_CreateSecretStorageDialog_passPhraseField"
onChange={this.onPassPhraseChange}
minScore={PASSWORD_MIN_SCORE}
value={this.state.passPhrase}
onValidate={this.onPassPhraseValidate}
fieldRef={this.passphraseField}
autoFocus={true}
label={_td("Enter a Security Phrase")}
labelEnterPassword={_td("Enter a Security Phrase")}
labelStrongPassword={_td("Great! This Security Phrase looks strong enough.")}
labelAllowedButUnsafe={_td("Great! This Security Phrase looks strong enough.")}
/>
</div>
<div className="mx_CreateSecretStorageDialog_passPhraseContainer">
<PassphraseField
className="mx_CreateSecretStorageDialog_passPhraseField"
onChange={this.onPassPhraseChange}
minScore={PASSWORD_MIN_SCORE}
value={this.state.passPhrase}
onValidate={this.onPassPhraseValidate}
fieldRef={this.passphraseField}
autoFocus={true}
label={_td("Enter a Security Phrase")}
labelEnterPassword={_td("Enter a Security Phrase")}
labelStrongPassword={_td("Great! This Security Phrase looks strong enough.")}
labelAllowedButUnsafe={_td("Great! This Security Phrase looks strong enough.")}
/>
</div>
<DialogButtons
primaryButton={_t('Continue')}
onPrimaryButtonClick={this.onPassPhraseNextClick}
hasCancel={false}
disabled={!this.state.passPhraseValid}
>
<button type="button"
onClick={this.onCancelClick}
className="danger"
>{ _t("Cancel") }</button>
</DialogButtons>
</form>;
<DialogButtons
primaryButton={_t("Continue")}
onPrimaryButtonClick={this.onPassPhraseNextClick}
hasCancel={false}
disabled={!this.state.passPhraseValid}
>
<button type="button" onClick={this.onCancelClick} className="danger">
{_t("Cancel")}
</button>
</DialogButtons>
</form>
);
}
private renderPhasePassPhraseConfirm(): JSX.Element {
@ -654,166 +674,188 @@ export default class CreateSecretStorageDialog extends React.PureComponent<IProp
let passPhraseMatch = null;
if (matchText) {
passPhraseMatch = <div>
<div>{ matchText }</div>
<AccessibleButton kind="link" onClick={this.onSetAgainClick}>
{ changeText }
</AccessibleButton>
</div>;
}
return <form onSubmit={this.onPassPhraseConfirmNextClick}>
<p>{ _t(
"Enter your Security Phrase a second time to confirm it.",
) }</p>
<div className="mx_CreateSecretStorageDialog_passPhraseContainer">
<Field
type="password"
onChange={this.onPassPhraseConfirmChange}
value={this.state.passPhraseConfirm}
className="mx_CreateSecretStorageDialog_passPhraseField"
label={_t("Confirm your Security Phrase")}
autoFocus={true}
autoComplete="new-password"
/>
<div className="mx_CreateSecretStorageDialog_passPhraseMatch">
{ passPhraseMatch }
passPhraseMatch = (
<div>
<div>{matchText}</div>
<AccessibleButton kind="link" onClick={this.onSetAgainClick}>
{changeText}
</AccessibleButton>
</div>
</div>
<DialogButtons
primaryButton={_t('Continue')}
onPrimaryButtonClick={this.onPassPhraseConfirmNextClick}
hasCancel={false}
disabled={this.state.passPhrase !== this.state.passPhraseConfirm}
>
<button type="button"
onClick={this.onCancelClick}
className="danger"
>{ _t("Skip") }</button>
</DialogButtons>
</form>;
);
}
return (
<form onSubmit={this.onPassPhraseConfirmNextClick}>
<p>{_t("Enter your Security Phrase a second time to confirm it.")}</p>
<div className="mx_CreateSecretStorageDialog_passPhraseContainer">
<Field
type="password"
onChange={this.onPassPhraseConfirmChange}
value={this.state.passPhraseConfirm}
className="mx_CreateSecretStorageDialog_passPhraseField"
label={_t("Confirm your Security Phrase")}
autoFocus={true}
autoComplete="new-password"
/>
<div className="mx_CreateSecretStorageDialog_passPhraseMatch">{passPhraseMatch}</div>
</div>
<DialogButtons
primaryButton={_t("Continue")}
onPrimaryButtonClick={this.onPassPhraseConfirmNextClick}
hasCancel={false}
disabled={this.state.passPhrase !== this.state.passPhraseConfirm}
>
<button type="button" onClick={this.onCancelClick} className="danger">
{_t("Skip")}
</button>
</DialogButtons>
</form>
);
}
private renderPhaseShowKey(): JSX.Element {
let continueButton;
if (this.state.phase === Phase.ShowKey) {
continueButton = <DialogButtons primaryButton={_t("Continue")}
disabled={!this.state.downloaded && !this.state.copied && !this.state.setPassphrase}
onPrimaryButtonClick={this.onShowKeyContinueClick}
hasCancel={false}
/>;
continueButton = (
<DialogButtons
primaryButton={_t("Continue")}
disabled={!this.state.downloaded && !this.state.copied && !this.state.setPassphrase}
onPrimaryButtonClick={this.onShowKeyContinueClick}
hasCancel={false}
/>
);
} else {
continueButton = <div className="mx_CreateSecretStorageDialog_continueSpinner">
<InlineSpinner />
</div>;
continueButton = (
<div className="mx_CreateSecretStorageDialog_continueSpinner">
<InlineSpinner />
</div>
);
}
return <div>
<p>{ _t(
"Store your Security 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">
<div className="mx_CreateSecretStorageDialog_recoveryKeyContainer">
<div className="mx_CreateSecretStorageDialog_recoveryKey">
<code ref={this.recoveryKeyNode}>{ this.recoveryKey.encodedPrivateKey }</code>
</div>
<div className="mx_CreateSecretStorageDialog_recoveryKeyButtons">
<AccessibleButton kind='primary'
className="mx_Dialog_primary"
onClick={this.onDownloadClick}
disabled={this.state.phase === Phase.Storing}
>
{ _t("Download") }
</AccessibleButton>
<span>{ _t("%(downloadButton)s or %(copyButton)s", {
downloadButton: "",
copyButton: "",
}) }</span>
<AccessibleButton
kind='primary'
className="mx_Dialog_primary mx_CreateSecretStorageDialog_recoveryKeyButtons_copyBtn"
onClick={this.onCopyClick}
disabled={this.state.phase === Phase.Storing}
>
{ this.state.copied ? _t("Copied!") : _t("Copy") }
</AccessibleButton>
return (
<div>
<p>
{_t(
"Store your Security 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">
<div className="mx_CreateSecretStorageDialog_recoveryKeyContainer">
<div className="mx_CreateSecretStorageDialog_recoveryKey">
<code ref={this.recoveryKeyNode}>{this.recoveryKey.encodedPrivateKey}</code>
</div>
<div className="mx_CreateSecretStorageDialog_recoveryKeyButtons">
<AccessibleButton
kind="primary"
className="mx_Dialog_primary"
onClick={this.onDownloadClick}
disabled={this.state.phase === Phase.Storing}
>
{_t("Download")}
</AccessibleButton>
<span>
{_t("%(downloadButton)s or %(copyButton)s", {
downloadButton: "",
copyButton: "",
})}
</span>
<AccessibleButton
kind="primary"
className="mx_Dialog_primary mx_CreateSecretStorageDialog_recoveryKeyButtons_copyBtn"
onClick={this.onCopyClick}
disabled={this.state.phase === Phase.Storing}
>
{this.state.copied ? _t("Copied!") : _t("Copy")}
</AccessibleButton>
</div>
</div>
</div>
{continueButton}
</div>
{ continueButton }
</div>;
);
}
private renderBusyPhase(): JSX.Element {
return <div>
<Spinner />
</div>;
return (
<div>
<Spinner />
</div>
);
}
private renderPhaseLoadError(): JSX.Element {
return <div>
<p>{ _t("Unable to query secret storage status") }</p>
<div className="mx_Dialog_buttons">
<DialogButtons primaryButton={_t('Retry')}
onPrimaryButtonClick={this.onLoadRetryClick}
hasCancel={this.state.canSkip}
onCancel={this.onCancel}
/>
return (
<div>
<p>{_t("Unable to query secret storage status")}</p>
<div className="mx_Dialog_buttons">
<DialogButtons
primaryButton={_t("Retry")}
onPrimaryButtonClick={this.onLoadRetryClick}
hasCancel={this.state.canSkip}
onCancel={this.onCancel}
/>
</div>
</div>
</div>;
);
}
private renderPhaseSkipConfirm(): JSX.Element {
return <div>
<p>{ _t(
"If you cancel now, you may lose encrypted messages & data if you lose access to your logins.",
) }</p>
<p>{ _t(
"You can also set up Secure Backup & manage your keys in Settings.",
) }</p>
<DialogButtons primaryButton={_t('Go back')}
onPrimaryButtonClick={this.onGoBackClick}
hasCancel={false}
>
<button type="button" className="danger" onClick={this.onCancel}>{ _t('Cancel') }</button>
</DialogButtons>
</div>;
return (
<div>
<p>
{_t("If you cancel now, you may lose encrypted messages & data if you lose access to your logins.")}
</p>
<p>{_t("You can also set up Secure Backup & manage your keys in Settings.")}</p>
<DialogButtons
primaryButton={_t("Go back")}
onPrimaryButtonClick={this.onGoBackClick}
hasCancel={false}
>
<button type="button" className="danger" onClick={this.onCancel}>
{_t("Cancel")}
</button>
</DialogButtons>
</div>
);
}
private titleForPhase(phase: Phase): string {
switch (phase) {
case Phase.ChooseKeyPassphrase:
return _t('Set up Secure Backup');
return _t("Set up Secure Backup");
case Phase.Migrate:
return _t('Upgrade your encryption');
return _t("Upgrade your encryption");
case Phase.Passphrase:
return _t('Set a Security Phrase');
return _t("Set a Security Phrase");
case Phase.PassphraseConfirm:
return _t('Confirm Security Phrase');
return _t("Confirm Security Phrase");
case Phase.ConfirmSkip:
return _t('Are you sure?');
return _t("Are you sure?");
case Phase.ShowKey:
return _t('Save your Security Key');
return _t("Save your Security Key");
case Phase.Storing:
return _t('Setting up keys');
return _t("Setting up keys");
default:
return '';
return "";
}
}
public render(): JSX.Element {
let content;
if (this.state.error) {
content = <div>
<p>{ _t("Unable to set up secret storage") }</p>
<div className="mx_Dialog_buttons">
<DialogButtons primaryButton={_t('Retry')}
onPrimaryButtonClick={this.bootstrapSecretStorage}
hasCancel={this.state.canSkip}
onCancel={this.onCancel}
/>
content = (
<div>
<p>{_t("Unable to set up secret storage")}</p>
<div className="mx_Dialog_buttons">
<DialogButtons
primaryButton={_t("Retry")}
onPrimaryButtonClick={this.bootstrapSecretStorage}
hasCancel={this.state.canSkip}
onCancel={this.onCancel}
/>
</div>
</div>
</div>;
);
} else {
switch (this.state.phase) {
case Phase.Loading:
@ -851,32 +893,31 @@ export default class CreateSecretStorageDialog extends React.PureComponent<IProp
case Phase.Passphrase:
case Phase.PassphraseConfirm:
titleClass = [
'mx_CreateSecretStorageDialog_titleWithIcon',
'mx_CreateSecretStorageDialog_securePhraseTitle',
"mx_CreateSecretStorageDialog_titleWithIcon",
"mx_CreateSecretStorageDialog_securePhraseTitle",
];
break;
case Phase.ShowKey:
titleClass = [
'mx_CreateSecretStorageDialog_titleWithIcon',
'mx_CreateSecretStorageDialog_secureBackupTitle',
"mx_CreateSecretStorageDialog_titleWithIcon",
"mx_CreateSecretStorageDialog_secureBackupTitle",
];
break;
case Phase.ChooseKeyPassphrase:
titleClass = 'mx_CreateSecretStorageDialog_centeredTitle';
titleClass = "mx_CreateSecretStorageDialog_centeredTitle";
break;
}
return (
<BaseDialog className='mx_CreateSecretStorageDialog'
<BaseDialog
className="mx_CreateSecretStorageDialog"
onFinished={this.props.onFinished}
title={this.titleForPhase(this.state.phase)}
titleClass={titleClass}
hasCancel={this.props.hasCancel && [Phase.Passphrase].includes(this.state.phase)}
fixedWidth={false}
>
<div>
{ content }
</div>
<div>{content}</div>
</BaseDialog>
);
}

View file

@ -15,13 +15,13 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import FileSaver from 'file-saver';
import React from 'react';
import { MatrixClient } from 'matrix-js-sdk/src/client';
import FileSaver from "file-saver";
import React from "react";
import { MatrixClient } from "matrix-js-sdk/src/client";
import { logger } from "matrix-js-sdk/src/logger";
import { _t } from '../../../../languageHandler';
import * as MegolmExportEncryption from '../../../../utils/MegolmExportEncryption';
import { _t } from "../../../../languageHandler";
import * as MegolmExportEncryption from "../../../../utils/MegolmExportEncryption";
import { IDialogProps } from "../../../../components/views/dialogs/IDialogProps";
import BaseDialog from "../../../../components/views/dialogs/BaseDialog";
import Field from "../../../../components/views/elements/Field";
@ -68,11 +68,11 @@ export default class ExportE2eKeysDialog extends React.Component<IProps, IState>
const passphrase = this.state.passphrase1;
if (passphrase !== this.state.passphrase2) {
this.setState({ errStr: _t('Passphrases must match') });
this.setState({ errStr: _t("Passphrases must match") });
return false;
}
if (!passphrase) {
this.setState({ errStr: _t('Passphrase must not be empty') });
this.setState({ errStr: _t("Passphrase must not be empty") });
return false;
}
@ -83,29 +83,31 @@ export default class ExportE2eKeysDialog extends React.Component<IProps, IState>
private startExport(passphrase: string): void {
// extra Promise.resolve() to turn synchronous exceptions into
// asynchronous ones.
Promise.resolve().then(() => {
return this.props.matrixClient.exportRoomKeys();
}).then((k) => {
return MegolmExportEncryption.encryptMegolmKeyFile(
JSON.stringify(k), passphrase,
);
}).then((f) => {
const blob = new Blob([f], {
type: 'text/plain;charset=us-ascii',
Promise.resolve()
.then(() => {
return this.props.matrixClient.exportRoomKeys();
})
.then((k) => {
return MegolmExportEncryption.encryptMegolmKeyFile(JSON.stringify(k), passphrase);
})
.then((f) => {
const blob = new Blob([f], {
type: "text/plain;charset=us-ascii",
});
FileSaver.saveAs(blob, "element-keys.txt");
this.props.onFinished(true);
})
.catch((e) => {
logger.error("Error exporting e2e keys:", e);
if (this.unmounted) {
return;
}
const msg = e.friendlyText || _t("Unknown error");
this.setState({
errStr: msg,
phase: Phase.Edit,
});
});
FileSaver.saveAs(blob, 'element-keys.txt');
this.props.onFinished(true);
}).catch((e) => {
logger.error("Error exporting e2e keys:", e);
if (this.unmounted) {
return;
}
const msg = e.friendlyText || _t('Unknown error');
this.setState({
errStr: msg,
phase: Phase.Edit,
});
});
this.setState({
errStr: null,
@ -126,54 +128,53 @@ export default class ExportE2eKeysDialog extends React.Component<IProps, IState>
};
public render(): JSX.Element {
const disableForm = (this.state.phase === Phase.Exporting);
const disableForm = this.state.phase === Phase.Exporting;
return (
<BaseDialog className='mx_exportE2eKeysDialog'
<BaseDialog
className="mx_exportE2eKeysDialog"
onFinished={this.props.onFinished}
title={_t("Export room keys")}
>
<form onSubmit={this.onPassphraseFormSubmit}>
<div className="mx_Dialog_content">
<p>
{ _t(
'This process allows you to export the keys for messages ' +
'you have received in encrypted rooms to a local file. You ' +
'will then be able to import the file into another Matrix ' +
'client in the future, so that client will also be able to ' +
'decrypt these messages.',
) }
{_t(
"This process allows you to export the keys for messages " +
"you have received in encrypted rooms to a local file. You " +
"will then be able to import the file into another Matrix " +
"client in the future, so that client will also be able to " +
"decrypt these messages.",
)}
</p>
<p>
{ _t(
'The exported file will allow anyone who can read it to decrypt ' +
'any encrypted messages that you can see, so you should be ' +
'careful to keep it secure. To help with this, you should enter ' +
'a passphrase below, which will be used to encrypt the exported ' +
'data. It will only be possible to import the data by using the ' +
'same passphrase.',
) }
{_t(
"The exported file will allow anyone who can read it to decrypt " +
"any encrypted messages that you can see, so you should be " +
"careful to keep it secure. To help with this, you should enter " +
"a passphrase below, which will be used to encrypt the exported " +
"data. It will only be possible to import the data by using the " +
"same passphrase.",
)}
</p>
<div className='error'>
{ this.state.errStr }
</div>
<div className='mx_E2eKeysDialog_inputTable'>
<div className='mx_E2eKeysDialog_inputRow'>
<div className="error">{this.state.errStr}</div>
<div className="mx_E2eKeysDialog_inputTable">
<div className="mx_E2eKeysDialog_inputRow">
<Field
label={_t("Enter passphrase")}
value={this.state.passphrase1}
onChange={e => this.onPassphraseChange(e, "passphrase1")}
onChange={(e) => this.onPassphraseChange(e, "passphrase1")}
autoFocus={true}
size={64}
type="password"
disabled={disableForm}
/>
</div>
<div className='mx_E2eKeysDialog_inputRow'>
<div className="mx_E2eKeysDialog_inputRow">
<Field
label={_t("Confirm passphrase")}
value={this.state.passphrase2}
onChange={e => this.onPassphraseChange(e, "passphrase2")}
onChange={(e) => this.onPassphraseChange(e, "passphrase2")}
size={64}
type="password"
disabled={disableForm}
@ -181,15 +182,15 @@ export default class ExportE2eKeysDialog extends React.Component<IProps, IState>
</div>
</div>
</div>
<div className='mx_Dialog_buttons'>
<div className="mx_Dialog_buttons">
<input
className='mx_Dialog_primary'
type='submit'
value={_t('Export')}
className="mx_Dialog_primary"
type="submit"
value={_t("Export")}
disabled={disableForm}
/>
<button onClick={this.onCancelClick} disabled={disableForm}>
{ _t("Cancel") }
{_t("Cancel")}
</button>
</div>
</form>

View file

@ -15,12 +15,12 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import React, { createRef } from 'react';
import { MatrixClient } from 'matrix-js-sdk/src/client';
import React, { createRef } from "react";
import { MatrixClient } from "matrix-js-sdk/src/client";
import { logger } from "matrix-js-sdk/src/logger";
import * as MegolmExportEncryption from '../../../../utils/MegolmExportEncryption';
import { _t } from '../../../../languageHandler';
import * as MegolmExportEncryption from "../../../../utils/MegolmExportEncryption";
import { _t } from "../../../../languageHandler";
import { IDialogProps } from "../../../../components/views/dialogs/IDialogProps";
import BaseDialog from "../../../../components/views/dialogs/BaseDialog";
import Field from "../../../../components/views/elements/Field";
@ -75,7 +75,7 @@ export default class ImportE2eKeysDialog extends React.Component<IProps, IState>
private onFormChange = (): void => {
const files = this.file.current.files || [];
this.setState({
enableSubmit: (this.state.passphrase !== "" && files.length > 0),
enableSubmit: this.state.passphrase !== "" && files.length > 0,
});
};
@ -97,26 +97,28 @@ export default class ImportE2eKeysDialog extends React.Component<IProps, IState>
phase: Phase.Importing,
});
return readFileAsArrayBuffer(file).then((arrayBuffer) => {
return MegolmExportEncryption.decryptMegolmKeyFile(
arrayBuffer, passphrase,
);
}).then((keys) => {
return this.props.matrixClient.importRoomKeys(JSON.parse(keys));
}).then(() => {
// TODO: it would probably be nice to give some feedback about what we've imported here.
this.props.onFinished(true);
}).catch((e) => {
logger.error("Error importing e2e keys:", e);
if (this.unmounted) {
return;
}
const msg = e.friendlyText || _t('Unknown error');
this.setState({
errStr: msg,
phase: Phase.Edit,
return readFileAsArrayBuffer(file)
.then((arrayBuffer) => {
return MegolmExportEncryption.decryptMegolmKeyFile(arrayBuffer, passphrase);
})
.then((keys) => {
return this.props.matrixClient.importRoomKeys(JSON.parse(keys));
})
.then(() => {
// TODO: it would probably be nice to give some feedback about what we've imported here.
this.props.onFinished(true);
})
.catch((e) => {
logger.error("Error importing e2e keys:", e);
if (this.unmounted) {
return;
}
const msg = e.friendlyText || _t("Unknown error");
this.setState({
errStr: msg,
phase: Phase.Edit,
});
});
});
}
private onCancelClick = (ev: React.MouseEvent): boolean => {
@ -126,50 +128,48 @@ export default class ImportE2eKeysDialog extends React.Component<IProps, IState>
};
public render(): JSX.Element {
const disableForm = (this.state.phase !== Phase.Edit);
const disableForm = this.state.phase !== Phase.Edit;
return (
<BaseDialog className='mx_importE2eKeysDialog'
<BaseDialog
className="mx_importE2eKeysDialog"
onFinished={this.props.onFinished}
title={_t("Import room keys")}
>
<form onSubmit={this.onFormSubmit}>
<div className="mx_Dialog_content">
<p>
{ _t(
'This process allows you to import 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.',
) }
{_t(
"This process allows you to import 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.",
)}
</p>
<p>
{ _t(
'The export file will be protected with a passphrase. ' +
'You should enter the passphrase here, to decrypt the file.',
) }
{_t(
"The export file will be protected with a passphrase. " +
"You should enter the passphrase here, to decrypt the file.",
)}
</p>
<div className='error'>
{ this.state.errStr }
</div>
<div className='mx_E2eKeysDialog_inputTable'>
<div className='mx_E2eKeysDialog_inputRow'>
<div className='mx_E2eKeysDialog_inputLabel'>
<label htmlFor='importFile'>
{ _t("File to import") }
</label>
<div className="error">{this.state.errStr}</div>
<div className="mx_E2eKeysDialog_inputTable">
<div className="mx_E2eKeysDialog_inputRow">
<div className="mx_E2eKeysDialog_inputLabel">
<label htmlFor="importFile">{_t("File to import")}</label>
</div>
<div className='mx_E2eKeysDialog_inputCell'>
<div className="mx_E2eKeysDialog_inputCell">
<input
ref={this.file}
id='importFile'
type='file'
id="importFile"
type="file"
autoFocus={true}
onChange={this.onFormChange}
disabled={disableForm} />
disabled={disableForm}
/>
</div>
</div>
<div className='mx_E2eKeysDialog_inputRow'>
<div className="mx_E2eKeysDialog_inputRow">
<Field
label={_t("Enter passphrase")}
value={this.state.passphrase}
@ -181,15 +181,15 @@ export default class ImportE2eKeysDialog extends React.Component<IProps, IState>
</div>
</div>
</div>
<div className='mx_Dialog_buttons'>
<div className="mx_Dialog_buttons">
<input
className='mx_Dialog_primary'
type='submit'
value={_t('Import')}
className="mx_Dialog_primary"
type="submit"
value={_t("Import")}
disabled={!this.state.enableSubmit || disableForm}
/>
<button onClick={this.onCancelClick} disabled={disableForm}>
{ _t("Cancel") }
{_t("Cancel")}
</button>
</div>
</form>

View file

@ -18,7 +18,7 @@ limitations under the License.
import React from "react";
import { IKeyBackupInfo } from "matrix-js-sdk/src/crypto/keybackup";
import { MatrixClientPeg } from '../../../../MatrixClientPeg';
import { MatrixClientPeg } from "../../../../MatrixClientPeg";
import dis from "../../../../dispatcher/dispatcher";
import { _t } from "../../../../languageHandler";
import Modal from "../../../../Modal";
@ -43,61 +43,66 @@ export default class NewRecoveryMethodDialog extends React.PureComponent<IProps>
};
private onSetupClick = async (): Promise<void> => {
Modal.createDialog(RestoreKeyBackupDialog, {
onFinished: this.props.onFinished,
}, null, /* priority = */ false, /* static = */ true);
Modal.createDialog(
RestoreKeyBackupDialog,
{
onFinished: this.props.onFinished,
},
null,
/* priority = */ false,
/* static = */ true,
);
};
public render(): JSX.Element {
const title = <span className="mx_KeyBackupFailedDialog_title">
{ _t("New Recovery Method") }
</span>;
const title = <span className="mx_KeyBackupFailedDialog_title">{_t("New Recovery Method")}</span>;
const newMethodDetected = <p>{ _t(
"A new Security Phrase and key for Secure Messages have been detected.",
) }</p>;
const newMethodDetected = <p>{_t("A new Security Phrase and key for Secure Messages have been detected.")}</p>;
const hackWarning = <p className="warning">{ _t(
"If you didn't set the new recovery method, an " +
"attacker may be trying to access your account. " +
"Change your account password and set a new recovery " +
"method immediately in Settings.",
) }</p>;
const hackWarning = (
<p className="warning">
{_t(
"If you didn't set the new recovery method, an " +
"attacker may be trying to access your account. " +
"Change your account password and set a new recovery " +
"method immediately in Settings.",
)}
</p>
);
let content;
if (MatrixClientPeg.get().getKeyBackupEnabled()) {
content = <div>
{ newMethodDetected }
<p>{ _t(
"This session is encrypting history using the new recovery method.",
) }</p>
{ hackWarning }
<DialogButtons
primaryButton={_t("OK")}
onPrimaryButtonClick={this.onOkClick}
cancelButton={_t("Go to Settings")}
onCancel={this.onGoToSettingsClick}
/>
</div>;
content = (
<div>
{newMethodDetected}
<p>{_t("This session is encrypting history using the new recovery method.")}</p>
{hackWarning}
<DialogButtons
primaryButton={_t("OK")}
onPrimaryButtonClick={this.onOkClick}
cancelButton={_t("Go to Settings")}
onCancel={this.onGoToSettingsClick}
/>
</div>
);
} else {
content = <div>
{ newMethodDetected }
{ hackWarning }
<DialogButtons
primaryButton={_t("Set up Secure Messages")}
onPrimaryButtonClick={this.onSetupClick}
cancelButton={_t("Go to Settings")}
onCancel={this.onGoToSettingsClick}
/>
</div>;
content = (
<div>
{newMethodDetected}
{hackWarning}
<DialogButtons
primaryButton={_t("Set up Secure Messages")}
onPrimaryButtonClick={this.onSetupClick}
cancelButton={_t("Go to Settings")}
onCancel={this.onGoToSettingsClick}
/>
</div>
);
}
return (
<BaseDialog className="mx_KeyBackupFailedDialog"
onFinished={this.props.onFinished}
title={title}
>
{ content }
<BaseDialog className="mx_KeyBackupFailedDialog" onFinished={this.props.onFinished} title={title}>
{content}
</BaseDialog>
);
}

View file

@ -37,36 +37,40 @@ export default class RecoveryMethodRemovedDialog extends React.PureComponent<IPr
this.props.onFinished();
Modal.createDialogAsync(
import("./CreateKeyBackupDialog") as unknown as Promise<ComponentType<{}>>,
null, null, /* priority = */ false, /* static = */ true,
null,
null,
/* priority = */ false,
/* static = */ true,
);
};
public render(): JSX.Element {
const title = <span className="mx_KeyBackupFailedDialog_title">
{ _t("Recovery Method Removed") }
</span>;
const title = <span className="mx_KeyBackupFailedDialog_title">{_t("Recovery Method Removed")}</span>;
return (
<BaseDialog className="mx_KeyBackupFailedDialog"
onFinished={this.props.onFinished}
title={title}
>
<BaseDialog className="mx_KeyBackupFailedDialog" onFinished={this.props.onFinished} title={title}>
<div>
<p>{ _t(
"This session 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 " +
"history with a new recovery method.",
) }</p>
<p className="warning">{ _t(
"If you didn't remove the recovery method, an " +
"attacker may be trying to access your account. " +
"Change your account password and set a new recovery " +
"method immediately in Settings.",
) }</p>
<p>
{_t(
"This session 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 " +
"history with a new recovery method.",
)}
</p>
<p className="warning">
{_t(
"If you didn't remove the recovery method, an " +
"attacker may be trying to access your account. " +
"Change your account password and set a new recovery " +
"method immediately in Settings.",
)}
</p>
<DialogButtons
primaryButton={_t("Set up Secure Messages")}
onPrimaryButtonClick={this.onSetupClick}