Merge pull request #7156 from vector-im/feature/mna/device-manager-verify-other-session

[Device management] Verify another session (PSG-722)
This commit is contained in:
Benoit Marty 2022-09-22 11:46:28 +02:00 committed by GitHub
commit e98bfe5c9b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 143 additions and 69 deletions

1
changelog.d/7143.wip Normal file
View File

@ -0,0 +1 @@
[Device management] Verify another session

View File

@ -34,8 +34,8 @@ import im.vector.app.core.resources.StringProvider
import im.vector.app.core.utils.PublishDataSource import im.vector.app.core.utils.PublishDataSource
import im.vector.app.features.auth.ReAuthActivity import im.vector.app.features.auth.ReAuthActivity
import im.vector.app.features.login.ReAuthHelper import im.vector.app.features.login.ReAuthHelper
import im.vector.app.features.settings.devices.v2.GetEncryptionTrustLevelForDeviceUseCase
import im.vector.app.features.settings.devices.v2.list.CheckIfSessionIsInactiveUseCase import im.vector.app.features.settings.devices.v2.list.CheckIfSessionIsInactiveUseCase
import im.vector.app.features.settings.devices.v2.verification.GetEncryptionTrustLevelForDeviceUseCase
import im.vector.lib.core.utils.flow.throttleFirst import im.vector.lib.core.utils.flow.throttleFirst
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.combine

View File

@ -17,7 +17,7 @@
package im.vector.app.features.settings.devices package im.vector.app.features.settings.devices
import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.core.di.ActiveSessionHolder
import im.vector.app.features.settings.devices.v2.CurrentSessionCrossSigningInfo import im.vector.app.features.settings.devices.v2.verification.CurrentSessionCrossSigningInfo
import javax.inject.Inject import javax.inject.Inject
class GetCurrentSessionCrossSigningInfoUseCase @Inject constructor( class GetCurrentSessionCrossSigningInfoUseCase @Inject constructor(

View File

@ -26,6 +26,7 @@ import im.vector.app.core.di.MavericksAssistedViewModelFactory
import im.vector.app.core.di.hiltMavericksViewModelFactory import im.vector.app.core.di.hiltMavericksViewModelFactory
import im.vector.app.features.settings.devices.v2.filter.DeviceManagerFilterType import im.vector.app.features.settings.devices.v2.filter.DeviceManagerFilterType
import im.vector.app.features.settings.devices.v2.verification.CheckIfCurrentSessionCanBeVerifiedUseCase import im.vector.app.features.settings.devices.v2.verification.CheckIfCurrentSessionCanBeVerifiedUseCase
import im.vector.app.features.settings.devices.v2.verification.GetCurrentSessionCrossSigningInfoUseCase
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch import kotlinx.coroutines.launch

View File

@ -19,6 +19,7 @@ package im.vector.app.features.settings.devices.v2
import com.airbnb.mvrx.Async import com.airbnb.mvrx.Async
import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.MavericksState
import com.airbnb.mvrx.Uninitialized import com.airbnb.mvrx.Uninitialized
import im.vector.app.features.settings.devices.v2.verification.CurrentSessionCrossSigningInfo
data class DevicesViewState( data class DevicesViewState(
val currentSessionCrossSigningInfo: CurrentSessionCrossSigningInfo = CurrentSessionCrossSigningInfo(), val currentSessionCrossSigningInfo: CurrentSessionCrossSigningInfo = CurrentSessionCrossSigningInfo(),

View File

@ -20,6 +20,9 @@ import im.vector.app.core.di.ActiveSessionHolder
import im.vector.app.features.settings.devices.v2.filter.DeviceManagerFilterType import im.vector.app.features.settings.devices.v2.filter.DeviceManagerFilterType
import im.vector.app.features.settings.devices.v2.filter.FilterDevicesUseCase import im.vector.app.features.settings.devices.v2.filter.FilterDevicesUseCase
import im.vector.app.features.settings.devices.v2.list.CheckIfSessionIsInactiveUseCase import im.vector.app.features.settings.devices.v2.list.CheckIfSessionIsInactiveUseCase
import im.vector.app.features.settings.devices.v2.verification.CurrentSessionCrossSigningInfo
import im.vector.app.features.settings.devices.v2.verification.GetCurrentSessionCrossSigningInfoUseCase
import im.vector.app.features.settings.devices.v2.verification.GetEncryptionTrustLevelForDeviceUseCase
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.distinctUntilChanged

View File

@ -62,6 +62,7 @@ class SessionInfoView @JvmOverloads constructor(
sessionInfoViewState.deviceFullInfo.roomEncryptionTrustLevel, sessionInfoViewState.deviceFullInfo.roomEncryptionTrustLevel,
sessionInfoViewState.isCurrentSession, sessionInfoViewState.isCurrentSession,
sessionInfoViewState.isLearnMoreLinkVisible, sessionInfoViewState.isLearnMoreLinkVisible,
sessionInfoViewState.isVerifyButtonVisible,
) )
renderDeviceLastSeenDetails( renderDeviceLastSeenDetails(
sessionInfoViewState.deviceFullInfo.isInactive, sessionInfoViewState.deviceFullInfo.isInactive,
@ -78,12 +79,13 @@ class SessionInfoView @JvmOverloads constructor(
encryptionTrustLevel: RoomEncryptionTrustLevel, encryptionTrustLevel: RoomEncryptionTrustLevel,
isCurrentSession: Boolean, isCurrentSession: Boolean,
hasLearnMoreLink: Boolean, hasLearnMoreLink: Boolean,
isVerifyButtonVisible: Boolean,
) { ) {
views.sessionInfoVerificationStatusImageView.render(encryptionTrustLevel) views.sessionInfoVerificationStatusImageView.render(encryptionTrustLevel)
if (encryptionTrustLevel == RoomEncryptionTrustLevel.Trusted) { if (encryptionTrustLevel == RoomEncryptionTrustLevel.Trusted) {
renderCrossSigningVerified(isCurrentSession) renderCrossSigningVerified(isCurrentSession)
} else { } else {
renderCrossSigningUnverified(isCurrentSession) renderCrossSigningUnverified(isCurrentSession, isVerifyButtonVisible)
} }
if (hasLearnMoreLink) { if (hasLearnMoreLink) {
appendLearnMoreToVerificationStatus() appendLearnMoreToVerificationStatus()
@ -120,7 +122,7 @@ class SessionInfoView @JvmOverloads constructor(
views.sessionInfoVerifySessionButton.isVisible = false views.sessionInfoVerifySessionButton.isVisible = false
} }
private fun renderCrossSigningUnverified(isCurrentSession: Boolean) { private fun renderCrossSigningUnverified(isCurrentSession: Boolean, isVerifyButtonVisible: Boolean) {
views.sessionInfoVerificationStatusTextView.text = context.getString(R.string.device_manager_verification_status_unverified) views.sessionInfoVerificationStatusTextView.text = context.getString(R.string.device_manager_verification_status_unverified)
views.sessionInfoVerificationStatusTextView.setTextColor(ThemeUtils.getColor(context, R.attr.colorError)) views.sessionInfoVerificationStatusTextView.setTextColor(ThemeUtils.getColor(context, R.attr.colorError))
val statusResId = if (isCurrentSession) { val statusResId = if (isCurrentSession) {
@ -129,7 +131,7 @@ class SessionInfoView @JvmOverloads constructor(
R.string.device_manager_verification_status_detail_other_session_unverified R.string.device_manager_verification_status_detail_other_session_unverified
} }
views.sessionInfoVerificationStatusDetailTextView.text = context.getString(statusResId) views.sessionInfoVerificationStatusDetailTextView.text = context.getString(statusResId)
views.sessionInfoVerifySessionButton.isVisible = true views.sessionInfoVerifySessionButton.isVisible = isVerifyButtonVisible
} }
// TODO. We don't have this info yet. Update later accordingly. // TODO. We don't have this info yet. Update later accordingly.

View File

@ -21,6 +21,7 @@ import im.vector.app.features.settings.devices.v2.DeviceFullInfo
data class SessionInfoViewState( data class SessionInfoViewState(
val isCurrentSession: Boolean, val isCurrentSession: Boolean,
val deviceFullInfo: DeviceFullInfo, val deviceFullInfo: DeviceFullInfo,
val isVerifyButtonVisible: Boolean = true,
val isDetailsButtonVisible: Boolean = true, val isDetailsButtonVisible: Boolean = true,
val isLearnMoreLinkVisible: Boolean = false, val isLearnMoreLinkVisible: Boolean = false,
val isLastSeenDetailsVisible: Boolean = false, val isLastSeenDetailsVisible: Boolean = false,

View File

@ -19,9 +19,9 @@ package im.vector.app.features.settings.devices.v2.overview
import androidx.lifecycle.asFlow import androidx.lifecycle.asFlow
import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.core.di.ActiveSessionHolder
import im.vector.app.features.settings.devices.v2.DeviceFullInfo import im.vector.app.features.settings.devices.v2.DeviceFullInfo
import im.vector.app.features.settings.devices.v2.GetCurrentSessionCrossSigningInfoUseCase
import im.vector.app.features.settings.devices.v2.GetEncryptionTrustLevelForDeviceUseCase
import im.vector.app.features.settings.devices.v2.list.CheckIfSessionIsInactiveUseCase import im.vector.app.features.settings.devices.v2.list.CheckIfSessionIsInactiveUseCase
import im.vector.app.features.settings.devices.v2.verification.GetCurrentSessionCrossSigningInfoUseCase
import im.vector.app.features.settings.devices.v2.verification.GetEncryptionTrustLevelForDeviceUseCase
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.emptyFlow

View File

@ -35,7 +35,6 @@ import im.vector.app.core.resources.ColorProvider
import im.vector.app.core.resources.DrawableProvider import im.vector.app.core.resources.DrawableProvider
import im.vector.app.databinding.FragmentSessionOverviewBinding import im.vector.app.databinding.FragmentSessionOverviewBinding
import im.vector.app.features.crypto.recover.SetupMode import im.vector.app.features.crypto.recover.SetupMode
import im.vector.app.features.settings.devices.v2.DeviceFullInfo
import im.vector.app.features.settings.devices.v2.list.SessionInfoViewState import im.vector.app.features.settings.devices.v2.list.SessionInfoViewState
import javax.inject.Inject import javax.inject.Inject
@ -82,9 +81,12 @@ class SessionOverviewFragment :
private fun observeViewEvents() { private fun observeViewEvents() {
viewModel.observeViewEvents { viewModel.observeViewEvents {
when (it) { when (it) {
is SessionOverviewViewEvent.SelfVerification -> { is SessionOverviewViewEvent.ShowVerifyCurrentSession -> {
navigator.requestSelfSessionVerification(requireActivity()) navigator.requestSelfSessionVerification(requireActivity())
} }
is SessionOverviewViewEvent.ShowVerifyOtherSession -> {
navigator.requestSessionVerification(requireActivity(), it.deviceId)
}
is SessionOverviewViewEvent.PromptResetSecrets -> { is SessionOverviewViewEvent.PromptResetSecrets -> {
navigator.open4SSetup(requireActivity(), SetupMode.PASSPHRASE_AND_NEEDED_SECRETS_RESET) navigator.open4SSetup(requireActivity(), SetupMode.PASSPHRASE_AND_NEEDED_SECRETS_RESET)
} }
@ -104,11 +106,7 @@ class SessionOverviewFragment :
override fun invalidate() = withState(viewModel) { state -> override fun invalidate() = withState(viewModel) { state ->
updateToolbar(state.isCurrentSession) updateToolbar(state.isCurrentSession)
updateEntryDetails(state.deviceId) updateEntryDetails(state.deviceId)
if (state.deviceInfo is Success) { updateSessionInfo(state)
renderSessionInfo(state.isCurrentSession, state.deviceInfo.invoke())
} else {
hideSessionInfo()
}
} }
private fun updateToolbar(isCurrentSession: Boolean) { private fun updateToolbar(isCurrentSession: Boolean) {
@ -124,16 +122,22 @@ class SessionOverviewFragment :
} }
} }
private fun renderSessionInfo(isCurrentSession: Boolean, deviceFullInfo: DeviceFullInfo) { private fun updateSessionInfo(viewState: SessionOverviewViewState) {
if (viewState.deviceInfo is Success) {
views.sessionOverviewInfo.isVisible = true views.sessionOverviewInfo.isVisible = true
val viewState = SessionInfoViewState( val isCurrentSession = viewState.isCurrentSession
val infoViewState = SessionInfoViewState(
isCurrentSession = isCurrentSession, isCurrentSession = isCurrentSession,
deviceFullInfo = deviceFullInfo, deviceFullInfo = viewState.deviceInfo.invoke(),
isVerifyButtonVisible = isCurrentSession || viewState.isCurrentSessionTrusted,
isDetailsButtonVisible = false, isDetailsButtonVisible = false,
isLearnMoreLinkVisible = true, isLearnMoreLinkVisible = true,
isLastSeenDetailsVisible = true, isLastSeenDetailsVisible = true,
) )
views.sessionOverviewInfo.render(viewState, dateFormatter, drawableProvider, colorProvider) views.sessionOverviewInfo.render(infoViewState, dateFormatter, drawableProvider, colorProvider)
} else {
hideSessionInfo()
}
} }
private fun hideSessionInfo() { private fun hideSessionInfo() {

View File

@ -19,6 +19,7 @@ package im.vector.app.features.settings.devices.v2.overview
import im.vector.app.core.platform.VectorViewEvents import im.vector.app.core.platform.VectorViewEvents
sealed class SessionOverviewViewEvent : VectorViewEvents { sealed class SessionOverviewViewEvent : VectorViewEvents {
object SelfVerification : SessionOverviewViewEvent() object ShowVerifyCurrentSession : SessionOverviewViewEvent()
data class ShowVerifyOtherSession(val deviceId: String) : SessionOverviewViewEvent()
object PromptResetSecrets : SessionOverviewViewEvent() object PromptResetSecrets : SessionOverviewViewEvent()
} }

View File

@ -21,17 +21,22 @@ import com.airbnb.mvrx.Success
import dagger.assisted.Assisted import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject import dagger.assisted.AssistedInject
import im.vector.app.core.di.ActiveSessionHolder
import im.vector.app.core.di.MavericksAssistedViewModelFactory import im.vector.app.core.di.MavericksAssistedViewModelFactory
import im.vector.app.core.di.hiltMavericksViewModelFactory import im.vector.app.core.di.hiltMavericksViewModelFactory
import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.platform.VectorViewModel
import im.vector.app.features.settings.devices.v2.IsCurrentSessionUseCase import im.vector.app.features.settings.devices.v2.IsCurrentSessionUseCase
import im.vector.app.features.settings.devices.v2.verification.CheckIfCurrentSessionCanBeVerifiedUseCase import im.vector.app.features.settings.devices.v2.verification.CheckIfCurrentSessionCanBeVerifiedUseCase
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.matrix.android.sdk.api.session.crypto.model.RoomEncryptionTrustLevel
class SessionOverviewViewModel @AssistedInject constructor( class SessionOverviewViewModel @AssistedInject constructor(
@Assisted val initialState: SessionOverviewViewState, @Assisted val initialState: SessionOverviewViewState,
private val activeSessionHolder: ActiveSessionHolder,
private val isCurrentSessionUseCase: IsCurrentSessionUseCase, private val isCurrentSessionUseCase: IsCurrentSessionUseCase,
private val getDeviceFullInfoUseCase: GetDeviceFullInfoUseCase, private val getDeviceFullInfoUseCase: GetDeviceFullInfoUseCase,
private val checkIfCurrentSessionCanBeVerifiedUseCase: CheckIfCurrentSessionCanBeVerifiedUseCase, private val checkIfCurrentSessionCanBeVerifiedUseCase: CheckIfCurrentSessionCanBeVerifiedUseCase,
@ -49,6 +54,7 @@ class SessionOverviewViewModel @AssistedInject constructor(
copy(isCurrentSession = isCurrentSession(deviceId)) copy(isCurrentSession = isCurrentSession(deviceId))
} }
observeSessionInfo(initialState.deviceId) observeSessionInfo(initialState.deviceId)
observeCurrentSessionInfo()
} }
private fun isCurrentSession(deviceId: String): Boolean { private fun isCurrentSession(deviceId: String): Boolean {
@ -61,6 +67,19 @@ class SessionOverviewViewModel @AssistedInject constructor(
.launchIn(viewModelScope) .launchIn(viewModelScope)
} }
private fun observeCurrentSessionInfo() {
activeSessionHolder.getSafeActiveSession()
?.sessionParams
?.deviceId
?.let { deviceId ->
getDeviceFullInfoUseCase.execute(deviceId)
.map { it.roomEncryptionTrustLevel == RoomEncryptionTrustLevel.Trusted }
.distinctUntilChanged()
.onEach { setState { copy(isCurrentSessionTrusted = it) } }
.launchIn(viewModelScope)
}
}
override fun handle(action: SessionOverviewAction) { override fun handle(action: SessionOverviewAction) {
when (action) { when (action) {
is SessionOverviewAction.VerifySession -> handleVerifySessionAction() is SessionOverviewAction.VerifySession -> handleVerifySessionAction()
@ -68,8 +87,10 @@ class SessionOverviewViewModel @AssistedInject constructor(
} }
private fun handleVerifySessionAction() = withState { viewState -> private fun handleVerifySessionAction() = withState { viewState ->
if (isCurrentSession(viewState.deviceId)) { if (viewState.isCurrentSession) {
handleVerifyCurrentSession() handleVerifyCurrentSession()
} else {
handleVerifyOtherSession(viewState.deviceId)
} }
} }
@ -77,10 +98,14 @@ class SessionOverviewViewModel @AssistedInject constructor(
viewModelScope.launch { viewModelScope.launch {
val currentSessionCanBeVerified = checkIfCurrentSessionCanBeVerifiedUseCase.execute() val currentSessionCanBeVerified = checkIfCurrentSessionCanBeVerifiedUseCase.execute()
if (currentSessionCanBeVerified) { if (currentSessionCanBeVerified) {
_viewEvents.post(SessionOverviewViewEvent.SelfVerification) _viewEvents.post(SessionOverviewViewEvent.ShowVerifyCurrentSession)
} else { } else {
_viewEvents.post(SessionOverviewViewEvent.PromptResetSecrets) _viewEvents.post(SessionOverviewViewEvent.PromptResetSecrets)
} }
} }
} }
private fun handleVerifyOtherSession(deviceId: String) {
_viewEvents.post(SessionOverviewViewEvent.ShowVerifyOtherSession(deviceId))
}
} }

View File

@ -24,6 +24,7 @@ import im.vector.app.features.settings.devices.v2.DeviceFullInfo
data class SessionOverviewViewState( data class SessionOverviewViewState(
val deviceId: String, val deviceId: String,
val isCurrentSession: Boolean = false, val isCurrentSession: Boolean = false,
val isCurrentSessionTrusted: Boolean = false,
val deviceInfo: Async<DeviceFullInfo> = Uninitialized, val deviceInfo: Async<DeviceFullInfo> = Uninitialized,
) : MavericksState { ) : MavericksState {
constructor(args: SessionOverviewArgs) : this( constructor(args: SessionOverviewArgs) : this(

View File

@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package im.vector.app.features.settings.devices.v2 package im.vector.app.features.settings.devices.v2.verification
/** /**
* Used to hold some info about the cross signing of the current Session. * Used to hold some info about the cross signing of the current Session.

View File

@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package im.vector.app.features.settings.devices.v2 package im.vector.app.features.settings.devices.v2.verification
import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.core.di.ActiveSessionHolder
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow

View File

@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package im.vector.app.features.settings.devices.v2 package im.vector.app.features.settings.devices.v2.verification
import org.matrix.android.sdk.api.session.crypto.model.RoomEncryptionTrustLevel import org.matrix.android.sdk.api.session.crypto.model.RoomEncryptionTrustLevel
import javax.inject.Inject import javax.inject.Inject

View File

@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package im.vector.app.features.settings.devices.v2 package im.vector.app.features.settings.devices.v2.verification
import org.matrix.android.sdk.api.session.crypto.model.CryptoDeviceInfo import org.matrix.android.sdk.api.session.crypto.model.CryptoDeviceInfo
import org.matrix.android.sdk.api.session.crypto.model.RoomEncryptionTrustLevel import org.matrix.android.sdk.api.session.crypto.model.RoomEncryptionTrustLevel

View File

@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package im.vector.app.features.settings.devices.v2 package im.vector.app.features.settings.devices.v2.verification
import org.matrix.android.sdk.api.session.crypto.crosssigning.DeviceTrustLevel import org.matrix.android.sdk.api.session.crypto.crosssigning.DeviceTrustLevel
import org.matrix.android.sdk.api.session.crypto.model.RoomEncryptionTrustLevel import org.matrix.android.sdk.api.session.crypto.model.RoomEncryptionTrustLevel

View File

@ -16,13 +16,10 @@
package im.vector.app.features.settings.devices package im.vector.app.features.settings.devices
import im.vector.app.features.settings.devices.v2.CurrentSessionCrossSigningInfo import im.vector.app.features.settings.devices.v2.verification.CurrentSessionCrossSigningInfo
import im.vector.app.test.fakes.FakeActiveSessionHolder import im.vector.app.test.fakes.FakeActiveSessionHolder
import io.mockk.every
import io.mockk.mockk
import org.amshove.kluent.shouldBeEqualTo import org.amshove.kluent.shouldBeEqualTo
import org.junit.Test import org.junit.Test
import org.matrix.android.sdk.api.auth.data.SessionParams
private const val A_DEVICE_ID = "device-id" private const val A_DEVICE_ID = "device-id"
@ -36,9 +33,7 @@ class GetCurrentSessionCrossSigningInfoUseCaseTest {
@Test @Test
fun `given the active session when getting cross signing info then the result is correct`() { fun `given the active session when getting cross signing info then the result is correct`() {
val sessionParams = mockk<SessionParams>() fakeActiveSessionHolder.fakeSession.givenSessionId(A_DEVICE_ID)
every { sessionParams.deviceId } returns A_DEVICE_ID
fakeActiveSessionHolder.fakeSession.givenSessionParams(sessionParams)
val isCrossSigningInitialized = true val isCrossSigningInitialized = true
fakeActiveSessionHolder.fakeSession fakeActiveSessionHolder.fakeSession
.fakeCryptoService .fakeCryptoService

View File

@ -19,6 +19,8 @@ package im.vector.app.features.settings.devices.v2
import com.airbnb.mvrx.Success import com.airbnb.mvrx.Success
import com.airbnb.mvrx.test.MvRxTestRule import com.airbnb.mvrx.test.MvRxTestRule
import im.vector.app.features.settings.devices.v2.verification.CheckIfCurrentSessionCanBeVerifiedUseCase import im.vector.app.features.settings.devices.v2.verification.CheckIfCurrentSessionCanBeVerifiedUseCase
import im.vector.app.features.settings.devices.v2.verification.CurrentSessionCrossSigningInfo
import im.vector.app.features.settings.devices.v2.verification.GetCurrentSessionCrossSigningInfoUseCase
import im.vector.app.test.fakes.FakeActiveSessionHolder import im.vector.app.test.fakes.FakeActiveSessionHolder
import im.vector.app.test.fakes.FakeVerificationService import im.vector.app.test.fakes.FakeVerificationService
import im.vector.app.test.test import im.vector.app.test.test

View File

@ -19,6 +19,9 @@ package im.vector.app.features.settings.devices.v2
import im.vector.app.features.settings.devices.v2.filter.DeviceManagerFilterType import im.vector.app.features.settings.devices.v2.filter.DeviceManagerFilterType
import im.vector.app.features.settings.devices.v2.filter.FilterDevicesUseCase import im.vector.app.features.settings.devices.v2.filter.FilterDevicesUseCase
import im.vector.app.features.settings.devices.v2.list.CheckIfSessionIsInactiveUseCase import im.vector.app.features.settings.devices.v2.list.CheckIfSessionIsInactiveUseCase
import im.vector.app.features.settings.devices.v2.verification.CurrentSessionCrossSigningInfo
import im.vector.app.features.settings.devices.v2.verification.GetCurrentSessionCrossSigningInfoUseCase
import im.vector.app.features.settings.devices.v2.verification.GetEncryptionTrustLevelForDeviceUseCase
import im.vector.app.test.fakes.FakeActiveSessionHolder import im.vector.app.test.fakes.FakeActiveSessionHolder
import im.vector.app.test.test import im.vector.app.test.test
import im.vector.app.test.testDispatcher import im.vector.app.test.testDispatcher

View File

@ -17,8 +17,6 @@
package im.vector.app.features.settings.devices.v2 package im.vector.app.features.settings.devices.v2
import im.vector.app.test.fakes.FakeActiveSessionHolder import im.vector.app.test.fakes.FakeActiveSessionHolder
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify import io.mockk.verify
import org.amshove.kluent.shouldBe import org.amshove.kluent.shouldBe
import org.junit.Test import org.junit.Test
@ -73,10 +71,7 @@ class IsCurrentSessionUseCaseTest {
result shouldBe false result shouldBe false
} }
private fun givenIdForCurrentSession(deviceId: String): SessionParams { private fun givenIdForCurrentSession(sessionId: String): SessionParams {
val sessionParams = mockk<SessionParams>() return fakeActiveSessionHolder.fakeSession.givenSessionId(sessionId)
every { sessionParams.deviceId } returns deviceId
fakeActiveSessionHolder.fakeSession.givenSessionParams(sessionParams)
return sessionParams
} }
} }

View File

@ -18,11 +18,11 @@ package im.vector.app.features.settings.devices.v2.overview
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.asFlow import androidx.lifecycle.asFlow
import im.vector.app.features.settings.devices.v2.CurrentSessionCrossSigningInfo
import im.vector.app.features.settings.devices.v2.DeviceFullInfo import im.vector.app.features.settings.devices.v2.DeviceFullInfo
import im.vector.app.features.settings.devices.v2.GetCurrentSessionCrossSigningInfoUseCase
import im.vector.app.features.settings.devices.v2.GetEncryptionTrustLevelForDeviceUseCase
import im.vector.app.features.settings.devices.v2.list.CheckIfSessionIsInactiveUseCase import im.vector.app.features.settings.devices.v2.list.CheckIfSessionIsInactiveUseCase
import im.vector.app.features.settings.devices.v2.verification.CurrentSessionCrossSigningInfo
import im.vector.app.features.settings.devices.v2.verification.GetCurrentSessionCrossSigningInfoUseCase
import im.vector.app.features.settings.devices.v2.verification.GetEncryptionTrustLevelForDeviceUseCase
import im.vector.app.test.fakes.FakeActiveSessionHolder import im.vector.app.test.fakes.FakeActiveSessionHolder
import im.vector.app.test.fakes.FakeFlowLiveDataConversions import im.vector.app.test.fakes.FakeFlowLiveDataConversions
import im.vector.app.test.fakes.givenAsFlow import im.vector.app.test.fakes.givenAsFlow

View File

@ -21,6 +21,7 @@ import com.airbnb.mvrx.test.MvRxTestRule
import im.vector.app.features.settings.devices.v2.DeviceFullInfo import im.vector.app.features.settings.devices.v2.DeviceFullInfo
import im.vector.app.features.settings.devices.v2.IsCurrentSessionUseCase import im.vector.app.features.settings.devices.v2.IsCurrentSessionUseCase
import im.vector.app.features.settings.devices.v2.verification.CheckIfCurrentSessionCanBeVerifiedUseCase import im.vector.app.features.settings.devices.v2.verification.CheckIfCurrentSessionCanBeVerifiedUseCase
import im.vector.app.test.fakes.FakeActiveSessionHolder
import im.vector.app.test.test import im.vector.app.test.test
import im.vector.app.test.testDispatcher import im.vector.app.test.testDispatcher
import io.mockk.coEvery import io.mockk.coEvery
@ -31,8 +32,10 @@ import io.mockk.verify
import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.flowOf
import org.junit.Rule import org.junit.Rule
import org.junit.Test import org.junit.Test
import org.matrix.android.sdk.api.session.crypto.model.RoomEncryptionTrustLevel
private const val A_SESSION_ID = "session-id" private const val A_SESSION_ID_1 = "session-id-1"
private const val A_SESSION_ID_2 = "session-id-2"
class SessionOverviewViewModelTest { class SessionOverviewViewModelTest {
@ -40,30 +43,34 @@ class SessionOverviewViewModelTest {
val mvRxTestRule = MvRxTestRule(testDispatcher = testDispatcher) val mvRxTestRule = MvRxTestRule(testDispatcher = testDispatcher)
private val args = SessionOverviewArgs( private val args = SessionOverviewArgs(
deviceId = A_SESSION_ID deviceId = A_SESSION_ID_1
) )
private val fakeActiveSessionHolder = FakeActiveSessionHolder()
private val isCurrentSessionUseCase = mockk<IsCurrentSessionUseCase>() private val isCurrentSessionUseCase = mockk<IsCurrentSessionUseCase>()
private val getDeviceFullInfoUseCase = mockk<GetDeviceFullInfoUseCase>() private val getDeviceFullInfoUseCase = mockk<GetDeviceFullInfoUseCase>()
private val checkIfCurrentSessionCanBeVerifiedUseCase = mockk<CheckIfCurrentSessionCanBeVerifiedUseCase>() private val checkIfCurrentSessionCanBeVerifiedUseCase = mockk<CheckIfCurrentSessionCanBeVerifiedUseCase>()
private fun createViewModel() = SessionOverviewViewModel( private fun createViewModel() = SessionOverviewViewModel(
initialState = SessionOverviewViewState(args), initialState = SessionOverviewViewState(args),
activeSessionHolder = fakeActiveSessionHolder.instance,
isCurrentSessionUseCase = isCurrentSessionUseCase, isCurrentSessionUseCase = isCurrentSessionUseCase,
getDeviceFullInfoUseCase = getDeviceFullInfoUseCase, getDeviceFullInfoUseCase = getDeviceFullInfoUseCase,
checkIfCurrentSessionCanBeVerifiedUseCase = checkIfCurrentSessionCanBeVerifiedUseCase, checkIfCurrentSessionCanBeVerifiedUseCase = checkIfCurrentSessionCanBeVerifiedUseCase,
) )
@Test @Test
fun `given the viewModel has been initialized then viewState is updated with session info`() { fun `given the viewModel has been initialized then viewState is updated with session info and current session verification status`() {
// Given // Given
val deviceFullInfo = mockk<DeviceFullInfo>() val deviceFullInfo = mockk<DeviceFullInfo>()
every { getDeviceFullInfoUseCase.execute(A_SESSION_ID) } returns flowOf(deviceFullInfo) every { getDeviceFullInfoUseCase.execute(A_SESSION_ID_1) } returns flowOf(deviceFullInfo)
val isCurrentSession = true val isCurrentSession = true
every { isCurrentSessionUseCase.execute(any()) } returns isCurrentSession every { isCurrentSessionUseCase.execute(any()) } returns isCurrentSession
givenCurrentSessionIsTrusted()
val expectedState = SessionOverviewViewState( val expectedState = SessionOverviewViewState(
deviceId = A_SESSION_ID, deviceId = A_SESSION_ID_1,
isCurrentSession = isCurrentSession, isCurrentSession = isCurrentSession,
deviceInfo = Success(deviceFullInfo) deviceInfo = Success(deviceFullInfo),
isCurrentSessionTrusted = true,
) )
// When // When
@ -74,8 +81,8 @@ class SessionOverviewViewModelTest {
.assertLatestState { state -> state == expectedState } .assertLatestState { state -> state == expectedState }
.finish() .finish()
verify { verify {
isCurrentSessionUseCase.execute(A_SESSION_ID) isCurrentSessionUseCase.execute(A_SESSION_ID_1)
getDeviceFullInfoUseCase.execute(A_SESSION_ID) getDeviceFullInfoUseCase.execute(A_SESSION_ID_1)
} }
} }
@ -83,10 +90,11 @@ class SessionOverviewViewModelTest {
fun `given current session can be verified when handling verify current session action then self verification event is posted`() { fun `given current session can be verified when handling verify current session action then self verification event is posted`() {
// Given // Given
val deviceFullInfo = mockk<DeviceFullInfo>() val deviceFullInfo = mockk<DeviceFullInfo>()
every { getDeviceFullInfoUseCase.execute(A_SESSION_ID) } returns flowOf(deviceFullInfo) every { getDeviceFullInfoUseCase.execute(A_SESSION_ID_1) } returns flowOf(deviceFullInfo)
every { isCurrentSessionUseCase.execute(any()) } returns true every { isCurrentSessionUseCase.execute(any()) } returns true
val verifySessionAction = SessionOverviewAction.VerifySession val verifySessionAction = SessionOverviewAction.VerifySession
coEvery { checkIfCurrentSessionCanBeVerifiedUseCase.execute() } returns true coEvery { checkIfCurrentSessionCanBeVerifiedUseCase.execute() } returns true
givenCurrentSessionIsTrusted()
// When // When
val viewModel = createViewModel() val viewModel = createViewModel()
@ -95,7 +103,7 @@ class SessionOverviewViewModelTest {
// Then // Then
viewModelTest viewModelTest
.assertEvent { it is SessionOverviewViewEvent.SelfVerification } .assertEvent { it is SessionOverviewViewEvent.ShowVerifyCurrentSession }
.finish() .finish()
coVerify { coVerify {
checkIfCurrentSessionCanBeVerifiedUseCase.execute() checkIfCurrentSessionCanBeVerifiedUseCase.execute()
@ -106,10 +114,11 @@ class SessionOverviewViewModelTest {
fun `given current session cannot be verified when handling verify current session action then reset secrets event is posted`() { fun `given current session cannot be verified when handling verify current session action then reset secrets event is posted`() {
// Given // Given
val deviceFullInfo = mockk<DeviceFullInfo>() val deviceFullInfo = mockk<DeviceFullInfo>()
every { getDeviceFullInfoUseCase.execute(A_SESSION_ID) } returns flowOf(deviceFullInfo) every { getDeviceFullInfoUseCase.execute(A_SESSION_ID_1) } returns flowOf(deviceFullInfo)
every { isCurrentSessionUseCase.execute(any()) } returns true every { isCurrentSessionUseCase.execute(any()) } returns true
val verifySessionAction = SessionOverviewAction.VerifySession val verifySessionAction = SessionOverviewAction.VerifySession
coEvery { checkIfCurrentSessionCanBeVerifiedUseCase.execute() } returns false coEvery { checkIfCurrentSessionCanBeVerifiedUseCase.execute() } returns false
givenCurrentSessionIsTrusted()
// When // When
val viewModel = createViewModel() val viewModel = createViewModel()
@ -124,4 +133,31 @@ class SessionOverviewViewModelTest {
checkIfCurrentSessionCanBeVerifiedUseCase.execute() checkIfCurrentSessionCanBeVerifiedUseCase.execute()
} }
} }
@Test
fun `given another session when handling verify session action then verify session event is posted`() {
// Given
val deviceFullInfo = mockk<DeviceFullInfo>()
every { getDeviceFullInfoUseCase.execute(A_SESSION_ID_1) } returns flowOf(deviceFullInfo)
every { isCurrentSessionUseCase.execute(any()) } returns false
val verifySessionAction = SessionOverviewAction.VerifySession
givenCurrentSessionIsTrusted()
// When
val viewModel = createViewModel()
val viewModelTest = viewModel.test()
viewModel.handle(verifySessionAction)
// Then
viewModelTest
.assertEvent { it is SessionOverviewViewEvent.ShowVerifyOtherSession }
.finish()
}
private fun givenCurrentSessionIsTrusted() {
fakeActiveSessionHolder.fakeSession.givenSessionId(A_SESSION_ID_2)
val deviceFullInfo = mockk<DeviceFullInfo>()
every { deviceFullInfo.roomEncryptionTrustLevel } returns RoomEncryptionTrustLevel.Trusted
every { getDeviceFullInfoUseCase.execute(A_SESSION_ID_2) } returns flowOf(deviceFullInfo)
}
} }

View File

@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package im.vector.app.features.settings.devices.v2 package im.vector.app.features.settings.devices.v2.verification
import im.vector.app.test.fakes.FakeActiveSessionHolder import im.vector.app.test.fakes.FakeActiveSessionHolder
import im.vector.app.test.fakes.FakeSession import im.vector.app.test.fakes.FakeSession
@ -30,7 +30,6 @@ import kotlinx.coroutines.test.runTest
import org.junit.After import org.junit.After
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
import org.matrix.android.sdk.api.auth.data.SessionParams
import org.matrix.android.sdk.api.session.crypto.crosssigning.MXCrossSigningInfo import org.matrix.android.sdk.api.session.crypto.crosssigning.MXCrossSigningInfo
import org.matrix.android.sdk.api.util.toOptional import org.matrix.android.sdk.api.util.toOptional
@ -107,11 +106,8 @@ class GetCurrentSessionCrossSigningInfoUseCaseTest {
} }
private fun givenSession(deviceId: String): FakeSession { private fun givenSession(deviceId: String): FakeSession {
val sessionParams = mockk<SessionParams>()
every { sessionParams.deviceId } returns deviceId
val fakeSession = fakeActiveSessionHolder.fakeSession val fakeSession = fakeActiveSessionHolder.fakeSession
fakeSession.givenSessionParams(sessionParams) fakeSession.givenSessionId(deviceId)
return fakeSession return fakeSession
} }

View File

@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package im.vector.app.features.settings.devices.v2 package im.vector.app.features.settings.devices.v2.verification
import org.amshove.kluent.shouldBeEqualTo import org.amshove.kluent.shouldBeEqualTo
import org.junit.Test import org.junit.Test

View File

@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package im.vector.app.features.settings.devices.v2 package im.vector.app.features.settings.devices.v2.verification
import io.mockk.every import io.mockk.every
import io.mockk.mockk import io.mockk.mockk

View File

@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package im.vector.app.features.settings.devices.v2 package im.vector.app.features.settings.devices.v2.verification
import org.amshove.kluent.shouldBeEqualTo import org.amshove.kluent.shouldBeEqualTo
import org.junit.Test import org.junit.Test

View File

@ -78,6 +78,13 @@ class FakeSession(
every { this@FakeSession.sessionParams } returns sessionParams every { this@FakeSession.sessionParams } returns sessionParams
} }
fun givenSessionId(sessionId: String): SessionParams {
val sessionParams = mockk<SessionParams>()
every { sessionParams.deviceId } returns sessionId
givenSessionParams(sessionParams)
return sessionParams
}
/** /**
* Do not forget to call mockkStatic("org.matrix.android.sdk.flow.FlowSessionKt") in the setup method of the tests. * Do not forget to call mockkStatic("org.matrix.android.sdk.flow.FlowSessionKt") in the setup method of the tests.
*/ */