From 9f3c8b6e5f80c2adc0e01f08734e2e600069d39b Mon Sep 17 00:00:00 2001 From: Valere Date: Tue, 5 Jul 2022 17:46:29 +0200 Subject: [PATCH 1/3] Handle case when device cannot be verified --- changelog.d/6466.bugfix | 1 + .../java/im/vector/app/CantVerifyTest.kt | 75 +++++++++++++++++++ .../vector/app/features/home/HomeActivity.kt | 16 +++- .../features/home/HomeActivityViewEvents.kt | 8 +- .../features/home/HomeActivityViewModel.kt | 32 ++++++-- ...ceVerificationInfoBottomSheetController.kt | 64 ++++++++++++---- .../settings/devices/DevicesAction.kt | 1 + .../settings/devices/DevicesViewEvents.kt | 2 + .../settings/devices/DevicesViewModel.kt | 1 + .../devices/VectorSettingsDevicesFragment.kt | 4 + vector/src/main/res/values/strings.xml | 4 +- 11 files changed, 183 insertions(+), 25 deletions(-) create mode 100644 changelog.d/6466.bugfix create mode 100644 vector/src/androidTest/java/im/vector/app/CantVerifyTest.kt diff --git a/changelog.d/6466.bugfix b/changelog.d/6466.bugfix new file mode 100644 index 0000000000..31fef9f69d --- /dev/null +++ b/changelog.d/6466.bugfix @@ -0,0 +1 @@ +When there is no way to verify a device (no 4S nor other device) propose to reset verification keys diff --git a/vector/src/androidTest/java/im/vector/app/CantVerifyTest.kt b/vector/src/androidTest/java/im/vector/app/CantVerifyTest.kt new file mode 100644 index 0000000000..ba844e56b7 --- /dev/null +++ b/vector/src/androidTest/java/im/vector/app/CantVerifyTest.kt @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2022 New Vector Ltd + * + * 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. + */ + +package im.vector.app + +import android.view.View +import androidx.test.espresso.Espresso +import androidx.test.espresso.assertion.ViewAssertions +import androidx.test.espresso.matcher.ViewMatchers +import androidx.test.ext.junit.rules.ActivityScenarioRule +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.LargeTest +import im.vector.app.features.MainActivity +import im.vector.app.ui.robot.ElementRobot +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import java.util.UUID + +@RunWith(AndroidJUnit4::class) +@LargeTest +class CantVerifyTest : VerificationTestBase() { + + @get:Rule + val activityRule = ActivityScenarioRule(MainActivity::class.java) + + private val elementRobot = ElementRobot() + var userName: String = "loginTest_${UUID.randomUUID()}" + + @Test + fun checkCantVerifyPopup() { + // Let' create an account + // This first session will create cross signing keys then logout + elementRobot.signUp(userName) + Espresso.onView(ViewMatchers.isRoot()).perform(SleepViewAction.sleep(2000)) + + elementRobot.signout(false) + Espresso.onView(ViewMatchers.isRoot()).perform(SleepViewAction.sleep(2000)) + + // Let's login again now + // There are no methods to verify (no other devices, nor 4S) + // So it should ask to reset all + elementRobot.login(userName) + + val activity = EspressoHelper.getCurrentActivity()!! + Espresso.onView(ViewMatchers.isRoot()) + .perform(waitForView(ViewMatchers.withText(R.string.crosssigning_cannot_verify_this_session))) + + // check that the text is correct + val popup = activity.findViewById(com.tapadoo.alerter.R.id.llAlertBackground)!! + activity.runOnUiThread { popup.performClick() } + + // ensure that it's the 4S setup bottomsheet + Espresso.onView(ViewMatchers.withId(R.id.bottomSheetFragmentContainer)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + + Espresso.onView(ViewMatchers.isRoot()).perform(SleepViewAction.sleep(2000)) + + Espresso.onView(ViewMatchers.withText(R.string.bottom_sheet_setup_secure_backup_title)) + .check(ViewAssertions.matches(ViewMatchers.isDisplayed())) + } +} diff --git a/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt b/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt index 926c1eb113..655a815506 100644 --- a/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt +++ b/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt @@ -238,7 +238,8 @@ class HomeActivity : homeActivityViewModel.observeViewEvents { when (it) { is HomeActivityViewEvents.AskPasswordToInitCrossSigning -> handleAskPasswordToInitCrossSigning(it) - is HomeActivityViewEvents.OnNewSession -> handleOnNewSession(it) + is HomeActivityViewEvents.CurrentSessionNotVerified -> handleOnNewSession(it) + is HomeActivityViewEvents.CurrentSessionCannotBeVerified -> handleCantVerify(it) HomeActivityViewEvents.PromptToEnableSessionPush -> handlePromptToEnablePush() HomeActivityViewEvents.StartRecoverySetupFlow -> handleStartRecoverySetup() is HomeActivityViewEvents.ForceVerification -> { @@ -422,7 +423,7 @@ class HomeActivity : } } - private fun handleOnNewSession(event: HomeActivityViewEvents.OnNewSession) { + private fun handleOnNewSession(event: HomeActivityViewEvents.CurrentSessionNotVerified) { // We need to ask promptSecurityEvent( event.userItem, @@ -437,6 +438,17 @@ class HomeActivity : } } + private fun handleCantVerify(event: HomeActivityViewEvents.CurrentSessionCannotBeVerified) { + // We need to ask + promptSecurityEvent( + event.userItem, + R.string.crosssigning_cannot_verify_this_session, + R.string.crosssigning_cannot_verify_this_session_desc + ) { + it.navigator.open4SSetup(it, SetupMode.PASSPHRASE_AND_NEEDED_SECRETS_RESET) + } + } + private fun handlePromptToEnablePush() { popupAlertManager.postVectorAlert( DefaultVectorAlert( diff --git a/vector/src/main/java/im/vector/app/features/home/HomeActivityViewEvents.kt b/vector/src/main/java/im/vector/app/features/home/HomeActivityViewEvents.kt index cb31a568e4..170550d5b4 100644 --- a/vector/src/main/java/im/vector/app/features/home/HomeActivityViewEvents.kt +++ b/vector/src/main/java/im/vector/app/features/home/HomeActivityViewEvents.kt @@ -21,7 +21,13 @@ import org.matrix.android.sdk.api.util.MatrixItem sealed interface HomeActivityViewEvents : VectorViewEvents { data class AskPasswordToInitCrossSigning(val userItem: MatrixItem.UserItem?) : HomeActivityViewEvents - data class OnNewSession(val userItem: MatrixItem.UserItem?, val waitForIncomingRequest: Boolean = true) : HomeActivityViewEvents + data class CurrentSessionNotVerified( + val userItem: MatrixItem.UserItem?, + val waitForIncomingRequest: Boolean = true, + ) : HomeActivityViewEvents + data class CurrentSessionCannotBeVerified( + val userItem: MatrixItem.UserItem?, + ) : HomeActivityViewEvents data class OnCrossSignedInvalidated(val userItem: MatrixItem.UserItem) : HomeActivityViewEvents object PromptToEnableSessionPush : HomeActivityViewEvents object ShowAnalyticsOptIn : HomeActivityViewEvents diff --git a/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt b/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt index b45e6fbcb0..7a95a70c3d 100644 --- a/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt @@ -364,14 +364,30 @@ class HomeActivityViewModel @AssistedInject constructor( // If 4S is forced, force verification _viewEvents.post(HomeActivityViewEvents.ForceVerification(true)) } else { - // New session - _viewEvents.post( - HomeActivityViewEvents.OnNewSession( - session.getUser(session.myUserId)?.toMatrixItem(), - // Always send request instead of waiting for an incoming as per recent EW changes - false - ) - ) + // we wan't to check if there is a way to actually verify this session, + // that means that there is another session to verify against, or + // secure backup is setup + val hasTargetDeviceToVerifyAgainst = session + .cryptoService() + .getUserDevices(session.myUserId) + .size >= 2 // this one + another + val is4Ssetup = session.sharedSecretStorageService().isRecoverySetup() + if (hasTargetDeviceToVerifyAgainst || is4Ssetup) { + // New session + _viewEvents.post( + HomeActivityViewEvents.CurrentSessionNotVerified( + session.getUser(session.myUserId)?.toMatrixItem(), + // Always send request instead of waiting for an incoming as per recent EW changes + false + ) + ) + } else { + _viewEvents.post( + HomeActivityViewEvents.CurrentSessionCannotBeVerified( + session.getUser(session.myUserId)?.toMatrixItem(), + ) + ) + } } } } diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/DeviceVerificationInfoBottomSheetController.kt b/vector/src/main/java/im/vector/app/features/settings/devices/DeviceVerificationInfoBottomSheetController.kt index 85abf846fa..e4a147fc10 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/DeviceVerificationInfoBottomSheetController.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/DeviceVerificationInfoBottomSheetController.kt @@ -106,6 +106,18 @@ class DeviceVerificationInfoBottomSheetController @Inject constructor( .toEpoxyCharSequence() ) } + } else { + genericItem { + id("reset${cryptoDeviceInfo.deviceId}") + style(ItemStyle.BIG_TEXT) + titleIconResourceId(shield) + title(host.stringProvider.getString(R.string.crosssigning_cannot_verify_this_session).toEpoxyCharSequence()) + description( + host.stringProvider + .getString(R.string.crosssigning_cannot_verify_this_session_desc) + .toEpoxyCharSequence() + ) + } } } else { if (!currentSessionIsTrusted) { @@ -141,19 +153,45 @@ class DeviceVerificationInfoBottomSheetController @Inject constructor( description("(${cryptoDeviceInfo.deviceId})".toEpoxyCharSequence()) } - if (isMine && !currentSessionIsTrusted && data.canVerifySession) { - // Add complete security - bottomSheetDividerItem { - id("completeSecurityDiv") - } - bottomSheetVerificationActionItem { - id("completeSecurity") - title(host.stringProvider.getString(R.string.crosssigning_verify_this_session)) - titleColor(host.colorProvider.getColorFromAttribute(R.attr.colorPrimary)) - iconRes(R.drawable.ic_arrow_right) - iconColor(host.colorProvider.getColorFromAttribute(R.attr.colorPrimary)) - listener { - host.callback?.onAction(DevicesAction.CompleteSecurity) + if (isMine) { + if (!currentSessionIsTrusted) { + if (data.canVerifySession) { + // Add complete security + bottomSheetDividerItem { + id("completeSecurityDiv") + } + bottomSheetVerificationActionItem { + id("completeSecurity") + title(host.stringProvider.getString(R.string.crosssigning_verify_this_session)) + titleColor(host.colorProvider.getColorFromAttribute(R.attr.colorPrimary)) + iconRes(R.drawable.ic_arrow_right) + iconColor(host.colorProvider.getColorFromAttribute(R.attr.colorPrimary)) + listener { + host.callback?.onAction(DevicesAction.CompleteSecurity) + } + } + } else { + bottomSheetDividerItem { + id("resetSecurityDiv") + } + bottomSheetVerificationActionItem { + id("resetSecurity") + title(host.stringProvider.getString(R.string.secure_backup_reset_all)) + titleColor(host.colorProvider.getColorFromAttribute(R.attr.colorPrimary)) + iconRes(R.drawable.ic_arrow_right) + iconColor(host.colorProvider.getColorFromAttribute(R.attr.colorPrimary)) + listener { + host.callback?.onAction(DevicesAction.ResetSecurity) + } + } + } + } else if (!isMine) { + if (currentSessionIsTrusted) { + // we can propose to verify it + val isVerified = cryptoDeviceInfo.trustLevel?.crossSigningVerified.orFalse() + if (!isVerified) { + addVerifyActions(cryptoDeviceInfo) + } } } } else if (!isMine) { diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/DevicesAction.kt b/vector/src/main/java/im/vector/app/features/settings/devices/DevicesAction.kt index 8ee0e7636e..b915236329 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/DevicesAction.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/DevicesAction.kt @@ -30,6 +30,7 @@ sealed class DevicesAction : VectorViewModelAction { data class VerifyMyDevice(val deviceId: String) : DevicesAction() data class VerifyMyDeviceManually(val deviceId: String) : DevicesAction() object CompleteSecurity : DevicesAction() + object ResetSecurity : DevicesAction() data class MarkAsManuallyVerified(val cryptoDeviceInfo: CryptoDeviceInfo) : DevicesAction() object SsoAuthDone : DevicesAction() diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/DevicesViewEvents.kt b/vector/src/main/java/im/vector/app/features/settings/devices/DevicesViewEvents.kt index c057e2b565..c97f353110 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/DevicesViewEvents.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/DevicesViewEvents.kt @@ -48,4 +48,6 @@ sealed class DevicesViewEvents : VectorViewEvents { ) : DevicesViewEvents() data class ShowManuallyVerify(val cryptoDeviceInfo: CryptoDeviceInfo) : DevicesViewEvents() + + object PromptResetSecrets : DevicesViewEvents() } diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/DevicesViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/devices/DevicesViewModel.kt index 1d9f2a97c2..1840a97098 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/DevicesViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/DevicesViewModel.kt @@ -239,6 +239,7 @@ class DevicesViewModel @AssistedInject constructor( uiaContinuation = null pendingAuth = null } + DevicesAction.ResetSecurity -> _viewEvents.post(DevicesViewEvents.PromptResetSecrets) } } diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/VectorSettingsDevicesFragment.kt b/vector/src/main/java/im/vector/app/features/settings/devices/VectorSettingsDevicesFragment.kt index 0c52099f92..a132dc1f49 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/VectorSettingsDevicesFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/VectorSettingsDevicesFragment.kt @@ -37,6 +37,7 @@ import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.databinding.DialogBaseEditTextBinding import im.vector.app.databinding.FragmentGenericRecyclerBinding import im.vector.app.features.auth.ReAuthActivity +import im.vector.app.features.crypto.recover.SetupMode import im.vector.app.features.crypto.verification.VerificationBottomSheet import org.matrix.android.sdk.api.auth.data.LoginFlowTypes import org.matrix.android.sdk.api.session.crypto.model.DeviceInfo @@ -89,6 +90,9 @@ class VectorSettingsDevicesFragment @Inject constructor( viewModel.handle(DevicesAction.MarkAsManuallyVerified(it.cryptoDeviceInfo)) } } + is DevicesViewEvents.PromptResetSecrets -> { + navigator.open4SSetup(requireContext(), SetupMode.PASSPHRASE_AND_NEEDED_SECRETS_RESET) + } } } } diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml index ed0cf691e2..062b7b4484 100644 --- a/vector/src/main/res/values/strings.xml +++ b/vector/src/main/res/values/strings.xml @@ -2351,7 +2351,9 @@ %d active sessions - Verify this login + Verify this device + Unable to verify this device + You won\'t be able to access encrypted message history. Reset your Secure Message Backup and verification keys to start fresh. Use an existing session to verify this one, granting it access to encrypted messages. From 6657d6c5afc4e8eb3c49e3cf6b6ca437ffc3b4e9 Mon Sep 17 00:00:00 2001 From: Valere Date: Tue, 5 Jul 2022 17:54:11 +0200 Subject: [PATCH 2/3] clean --- .../devices/DeviceVerificationInfoBottomSheetController.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/DeviceVerificationInfoBottomSheetController.kt b/vector/src/main/java/im/vector/app/features/settings/devices/DeviceVerificationInfoBottomSheetController.kt index e4a147fc10..b1084077cb 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/DeviceVerificationInfoBottomSheetController.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/DeviceVerificationInfoBottomSheetController.kt @@ -194,7 +194,9 @@ class DeviceVerificationInfoBottomSheetController @Inject constructor( } } } - } else if (!isMine) { + } else + /** if (!isMine) **/ + { if (currentSessionIsTrusted) { // we can propose to verify it val isVerified = cryptoDeviceInfo.trustLevel?.crossSigningVerified.orFalse() From ac7b47b8b2d37d6f76c4aada806e37ef25ce6b06 Mon Sep 17 00:00:00 2001 From: Valere Date: Tue, 12 Jul 2022 10:17:41 +0200 Subject: [PATCH 3/3] code reviews --- .../DeviceVerificationInfoBottomSheetController.kt | 8 -------- vector/src/main/res/values/strings.xml | 2 +- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/DeviceVerificationInfoBottomSheetController.kt b/vector/src/main/java/im/vector/app/features/settings/devices/DeviceVerificationInfoBottomSheetController.kt index b1084077cb..c91c0f457b 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/DeviceVerificationInfoBottomSheetController.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/DeviceVerificationInfoBottomSheetController.kt @@ -185,14 +185,6 @@ class DeviceVerificationInfoBottomSheetController @Inject constructor( } } } - } else if (!isMine) { - if (currentSessionIsTrusted) { - // we can propose to verify it - val isVerified = cryptoDeviceInfo.trustLevel?.crossSigningVerified.orFalse() - if (!isVerified) { - addVerifyActions(cryptoDeviceInfo) - } - } } } else /** if (!isMine) **/ diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml index 062b7b4484..d203b74e52 100644 --- a/vector/src/main/res/values/strings.xml +++ b/vector/src/main/res/values/strings.xml @@ -2353,7 +2353,7 @@ Verify this device Unable to verify this device - You won\'t be able to access encrypted message history. Reset your Secure Message Backup and verification keys to start fresh. + You won’t be able to access encrypted message history. Reset your Secure Message Backup and verification keys to start fresh. Use an existing session to verify this one, granting it access to encrypted messages.