diff --git a/src/components/views/right_panel/UserInfo.js b/src/components/views/right_panel/UserInfo.js index 24c5489c4f..4219da690e 100644 --- a/src/components/views/right_panel/UserInfo.js +++ b/src/components/views/right_panel/UserInfo.js @@ -25,7 +25,7 @@ import dis from '../../../dispatcher'; import Modal from '../../../Modal'; import * as sdk from '../../../index'; import { _t } from '../../../languageHandler'; -import createRoom, {findDMForUser} from '../../../createRoom'; +import createRoom from '../../../createRoom'; import DMRoomMap from '../../../utils/DMRoomMap'; import AccessibleButton from '../elements/AccessibleButton'; import SdkConfig from '../../../SdkConfig'; @@ -43,6 +43,7 @@ import MatrixClientContext from "../../../contexts/MatrixClientContext"; import {RIGHT_PANEL_PHASES} from "../../../stores/RightPanelStorePhases"; import EncryptionPanel from "./EncryptionPanel"; import { useAsyncMemo } from '../../../hooks/useAsyncMemo'; +import { verifyUser, legacyVerifyUser, verifyDevice } from '../../../verification'; const _disambiguateDevices = (devices) => { const names = Object.create(null); @@ -153,66 +154,6 @@ function useHasCrossSigningKeys(cli, member, canVerify, setUpdating) { }, [cli, member, canVerify], false); } -async function verifyDevice(userId, device) { - const cli = MatrixClientPeg.get(); - const member = cli.getUser(userId); - const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog"); - Modal.createTrackedDialog("Verification warning", "unverified session", QuestionDialog, { - headerImage: require("../../../../res/img/e2e/warning.svg"), - title: _t("Not Trusted"), - description:
-

{_t("%(name)s (%(userId)s) signed in to a new session without verifying it:", {name: member.displayName, userId})}

-

{device.getDisplayName()} ({device.deviceId})

-

{_t("Ask this user to verify their session, or manually verify it below.")}

-
, - onFinished: async (doneClicked) => { - const manuallyVerifyClicked = !doneClicked; - if (!manuallyVerifyClicked) { - return; - } - const cli = MatrixClientPeg.get(); - const verificationRequestPromise = cli.requestVerification( - userId, - [device.deviceId], - ); - dis.dispatch({ - action: "set_right_panel_phase", - phase: RIGHT_PANEL_PHASES.EncryptionPanel, - refireParams: {member, verificationRequestPromise}, - }); - }, - primaryButton: _t("Done"), - cancelButton: _t("Manually Verify"), - }); -} - -async function legacyVerifyUser(member) { - const cli = MatrixClientPeg.get(); - const verificationRequestPromise = cli.requestVerification(member.userId); - dis.dispatch({ - action: "set_right_panel_phase", - phase: RIGHT_PANEL_PHASES.EncryptionPanel, - refireParams: {member, verificationRequestPromise}, - }); -} - -function verifyUser(user) { - const cli = MatrixClientPeg.get(); - const dmRoom = findDMForUser(cli, user.userId); - let existingRequest; - if (dmRoom) { - existingRequest = cli.findVerificationRequestDMInProgress(dmRoom.roomId); - } - dis.dispatch({ - action: "set_right_panel_phase", - phase: RIGHT_PANEL_PHASES.EncryptionPanel, - refireParams: { - member: user, - verificationRequest: existingRequest, - }, - }); -} - function DeviceItem({userId, device}) { const cli = useContext(MatrixClientContext); const isMe = userId === cli.getUserId(); @@ -239,7 +180,7 @@ function DeviceItem({userId, device}) { const onDeviceClick = () => { if (!isVerified) { - verifyDevice(userId, device); + verifyDevice(cli.getUser(userId), device); } }; diff --git a/src/components/views/toasts/VerificationRequestToast.js b/src/components/views/toasts/VerificationRequestToast.js index c11cefc839..f590cecc3e 100644 --- a/src/components/views/toasts/VerificationRequestToast.js +++ b/src/components/views/toasts/VerificationRequestToast.js @@ -24,6 +24,7 @@ import {userLabelForEventRoom} from "../../../utils/KeyVerificationStateObserver import dis from "../../../dispatcher"; import ToastStore from "../../../stores/ToastStore"; import Modal from "../../../Modal"; +import {enable4SIfNeeded} from "../../../verification"; export default class VerificationRequestToast extends React.PureComponent { constructor(props) { @@ -73,6 +74,9 @@ export default class VerificationRequestToast extends React.PureComponent { } accept = async () => { + if (!await enable4SIfNeeded()) { + return; + } ToastStore.sharedInstance().dismissToast(this.props.toastKey); const {request} = this.props; // no room id for to_device requests diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 15a050d783..2f8f5c48ad 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -291,6 +291,11 @@ "%(senderName)s updated a ban rule that was matching %(oldGlob)s to matching %(newGlob)s for %(reason)s": "%(senderName)s updated a ban rule that was matching %(oldGlob)s to matching %(newGlob)s for %(reason)s", "Light theme": "Light theme", "Dark theme": "Dark theme", + "Not Trusted": "Not Trusted", + "%(name)s (%(userId)s) signed in to a new session without verifying it:": "%(name)s (%(userId)s) signed in to a new session without verifying it:", + "Ask this user to verify their session, or manually verify it below.": "Ask this user to verify their session, or manually verify it below.", + "Done": "Done", + "Manually Verify": "Manually Verify", "%(displayName)s is typing …": "%(displayName)s is typing …", "%(names)s and %(count)s others are typing …|other": "%(names)s and %(count)s others are typing …", "%(names)s and %(count)s others are typing …|one": "%(names)s and one other is typing …", @@ -1185,11 +1190,6 @@ "Yours, or the other users’ session": "Yours, or the other users’ session", "Members": "Members", "Files": "Files", - "Not Trusted": "Not Trusted", - "%(name)s (%(userId)s) signed in to a new session without verifying it:": "%(name)s (%(userId)s) signed in to a new session without verifying it:", - "Ask this user to verify their session, or manually verify it below.": "Ask this user to verify their session, or manually verify it below.", - "Done": "Done", - "Manually Verify": "Manually Verify", "Trusted": "Trusted", "Not trusted": "Not trusted", "%(count)s verified sessions|other": "%(count)s verified sessions", diff --git a/src/verification.js b/src/verification.js new file mode 100644 index 0000000000..235828dc92 --- /dev/null +++ b/src/verification.js @@ -0,0 +1,106 @@ +/* +Copyright 2019, 2020 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import {MatrixClientPeg} from './MatrixClientPeg'; +import dis from "./dispatcher"; +import Modal from './Modal'; +import * as sdk from './index'; +import { _t } from './languageHandler'; +import {RIGHT_PANEL_PHASES} from "./stores/RightPanelStorePhases"; +import {findDMForUser} from './createRoom'; +import {accessSecretStorage} from './CrossSigningManager'; +import SettingsStore from './settings/SettingsStore'; + +export async function enable4SIfNeeded() { + const cli = MatrixClientPeg.get(); + if (!cli.isCryptoEnabled() || !SettingsStore.isFeatureEnabled("feature_cross_signing")) { + return false; + } + const usk = cli.getCrossSigningId("user_signing"); + if (!usk) { + await accessSecretStorage(); + return false; + } + + return true; +} + +export async function verifyDevice(user, device) { + if (!await enable4SIfNeeded()) { + return; + } + const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog"); + Modal.createTrackedDialog("Verification warning", "unverified session", QuestionDialog, { + headerImage: require("../res/img/e2e/warning.svg"), + title: _t("Not Trusted"), + description:
+

{_t("%(name)s (%(userId)s) signed in to a new session without verifying it:", {name: user.displayName, userId: user.userId})}

+

{device.getDisplayName()} ({device.deviceId})

+

{_t("Ask this user to verify their session, or manually verify it below.")}

+
, + onFinished: async (doneClicked) => { + const manuallyVerifyClicked = !doneClicked; + if (!manuallyVerifyClicked) { + return; + } + const cli = MatrixClientPeg.get(); + const verificationRequestPromise = cli.requestVerification( + user.userId, + [device.deviceId], + ); + dis.dispatch({ + action: "set_right_panel_phase", + phase: RIGHT_PANEL_PHASES.EncryptionPanel, + refireParams: {member: user, verificationRequestPromise}, + }); + }, + primaryButton: _t("Done"), + cancelButton: _t("Manually Verify"), + }); +} + +export async function legacyVerifyUser(user) { + if (!await enable4SIfNeeded()) { + return; + } + const cli = MatrixClientPeg.get(); + const verificationRequestPromise = cli.requestVerification(user.userId); + dis.dispatch({ + action: "set_right_panel_phase", + phase: RIGHT_PANEL_PHASES.EncryptionPanel, + refireParams: {member: user, verificationRequestPromise}, + }); +} + +export async function verifyUser(user) { + if (!await enable4SIfNeeded()) { + return; + } + const cli = MatrixClientPeg.get(); + const dmRoom = findDMForUser(cli, user.userId); + let existingRequest; + if (dmRoom) { + existingRequest = cli.findVerificationRequestDMInProgress(dmRoom.roomId); + } + dis.dispatch({ + action: "set_right_panel_phase", + phase: RIGHT_PANEL_PHASES.EncryptionPanel, + refireParams: { + member: user, + verificationRequest: existingRequest, + }, + }); +}