Merge pull request #6475 from vector-im/feature/bca/crypto_unable_self_verify
Handle case when device cannot be verified
This commit is contained in:
commit
8e2eb1903d
1
changelog.d/6466.bugfix
Normal file
1
changelog.d/6466.bugfix
Normal file
@ -0,0 +1 @@
|
|||||||
|
When there is no way to verify a device (no 4S nor other device) propose to reset verification keys
|
75
vector/src/androidTest/java/im/vector/app/CantVerifyTest.kt
Normal file
75
vector/src/androidTest/java/im/vector/app/CantVerifyTest.kt
Normal file
@ -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<View>(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()))
|
||||||
|
}
|
||||||
|
}
|
@ -240,7 +240,8 @@ class HomeActivity :
|
|||||||
homeActivityViewModel.observeViewEvents {
|
homeActivityViewModel.observeViewEvents {
|
||||||
when (it) {
|
when (it) {
|
||||||
is HomeActivityViewEvents.AskPasswordToInitCrossSigning -> handleAskPasswordToInitCrossSigning(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.PromptToEnableSessionPush -> handlePromptToEnablePush()
|
||||||
HomeActivityViewEvents.StartRecoverySetupFlow -> handleStartRecoverySetup()
|
HomeActivityViewEvents.StartRecoverySetupFlow -> handleStartRecoverySetup()
|
||||||
is HomeActivityViewEvents.ForceVerification -> {
|
is HomeActivityViewEvents.ForceVerification -> {
|
||||||
@ -424,7 +425,7 @@ class HomeActivity :
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleOnNewSession(event: HomeActivityViewEvents.OnNewSession) {
|
private fun handleOnNewSession(event: HomeActivityViewEvents.CurrentSessionNotVerified) {
|
||||||
// We need to ask
|
// We need to ask
|
||||||
promptSecurityEvent(
|
promptSecurityEvent(
|
||||||
event.userItem,
|
event.userItem,
|
||||||
@ -439,6 +440,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() {
|
private fun handlePromptToEnablePush() {
|
||||||
popupAlertManager.postVectorAlert(
|
popupAlertManager.postVectorAlert(
|
||||||
DefaultVectorAlert(
|
DefaultVectorAlert(
|
||||||
|
@ -21,7 +21,13 @@ import org.matrix.android.sdk.api.util.MatrixItem
|
|||||||
|
|
||||||
sealed interface HomeActivityViewEvents : VectorViewEvents {
|
sealed interface HomeActivityViewEvents : VectorViewEvents {
|
||||||
data class AskPasswordToInitCrossSigning(val userItem: MatrixItem.UserItem?) : HomeActivityViewEvents
|
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
|
data class OnCrossSignedInvalidated(val userItem: MatrixItem.UserItem) : HomeActivityViewEvents
|
||||||
object PromptToEnableSessionPush : HomeActivityViewEvents
|
object PromptToEnableSessionPush : HomeActivityViewEvents
|
||||||
object ShowAnalyticsOptIn : HomeActivityViewEvents
|
object ShowAnalyticsOptIn : HomeActivityViewEvents
|
||||||
|
@ -362,14 +362,30 @@ class HomeActivityViewModel @AssistedInject constructor(
|
|||||||
// If 4S is forced, force verification
|
// If 4S is forced, force verification
|
||||||
_viewEvents.post(HomeActivityViewEvents.ForceVerification(true))
|
_viewEvents.post(HomeActivityViewEvents.ForceVerification(true))
|
||||||
} else {
|
} else {
|
||||||
// New session
|
// we wan't to check if there is a way to actually verify this session,
|
||||||
_viewEvents.post(
|
// that means that there is another session to verify against, or
|
||||||
HomeActivityViewEvents.OnNewSession(
|
// secure backup is setup
|
||||||
session.getUser(session.myUserId)?.toMatrixItem(),
|
val hasTargetDeviceToVerifyAgainst = session
|
||||||
// Always send request instead of waiting for an incoming as per recent EW changes
|
.cryptoService()
|
||||||
false
|
.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(),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -106,6 +106,18 @@ class DeviceVerificationInfoBottomSheetController @Inject constructor(
|
|||||||
.toEpoxyCharSequence()
|
.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 {
|
} else {
|
||||||
if (!currentSessionIsTrusted) {
|
if (!currentSessionIsTrusted) {
|
||||||
@ -141,22 +153,42 @@ class DeviceVerificationInfoBottomSheetController @Inject constructor(
|
|||||||
description("(${cryptoDeviceInfo.deviceId})".toEpoxyCharSequence())
|
description("(${cryptoDeviceInfo.deviceId})".toEpoxyCharSequence())
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isMine && !currentSessionIsTrusted && data.canVerifySession) {
|
if (isMine) {
|
||||||
// Add complete security
|
if (!currentSessionIsTrusted) {
|
||||||
bottomSheetDividerItem {
|
if (data.canVerifySession) {
|
||||||
id("completeSecurityDiv")
|
// Add complete security
|
||||||
}
|
bottomSheetDividerItem {
|
||||||
bottomSheetVerificationActionItem {
|
id("completeSecurityDiv")
|
||||||
id("completeSecurity")
|
}
|
||||||
title(host.stringProvider.getString(R.string.crosssigning_verify_this_session))
|
bottomSheetVerificationActionItem {
|
||||||
titleColor(host.colorProvider.getColorFromAttribute(R.attr.colorPrimary))
|
id("completeSecurity")
|
||||||
iconRes(R.drawable.ic_arrow_right)
|
title(host.stringProvider.getString(R.string.crosssigning_verify_this_session))
|
||||||
iconColor(host.colorProvider.getColorFromAttribute(R.attr.colorPrimary))
|
titleColor(host.colorProvider.getColorFromAttribute(R.attr.colorPrimary))
|
||||||
listener {
|
iconRes(R.drawable.ic_arrow_right)
|
||||||
host.callback?.onAction(DevicesAction.CompleteSecurity)
|
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) {
|
} else
|
||||||
|
/** if (!isMine) **/
|
||||||
|
{
|
||||||
if (currentSessionIsTrusted) {
|
if (currentSessionIsTrusted) {
|
||||||
// we can propose to verify it
|
// we can propose to verify it
|
||||||
val isVerified = cryptoDeviceInfo.trustLevel?.crossSigningVerified.orFalse()
|
val isVerified = cryptoDeviceInfo.trustLevel?.crossSigningVerified.orFalse()
|
||||||
|
@ -30,6 +30,7 @@ sealed class DevicesAction : VectorViewModelAction {
|
|||||||
data class VerifyMyDevice(val deviceId: String) : DevicesAction()
|
data class VerifyMyDevice(val deviceId: String) : DevicesAction()
|
||||||
data class VerifyMyDeviceManually(val deviceId: String) : DevicesAction()
|
data class VerifyMyDeviceManually(val deviceId: String) : DevicesAction()
|
||||||
object CompleteSecurity : DevicesAction()
|
object CompleteSecurity : DevicesAction()
|
||||||
|
object ResetSecurity : DevicesAction()
|
||||||
data class MarkAsManuallyVerified(val cryptoDeviceInfo: CryptoDeviceInfo) : DevicesAction()
|
data class MarkAsManuallyVerified(val cryptoDeviceInfo: CryptoDeviceInfo) : DevicesAction()
|
||||||
|
|
||||||
object SsoAuthDone : DevicesAction()
|
object SsoAuthDone : DevicesAction()
|
||||||
|
@ -48,4 +48,6 @@ sealed class DevicesViewEvents : VectorViewEvents {
|
|||||||
) : DevicesViewEvents()
|
) : DevicesViewEvents()
|
||||||
|
|
||||||
data class ShowManuallyVerify(val cryptoDeviceInfo: CryptoDeviceInfo) : DevicesViewEvents()
|
data class ShowManuallyVerify(val cryptoDeviceInfo: CryptoDeviceInfo) : DevicesViewEvents()
|
||||||
|
|
||||||
|
object PromptResetSecrets : DevicesViewEvents()
|
||||||
}
|
}
|
||||||
|
@ -239,6 +239,7 @@ class DevicesViewModel @AssistedInject constructor(
|
|||||||
uiaContinuation = null
|
uiaContinuation = null
|
||||||
pendingAuth = null
|
pendingAuth = null
|
||||||
}
|
}
|
||||||
|
DevicesAction.ResetSecurity -> _viewEvents.post(DevicesViewEvents.PromptResetSecrets)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,6 +37,7 @@ import im.vector.app.core.platform.VectorBaseFragment
|
|||||||
import im.vector.app.databinding.DialogBaseEditTextBinding
|
import im.vector.app.databinding.DialogBaseEditTextBinding
|
||||||
import im.vector.app.databinding.FragmentGenericRecyclerBinding
|
import im.vector.app.databinding.FragmentGenericRecyclerBinding
|
||||||
import im.vector.app.features.auth.ReAuthActivity
|
import im.vector.app.features.auth.ReAuthActivity
|
||||||
|
import im.vector.app.features.crypto.recover.SetupMode
|
||||||
import im.vector.app.features.crypto.verification.VerificationBottomSheet
|
import im.vector.app.features.crypto.verification.VerificationBottomSheet
|
||||||
import org.matrix.android.sdk.api.auth.data.LoginFlowTypes
|
import org.matrix.android.sdk.api.auth.data.LoginFlowTypes
|
||||||
import org.matrix.android.sdk.api.session.crypto.model.DeviceInfo
|
import org.matrix.android.sdk.api.session.crypto.model.DeviceInfo
|
||||||
@ -89,6 +90,9 @@ class VectorSettingsDevicesFragment @Inject constructor(
|
|||||||
viewModel.handle(DevicesAction.MarkAsManuallyVerified(it.cryptoDeviceInfo))
|
viewModel.handle(DevicesAction.MarkAsManuallyVerified(it.cryptoDeviceInfo))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
is DevicesViewEvents.PromptResetSecrets -> {
|
||||||
|
navigator.open4SSetup(requireContext(), SetupMode.PASSPHRASE_AND_NEEDED_SECRETS_RESET)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2352,7 +2352,9 @@
|
|||||||
<item quantity="other">%d active sessions</item>
|
<item quantity="other">%d active sessions</item>
|
||||||
</plurals>
|
</plurals>
|
||||||
|
|
||||||
<string name="crosssigning_verify_this_session">Verify this login</string>
|
<string name="crosssigning_verify_this_session">Verify this device</string>
|
||||||
|
<string name="crosssigning_cannot_verify_this_session">Unable to verify this device</string>
|
||||||
|
<string name="crosssigning_cannot_verify_this_session_desc">You won’t be able to access encrypted message history. Reset your Secure Message Backup and verification keys to start fresh.</string>
|
||||||
|
|
||||||
<string name="verification_open_other_to_verify">Use an existing session to verify this one, granting it access to encrypted messages.</string>
|
<string name="verification_open_other_to_verify">Use an existing session to verify this one, granting it access to encrypted messages.</string>
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user