Browse Source

Prevent verification while initial sync is in progress (#1138)

* Prevent verification while initial sync is in progress

* Add `canVerifySessionFlow` to simplify the check
pull/1146/head
Jorge Martin Espinosa 1 year ago committed by GitHub
parent
commit
c670fc9e9c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      changelog.d/1131.bugfix
  2. 11
      features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootPresenter.kt
  3. 5
      features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListPresenter.kt
  4. 2
      features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListPresenterTests.kt
  5. 6
      libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/verification/SessionVerificationService.kt
  6. 17
      libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt
  7. 11
      libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/verification/RustSessionVerificationService.kt
  8. 11
      libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/verification/FakeSessionVerificationService.kt

1
changelog.d/1131.bugfix

@ -0,0 +1 @@
Only display verification prompt after initial sync is done.

11
features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootPresenter.kt

@ -20,7 +20,6 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.MutableState import androidx.compose.runtime.MutableState
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
@ -34,7 +33,6 @@ import io.element.android.libraries.matrix.api.MatrixClient
import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.matrix.api.user.MatrixUser
import io.element.android.libraries.matrix.api.user.getCurrentUser import io.element.android.libraries.matrix.api.user.getCurrentUser
import io.element.android.libraries.matrix.api.verification.SessionVerificationService import io.element.android.libraries.matrix.api.verification.SessionVerificationService
import io.element.android.libraries.matrix.api.verification.SessionVerifiedStatus
import io.element.android.services.analytics.api.AnalyticsService import io.element.android.services.analytics.api.AnalyticsService
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -62,11 +60,8 @@ class PreferencesRootPresenter @Inject constructor(
val snackbarMessage by snackbarDispatcher.collectSnackbarMessageAsState() val snackbarMessage by snackbarDispatcher.collectSnackbarMessageAsState()
val hasAnalyticsProviders = remember { analyticsService.getAvailableAnalyticsProviders().isNotEmpty() } val hasAnalyticsProviders = remember { analyticsService.getAvailableAnalyticsProviders().isNotEmpty() }
// Session verification status (unknown, not verified, verified) // We should display the 'complete verification' option if the current session can be verified
val sessionVerifiedStatus by sessionVerificationService.sessionVerifiedStatus.collectAsState() val showCompleteVerification by sessionVerificationService.canVerifySessionFlow.collectAsState(false)
val sessionIsNotVerified by remember {
derivedStateOf { sessionVerifiedStatus == SessionVerifiedStatus.NotVerified }
}
val accountManagementUrl: MutableState<String?> = remember { val accountManagementUrl: MutableState<String?> = remember {
mutableStateOf(null) mutableStateOf(null)
@ -82,7 +77,7 @@ class PreferencesRootPresenter @Inject constructor(
logoutState = logoutState, logoutState = logoutState,
myUser = matrixUser.value, myUser = matrixUser.value,
version = versionFormatter.get(), version = versionFormatter.get(),
showCompleteVerification = sessionIsNotVerified, showCompleteVerification = showCompleteVerification,
accountManagementUrl = accountManagementUrl.value, accountManagementUrl = accountManagementUrl.value,
showAnalyticsSettings = hasAnalyticsProviders, showAnalyticsSettings = hasAnalyticsProviders,
showDeveloperSettings = showDeveloperSettings, showDeveloperSettings = showDeveloperSettings,

5
features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListPresenter.kt

@ -39,7 +39,6 @@ import io.element.android.libraries.matrix.api.MatrixClient
import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.matrix.api.user.MatrixUser
import io.element.android.libraries.matrix.api.user.getCurrentUser import io.element.android.libraries.matrix.api.user.getCurrentUser
import io.element.android.libraries.matrix.api.verification.SessionVerificationService import io.element.android.libraries.matrix.api.verification.SessionVerificationService
import io.element.android.libraries.matrix.api.verification.SessionVerifiedStatus
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import javax.inject.Inject import javax.inject.Inject
@ -73,11 +72,11 @@ class RoomListPresenter @Inject constructor(
} }
// Session verification status (unknown, not verified, verified) // Session verification status (unknown, not verified, verified)
val sessionVerifiedStatus by sessionVerificationService.sessionVerifiedStatus.collectAsState() val canVerifySession by sessionVerificationService.canVerifySessionFlow.collectAsState(initial = false)
var verificationPromptDismissed by rememberSaveable { mutableStateOf(false) } var verificationPromptDismissed by rememberSaveable { mutableStateOf(false) }
// We combine both values to only display the prompt if the session is not verified and it wasn't dismissed // We combine both values to only display the prompt if the session is not verified and it wasn't dismissed
val displayVerificationPrompt by remember { val displayVerificationPrompt by remember {
derivedStateOf { sessionVerifiedStatus == SessionVerifiedStatus.NotVerified && !verificationPromptDismissed } derivedStateOf { canVerifySession && !verificationPromptDismissed }
} }
var displaySearchResults by rememberSaveable { mutableStateOf(false) } var displaySearchResults by rememberSaveable { mutableStateOf(false) }

2
features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListPresenterTests.kt

@ -202,7 +202,7 @@ class RoomListPresenterTests {
fun `present - handle DismissRequestVerificationPrompt`() = runTest { fun `present - handle DismissRequestVerificationPrompt`() = runTest {
val roomListService = FakeRoomListService() val roomListService = FakeRoomListService()
val matrixClient = FakeMatrixClient( val matrixClient = FakeMatrixClient(
roomListService = roomListService roomListService = roomListService,
) )
val presenter = createRoomListPresenter( val presenter = createRoomListPresenter(
client = matrixClient, client = matrixClient,

6
libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/verification/SessionVerificationService.kt

@ -16,6 +16,7 @@
package io.element.android.libraries.matrix.api.verification package io.element.android.libraries.matrix.api.verification
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
interface SessionVerificationService { interface SessionVerificationService {
@ -37,6 +38,11 @@ interface SessionVerificationService {
*/ */
val sessionVerifiedStatus: StateFlow<SessionVerifiedStatus> val sessionVerifiedStatus: StateFlow<SessionVerifiedStatus>
/**
* Returns whether the current session needs to be verified and the SDK is ready to start the verification.
*/
val canVerifySessionFlow: Flow<Boolean>
/** /**
* Request verification of the current session. * Request verification of the current session.
*/ */

17
libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt

@ -35,7 +35,6 @@ import io.element.android.libraries.matrix.api.room.RoomMembershipObserver
import io.element.android.libraries.matrix.api.roomlist.RoomListService import io.element.android.libraries.matrix.api.roomlist.RoomListService
import io.element.android.libraries.matrix.api.roomlist.awaitLoaded import io.element.android.libraries.matrix.api.roomlist.awaitLoaded
import io.element.android.libraries.matrix.api.sync.SyncService import io.element.android.libraries.matrix.api.sync.SyncService
import io.element.android.libraries.matrix.api.sync.SyncState
import io.element.android.libraries.matrix.api.user.MatrixSearchUserResults import io.element.android.libraries.matrix.api.user.MatrixSearchUserResults
import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.matrix.api.user.MatrixUser
import io.element.android.libraries.matrix.api.verification.SessionVerificationService import io.element.android.libraries.matrix.api.verification.SessionVerificationService
@ -93,8 +92,8 @@ class RustMatrixClient constructor(
private val innerRoomListService = syncService.roomListService() private val innerRoomListService = syncService.roomListService()
private val sessionDispatcher = dispatchers.io.limitedParallelism(64) private val sessionDispatcher = dispatchers.io.limitedParallelism(64)
private val sessionCoroutineScope = appCoroutineScope.childScope(dispatchers.main, "Session-${sessionId}") private val sessionCoroutineScope = appCoroutineScope.childScope(dispatchers.main, "Session-${sessionId}")
private val verificationService = RustSessionVerificationService()
private val rustSyncService = RustSyncService(syncService, sessionCoroutineScope) private val rustSyncService = RustSyncService(syncService, sessionCoroutineScope)
private val verificationService = RustSessionVerificationService(rustSyncService)
private val pushersService = RustPushersService( private val pushersService = RustPushersService(
client = client, client = client,
dispatchers = dispatchers, dispatchers = dispatchers,
@ -149,13 +148,11 @@ class RustMatrixClient constructor(
init { init {
client.setDelegate(clientDelegate) client.setDelegate(clientDelegate)
rustSyncService.syncState roomListService.state.onEach { state ->
.onEach { syncState -> if (state == RoomListService.State.Running) {
if (syncState == SyncState.Running) { setupVerificationControllerIfNeeded()
onSlidingSyncUpdate()
} }
} }.launchIn(sessionCoroutineScope)
.launchIn(sessionCoroutineScope)
} }
override suspend fun getRoom(roomId: RoomId): MatrixRoom? = withContext(sessionDispatcher) { override suspend fun getRoom(roomId: RoomId): MatrixRoom? = withContext(sessionDispatcher) {
@ -338,8 +335,8 @@ class RustMatrixClient constructor(
} }
} }
private fun onSlidingSyncUpdate() { private fun setupVerificationControllerIfNeeded() {
if (!verificationService.isReady.value) { if (verificationService.verificationController == null) {
try { try {
verificationService.verificationController = client.getSessionVerificationController() verificationService.verificationController = client.getSessionVerificationController()
} catch (e: Throwable) { } catch (e: Throwable) {

11
libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/verification/RustSessionVerificationService.kt

@ -17,20 +17,25 @@
package io.element.android.libraries.matrix.impl.verification package io.element.android.libraries.matrix.impl.verification
import io.element.android.libraries.core.data.tryOrNull import io.element.android.libraries.core.data.tryOrNull
import io.element.android.libraries.matrix.api.sync.SyncState
import io.element.android.libraries.matrix.api.verification.SessionVerificationService import io.element.android.libraries.matrix.api.verification.SessionVerificationService
import io.element.android.libraries.matrix.api.verification.SessionVerifiedStatus import io.element.android.libraries.matrix.api.verification.SessionVerifiedStatus
import io.element.android.libraries.matrix.api.verification.VerificationEmoji import io.element.android.libraries.matrix.api.verification.VerificationEmoji
import io.element.android.libraries.matrix.api.verification.VerificationFlowState import io.element.android.libraries.matrix.api.verification.VerificationFlowState
import io.element.android.libraries.matrix.impl.sync.RustSyncService
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.combine
import org.matrix.rustcomponents.sdk.SessionVerificationController import org.matrix.rustcomponents.sdk.SessionVerificationController
import org.matrix.rustcomponents.sdk.SessionVerificationControllerDelegate import org.matrix.rustcomponents.sdk.SessionVerificationControllerDelegate
import org.matrix.rustcomponents.sdk.SessionVerificationControllerInterface import org.matrix.rustcomponents.sdk.SessionVerificationControllerInterface
import org.matrix.rustcomponents.sdk.SessionVerificationEmoji import org.matrix.rustcomponents.sdk.SessionVerificationEmoji
import javax.inject.Inject import javax.inject.Inject
class RustSessionVerificationService @Inject constructor() : SessionVerificationService, SessionVerificationControllerDelegate { class RustSessionVerificationService @Inject constructor(
private val syncService: RustSyncService,
) : SessionVerificationService, SessionVerificationControllerDelegate {
var verificationController: SessionVerificationControllerInterface? = null var verificationController: SessionVerificationControllerInterface? = null
set(value) { set(value) {
@ -52,6 +57,10 @@ class RustSessionVerificationService @Inject constructor() : SessionVerification
private val _sessionVerifiedStatus = MutableStateFlow<SessionVerifiedStatus>(SessionVerifiedStatus.Unknown) private val _sessionVerifiedStatus = MutableStateFlow<SessionVerifiedStatus>(SessionVerifiedStatus.Unknown)
override val sessionVerifiedStatus: StateFlow<SessionVerifiedStatus> = _sessionVerifiedStatus.asStateFlow() override val sessionVerifiedStatus: StateFlow<SessionVerifiedStatus> = _sessionVerifiedStatus.asStateFlow()
override val canVerifySessionFlow = combine(sessionVerifiedStatus, syncService.syncState) { verificationStatus, syncState ->
syncState == SyncState.Running && verificationStatus == SessionVerifiedStatus.NotVerified
}
override suspend fun requestVerification() = tryOrFail { override suspend fun requestVerification() = tryOrFail {
verificationController?.requestVerification() verificationController?.requestVerification()
} }

11
libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/verification/FakeSessionVerificationService.kt

@ -20,6 +20,7 @@ import io.element.android.libraries.matrix.api.verification.SessionVerificationS
import io.element.android.libraries.matrix.api.verification.VerificationFlowState import io.element.android.libraries.matrix.api.verification.VerificationFlowState
import io.element.android.libraries.matrix.api.verification.SessionVerifiedStatus import io.element.android.libraries.matrix.api.verification.SessionVerifiedStatus
import io.element.android.libraries.matrix.api.verification.VerificationEmoji import io.element.android.libraries.matrix.api.verification.VerificationEmoji
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
@ -27,13 +28,13 @@ class FakeSessionVerificationService : SessionVerificationService {
private val _isReady = MutableStateFlow(false) private val _isReady = MutableStateFlow(false)
private val _sessionVerifiedStatus = MutableStateFlow<SessionVerifiedStatus>(SessionVerifiedStatus.Unknown) private val _sessionVerifiedStatus = MutableStateFlow<SessionVerifiedStatus>(SessionVerifiedStatus.Unknown)
private var _verificationFlowState = MutableStateFlow<VerificationFlowState>(VerificationFlowState.Initial) private var _verificationFlowState = MutableStateFlow<VerificationFlowState>(VerificationFlowState.Initial)
private var _canVerifySessionFlow = MutableStateFlow(true)
private var emojiList = emptyList<VerificationEmoji>() private var emojiList = emptyList<VerificationEmoji>()
var shouldFail = false var shouldFail = false
override val verificationFlowState: StateFlow<VerificationFlowState> override val verificationFlowState: StateFlow<VerificationFlowState> =_verificationFlowState
get() = _verificationFlowState
override val sessionVerifiedStatus: StateFlow<SessionVerifiedStatus> = _sessionVerifiedStatus override val sessionVerifiedStatus: StateFlow<SessionVerifiedStatus> = _sessionVerifiedStatus
override val canVerifySessionFlow: Flow<Boolean> = _canVerifySessionFlow
override val isReady: StateFlow<Boolean> = _isReady override val isReady: StateFlow<Boolean> = _isReady
@ -77,6 +78,10 @@ class FakeSessionVerificationService : SessionVerificationService {
_verificationFlowState.value = state _verificationFlowState.value = state
} }
fun givenCanVerifySession(canVerify: Boolean) {
_canVerifySessionFlow.value = canVerify
}
fun givenIsReady(value: Boolean) { fun givenIsReady(value: Boolean) {
_isReady.value = value _isReady.value = value
} }

Loading…
Cancel
Save