From 6bfb9440b5cf3502fdb453d0e53127863902dc0c Mon Sep 17 00:00:00 2001 From: David Perez Date: Tue, 13 Feb 2024 12:18:23 -0600 Subject: [PATCH] Add AuthDiskSource helper for knowing when the user changes (#1007) --- .../repository/model/UserSwitchingData.kt | 9 +++ .../util/AuthDiskSourceExtensions.kt | 21 ++++++ .../vault/manager/VaultLockManagerImpl.kt | 20 ++---- .../util/AuthDiskSourceExtensionsTest.kt | 72 +++++++++++++++++++ 4 files changed, 109 insertions(+), 13 deletions(-) create mode 100644 app/src/main/java/com/x8bit/bitwarden/data/auth/repository/model/UserSwitchingData.kt diff --git a/app/src/main/java/com/x8bit/bitwarden/data/auth/repository/model/UserSwitchingData.kt b/app/src/main/java/com/x8bit/bitwarden/data/auth/repository/model/UserSwitchingData.kt new file mode 100644 index 0000000000..f1616d8679 --- /dev/null +++ b/app/src/main/java/com/x8bit/bitwarden/data/auth/repository/model/UserSwitchingData.kt @@ -0,0 +1,9 @@ +package com.x8bit.bitwarden.data.auth.repository.model + +/** + * Contains the values of the previous and new active user IDs when switching active users. + */ +data class UserSwitchingData( + val previousActiveUserId: String?, + val currentActiveUserId: String?, +) diff --git a/app/src/main/java/com/x8bit/bitwarden/data/auth/repository/util/AuthDiskSourceExtensions.kt b/app/src/main/java/com/x8bit/bitwarden/data/auth/repository/util/AuthDiskSourceExtensions.kt index 5eaba051e0..64ac695c3c 100644 --- a/app/src/main/java/com/x8bit/bitwarden/data/auth/repository/util/AuthDiskSourceExtensions.kt +++ b/app/src/main/java/com/x8bit/bitwarden/data/auth/repository/util/AuthDiskSourceExtensions.kt @@ -2,6 +2,7 @@ package com.x8bit.bitwarden.data.auth.repository.util import com.x8bit.bitwarden.data.auth.datasource.disk.AuthDiskSource import com.x8bit.bitwarden.data.auth.repository.model.UserOrganizations +import com.x8bit.bitwarden.data.auth.repository.model.UserSwitchingData import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine @@ -53,3 +54,23 @@ val AuthDiskSource.userOrganizationsListFlow: Flow> ) { values -> values.toList() } } .distinctUntilChanged() + +/** + * Returns a [Flow] that emits every time the active user is changed. + */ +val AuthDiskSource.userSwitchingChangesFlow: Flow + get() { + var lastActiveUserId: String? = null + return this + .userStateFlow + .map { it?.activeUserId } + .distinctUntilChanged() + .map { activeUserId -> + val previousActiveUserId = lastActiveUserId + lastActiveUserId = activeUserId + UserSwitchingData( + previousActiveUserId = previousActiveUserId, + currentActiveUserId = activeUserId, + ) + } + } diff --git a/app/src/main/java/com/x8bit/bitwarden/data/vault/manager/VaultLockManagerImpl.kt b/app/src/main/java/com/x8bit/bitwarden/data/vault/manager/VaultLockManagerImpl.kt index da15b522b1..89650f98ae 100644 --- a/app/src/main/java/com/x8bit/bitwarden/data/vault/manager/VaultLockManagerImpl.kt +++ b/app/src/main/java/com/x8bit/bitwarden/data/vault/manager/VaultLockManagerImpl.kt @@ -10,6 +10,7 @@ import com.x8bit.bitwarden.data.auth.datasource.disk.AuthDiskSource import com.x8bit.bitwarden.data.auth.datasource.sdk.AuthSdkSource import com.x8bit.bitwarden.data.auth.manager.UserLogoutManager import com.x8bit.bitwarden.data.auth.repository.util.toSdkParams +import com.x8bit.bitwarden.data.auth.repository.util.userSwitchingChangesFlow import com.x8bit.bitwarden.data.platform.manager.AppForegroundManager import com.x8bit.bitwarden.data.platform.manager.dispatcher.DispatcherManager import com.x8bit.bitwarden.data.platform.manager.model.AppForegroundState @@ -36,7 +37,6 @@ import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.flow.merge import kotlinx.coroutines.flow.onCompletion import kotlinx.coroutines.flow.onEach @@ -300,21 +300,15 @@ class VaultLockManagerImpl( } private fun observeUserSwitchingChanges() { - var lastActiveUserId: String? = null - authDiskSource - .userStateFlow - .mapNotNull { it?.activeUserId } - .distinctUntilChanged() - .onEach { activeUserId -> - val previousActiveUserId = lastActiveUserId - lastActiveUserId = activeUserId - if (previousActiveUserId != null && - activeUserId != previousActiveUserId - ) { + .userSwitchingChangesFlow + .onEach { userSwitchingData -> + val previousActiveUserId = userSwitchingData.previousActiveUserId + val currentActiveUserId = userSwitchingData.currentActiveUserId + if (previousActiveUserId != null && currentActiveUserId != null) { handleUserSwitch( previousActiveUserId = previousActiveUserId, - currentActiveUserId = activeUserId, + currentActiveUserId = currentActiveUserId, ) } } diff --git a/app/src/test/java/com/x8bit/bitwarden/data/auth/repository/util/AuthDiskSourceExtensionsTest.kt b/app/src/test/java/com/x8bit/bitwarden/data/auth/repository/util/AuthDiskSourceExtensionsTest.kt index 38de9f80fd..1c51d25a89 100644 --- a/app/src/test/java/com/x8bit/bitwarden/data/auth/repository/util/AuthDiskSourceExtensionsTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/data/auth/repository/util/AuthDiskSourceExtensionsTest.kt @@ -7,6 +7,7 @@ import com.x8bit.bitwarden.data.auth.datasource.disk.model.UserStateJson import com.x8bit.bitwarden.data.auth.datasource.disk.util.FakeAuthDiskSource import com.x8bit.bitwarden.data.auth.repository.model.Organization import com.x8bit.bitwarden.data.auth.repository.model.UserOrganizations +import com.x8bit.bitwarden.data.auth.repository.model.UserSwitchingData import com.x8bit.bitwarden.data.vault.datasource.network.model.createMockOrganization import io.mockk.every import io.mockk.mockk @@ -154,4 +155,75 @@ class AuthDiskSourceExtensionsTest { ) } } + + @Test + fun `userSwitchingChangesFlow should emit changes when active user changes`() = runTest { + authDiskSource.userSwitchingChangesFlow.test { + assertEquals( + UserSwitchingData( + previousActiveUserId = null, + currentActiveUserId = null, + ), + awaitItem(), + ) + authDiskSource.userState = MOCK_USER_STATE + assertEquals( + UserSwitchingData( + previousActiveUserId = null, + currentActiveUserId = MOCK_USER_ID, + ), + awaitItem(), + ) + authDiskSource.userState = MOCK_USER_STATE.copy( + accounts = mapOf( + MOCK_USER_ID to MOCK_ACCOUNT, + "mockId-2" to mockk(), + ), + ) + expectNoEvents() + authDiskSource.userState = null + assertEquals( + UserSwitchingData( + previousActiveUserId = MOCK_USER_ID, + currentActiveUserId = null, + ), + awaitItem(), + ) + } + } } + +private const val MOCK_USER_ID: String = "mockId-1" + +private val MOCK_PROFILE = AccountJson.Profile( + userId = MOCK_USER_ID, + email = "email", + isEmailVerified = true, + name = null, + stamp = null, + organizationId = null, + avatarColorHex = null, + hasPremium = false, + forcePasswordResetReason = null, + kdfType = null, + kdfIterations = null, + kdfMemory = null, + kdfParallelism = null, + userDecryptionOptions = null, +) + +private val MOCK_ACCOUNT = AccountJson( + profile = MOCK_PROFILE, + tokens = AccountJson.Tokens( + accessToken = "accessToken", + refreshToken = "refreshToken", + ), + settings = AccountJson.Settings( + environmentUrlData = null, + ), +) + +private val MOCK_USER_STATE = UserStateJson( + activeUserId = MOCK_USER_ID, + accounts = mapOf(MOCK_USER_ID to MOCK_ACCOUNT), +)