diff --git a/app/src/main/java/com/x8bit/bitwarden/data/auth/repository/AuthRepositoryImpl.kt b/app/src/main/java/com/x8bit/bitwarden/data/auth/repository/AuthRepositoryImpl.kt index 7808383539..ef7745d62e 100644 --- a/app/src/main/java/com/x8bit/bitwarden/data/auth/repository/AuthRepositoryImpl.kt +++ b/app/src/main/java/com/x8bit/bitwarden/data/auth/repository/AuthRepositoryImpl.kt @@ -144,7 +144,7 @@ class AuthRepositoryImpl( override val userStateFlow: StateFlow = combine( authDiskSource.userStateFlow, authDiskSource.userOrganizationsListFlow, - vaultRepository.vaultStateFlow, + vaultRepository.vaultUnlockDataStateFlow, mutableHasPendingAccountAdditionStateFlow, mutableHasPendingAccountDeletionStateFlow, ) { @@ -174,7 +174,7 @@ class AuthRepositoryImpl( initialValue = authDiskSource .userState ?.toUserState( - vaultState = vaultRepository.vaultStateFlow.value, + vaultState = vaultRepository.vaultUnlockDataStateFlow.value, userOrganizationsList = authDiskSource.userOrganizationsList, hasPendingAccountAddition = mutableHasPendingAccountAdditionStateFlow.value, isBiometricsEnabledProvider = ::isBiometricsEnabled, diff --git a/app/src/main/java/com/x8bit/bitwarden/data/auth/repository/util/UserStateJsonExtensions.kt b/app/src/main/java/com/x8bit/bitwarden/data/auth/repository/util/UserStateJsonExtensions.kt index 2270e727b6..c29b4457c6 100644 --- a/app/src/main/java/com/x8bit/bitwarden/data/auth/repository/util/UserStateJsonExtensions.kt +++ b/app/src/main/java/com/x8bit/bitwarden/data/auth/repository/util/UserStateJsonExtensions.kt @@ -6,7 +6,8 @@ import com.x8bit.bitwarden.data.auth.repository.model.UserState import com.x8bit.bitwarden.data.auth.repository.model.VaultUnlockType import com.x8bit.bitwarden.data.platform.repository.util.toEnvironmentUrlsOrDefault import com.x8bit.bitwarden.data.vault.datasource.network.model.SyncResponseJson -import com.x8bit.bitwarden.data.vault.repository.model.VaultState +import com.x8bit.bitwarden.data.vault.repository.model.VaultUnlockData +import com.x8bit.bitwarden.data.vault.repository.util.statusFor import com.x8bit.bitwarden.ui.platform.base.util.toHexColorRepresentation /** @@ -44,7 +45,7 @@ fun UserStateJson.toUpdatedUserStateJson( * Converts the given [UserStateJson] to a [UserState] using the given [vaultState]. */ fun UserStateJson.toUserState( - vaultState: VaultState, + vaultState: List, userOrganizationsList: List, hasPendingAccountAddition: Boolean, isBiometricsEnabledProvider: (userId: String) -> Boolean, @@ -69,7 +70,8 @@ fun UserStateJson.toUserState( .toEnvironmentUrlsOrDefault(), isPremium = accountJson.profile.hasPremium == true, isLoggedIn = accountJson.isLoggedIn, - isVaultUnlocked = userId in vaultState.unlockedVaultUserIds, + isVaultUnlocked = vaultState.statusFor(userId) == + VaultUnlockData.Status.UNLOCKED, organizations = userOrganizationsList .find { it.userId == userId } ?.organizations diff --git a/app/src/main/java/com/x8bit/bitwarden/data/autofill/provider/AutofillCipherProviderImpl.kt b/app/src/main/java/com/x8bit/bitwarden/data/autofill/provider/AutofillCipherProviderImpl.kt index a31711abf7..7e1758bfdf 100644 --- a/app/src/main/java/com/x8bit/bitwarden/data/autofill/provider/AutofillCipherProviderImpl.kt +++ b/app/src/main/java/com/x8bit/bitwarden/data/autofill/provider/AutofillCipherProviderImpl.kt @@ -7,10 +7,12 @@ import com.x8bit.bitwarden.data.autofill.model.AutofillCipher import com.x8bit.bitwarden.data.platform.manager.ciphermatching.CipherMatchingManager import com.x8bit.bitwarden.data.platform.util.subtitle import com.x8bit.bitwarden.data.vault.repository.VaultRepository +import com.x8bit.bitwarden.data.vault.repository.model.VaultUnlockData +import com.x8bit.bitwarden.data.vault.repository.util.statusFor import kotlinx.coroutines.flow.first /** - * The default [AutofillCipherProvider] implementation. This service is used for getting currrent + * The default [AutofillCipherProvider] implementation. This service is used for getting current * [AutofillCipher]s. */ class AutofillCipherProviderImpl( @@ -25,7 +27,9 @@ class AutofillCipherProviderImpl( // Wait for any unlocking actions to finish. This can be relevant on startup for Never lock // accounts. - vaultRepository.vaultStateFlow.first { userId !in it.unlockingVaultUserIds } + vaultRepository.vaultUnlockDataStateFlow.first { + it.statusFor(userId) != VaultUnlockData.Status.UNLOCKING + } return !vaultRepository.isVaultUnlocked(userId = userId) } diff --git a/app/src/main/java/com/x8bit/bitwarden/data/vault/manager/VaultLockManager.kt b/app/src/main/java/com/x8bit/bitwarden/data/vault/manager/VaultLockManager.kt index 10a8235b04..0ff153f6a9 100644 --- a/app/src/main/java/com/x8bit/bitwarden/data/vault/manager/VaultLockManager.kt +++ b/app/src/main/java/com/x8bit/bitwarden/data/vault/manager/VaultLockManager.kt @@ -2,7 +2,7 @@ package com.x8bit.bitwarden.data.vault.manager import com.bitwarden.core.InitUserCryptoMethod import com.bitwarden.crypto.Kdf -import com.x8bit.bitwarden.data.vault.repository.model.VaultState +import com.x8bit.bitwarden.data.vault.repository.model.VaultUnlockData import com.x8bit.bitwarden.data.vault.repository.model.VaultUnlockResult import kotlinx.coroutines.flow.StateFlow @@ -11,9 +11,9 @@ import kotlinx.coroutines.flow.StateFlow */ interface VaultLockManager { /** - * Flow that represents the current vault state. + * Flow that represents the current vault lock state for each user. */ - val vaultStateFlow: StateFlow + val vaultUnlockDataStateFlow: StateFlow> /** * Whether or not the vault is currently locked for the given [userId]. 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 f9efd8c730..f614e7c75d 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 @@ -20,9 +20,11 @@ import com.x8bit.bitwarden.data.platform.util.asSuccess import com.x8bit.bitwarden.data.platform.util.flatMap import com.x8bit.bitwarden.data.vault.datasource.sdk.VaultSdkSource import com.x8bit.bitwarden.data.vault.datasource.sdk.model.InitializeCryptoResult -import com.x8bit.bitwarden.data.vault.repository.model.VaultState +import com.x8bit.bitwarden.data.vault.repository.model.VaultUnlockData import com.x8bit.bitwarden.data.vault.repository.model.VaultUnlockResult +import com.x8bit.bitwarden.data.vault.repository.util.statusFor import com.x8bit.bitwarden.data.vault.repository.util.toVaultUnlockResult +import com.x8bit.bitwarden.data.vault.repository.util.update import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow @@ -68,16 +70,11 @@ class VaultLockManagerImpl( private val activeUserId: String? get() = authDiskSource.userState?.activeUserId private val userIds: Set get() = authDiskSource.userState?.accounts?.keys.orEmpty() - private val mutableVaultStateStateFlow = - MutableStateFlow( - VaultState( - unlockedVaultUserIds = emptySet(), - unlockingVaultUserIds = emptySet(), - ), - ) + private val mutableVaultUnlockDataStateFlow = + MutableStateFlow>(emptyList()) - override val vaultStateFlow: StateFlow - get() = mutableVaultStateStateFlow.asStateFlow() + override val vaultUnlockDataStateFlow: StateFlow> + get() = mutableVaultUnlockDataStateFlow.asStateFlow() init { observeAppForegroundChanges() @@ -86,10 +83,10 @@ class VaultLockManagerImpl( } override fun isVaultUnlocked(userId: String): Boolean = - userId in mutableVaultStateStateFlow.value.unlockedVaultUserIds + mutableVaultUnlockDataStateFlow.value.statusFor(userId) == VaultUnlockData.Status.UNLOCKED override fun isVaultUnlocking(userId: String): Boolean = - userId in mutableVaultStateStateFlow.value.unlockingVaultUserIds + mutableVaultUnlockDataStateFlow.value.statusFor(userId) == VaultUnlockData.Status.UNLOCKING override fun lockVault(userId: String) { setVaultToLocked(userId = userId) @@ -205,10 +202,8 @@ class VaultLockManagerImpl( } private fun setVaultToUnlocked(userId: String) { - mutableVaultStateStateFlow.update { - it.copy( - unlockedVaultUserIds = it.unlockedVaultUserIds + userId, - ) + mutableVaultUnlockDataStateFlow.update { + it.update(userId, VaultUnlockData.Status.UNLOCKED) } // If we are unlocking an account with a timeout of Never, we should make sure to store the // auto-unlock key. @@ -217,10 +212,8 @@ class VaultLockManagerImpl( private fun setVaultToLocked(userId: String) { vaultSdkSource.clearCrypto(userId = userId) - mutableVaultStateStateFlow.update { - it.copy( - unlockedVaultUserIds = it.unlockedVaultUserIds - userId, - ) + mutableVaultUnlockDataStateFlow.update { + it.update(userId, null) } authDiskSource.storeUserAutoUnlockKey( userId = userId, @@ -229,18 +222,16 @@ class VaultLockManagerImpl( } private fun setVaultToUnlocking(userId: String) { - mutableVaultStateStateFlow.update { - it.copy( - unlockingVaultUserIds = it.unlockingVaultUserIds + userId, - ) + mutableVaultUnlockDataStateFlow.update { + it.update(userId, VaultUnlockData.Status.UNLOCKING) } } private fun setVaultToNotUnlocking(userId: String) { - mutableVaultStateStateFlow.update { - it.copy( - unlockingVaultUserIds = it.unlockingVaultUserIds - userId, - ) + val status = mutableVaultUnlockDataStateFlow.value.statusFor(userId) + if (status != VaultUnlockData.Status.UNLOCKING) return + mutableVaultUnlockDataStateFlow.update { + it.update(userId, null) } } diff --git a/app/src/main/java/com/x8bit/bitwarden/data/vault/repository/model/VaultState.kt b/app/src/main/java/com/x8bit/bitwarden/data/vault/repository/model/VaultState.kt deleted file mode 100644 index 0b9debbaea..0000000000 --- a/app/src/main/java/com/x8bit/bitwarden/data/vault/repository/model/VaultState.kt +++ /dev/null @@ -1,13 +0,0 @@ -package com.x8bit.bitwarden.data.vault.repository.model - -/** - * General description of the vault across multiple users. - * - * @property unlockedVaultUserIds The user IDs for all users that currently have unlocked vaults. - * @property unlockedVaultUserIds The user IDs for all users that are actively unlocking their - * vaults. - */ -data class VaultState( - val unlockedVaultUserIds: Set, - val unlockingVaultUserIds: Set, -) diff --git a/app/src/main/java/com/x8bit/bitwarden/data/vault/repository/model/VaultUnlockData.kt b/app/src/main/java/com/x8bit/bitwarden/data/vault/repository/model/VaultUnlockData.kt new file mode 100644 index 0000000000..a57bac85a5 --- /dev/null +++ b/app/src/main/java/com/x8bit/bitwarden/data/vault/repository/model/VaultUnlockData.kt @@ -0,0 +1,21 @@ +package com.x8bit.bitwarden.data.vault.repository.model + +/** + * The vault state for a given user ID. + * + * @property userId The user ID. + * @property status The lock status of the user's vault. + */ +data class VaultUnlockData( + val userId: String, + val status: Status, +) { + /** + * The lock status of a user's vault. + */ + enum class Status { + PENDING, + UNLOCKED, + UNLOCKING, + } +} diff --git a/app/src/main/java/com/x8bit/bitwarden/data/vault/repository/util/VaultUnlockDataExtensions.kt b/app/src/main/java/com/x8bit/bitwarden/data/vault/repository/util/VaultUnlockDataExtensions.kt new file mode 100644 index 0000000000..edff4af3e9 --- /dev/null +++ b/app/src/main/java/com/x8bit/bitwarden/data/vault/repository/util/VaultUnlockDataExtensions.kt @@ -0,0 +1,32 @@ +package com.x8bit.bitwarden.data.vault.repository.util + +import com.x8bit.bitwarden.data.vault.repository.model.VaultUnlockData + +/** + * Get the vault unlock status for a [userId] from a list of [VaultUnlockData]. + */ +fun List.statusFor(userId: String): VaultUnlockData.Status? = + firstOrNull { it.userId == userId }?.status + +/** + * Update the vault unlock status for a [userId] in a list of [VaultUnlockData]. + */ +fun List.update( + userId: String, + status: VaultUnlockData.Status?, +): List { + val updatedList = filter { + it.userId != userId + } + return if (status == null) { + updatedList + } else { + updatedList + .plus( + VaultUnlockData( + userId = userId, + status = status, + ), + ) + } +} diff --git a/app/src/test/java/com/x8bit/bitwarden/data/auth/repository/AuthRepositoryTest.kt b/app/src/test/java/com/x8bit/bitwarden/data/auth/repository/AuthRepositoryTest.kt index 4232ef908c..ca1ababfb4 100644 --- a/app/src/test/java/com/x8bit/bitwarden/data/auth/repository/AuthRepositoryTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/data/auth/repository/AuthRepositoryTest.kt @@ -73,7 +73,7 @@ import com.x8bit.bitwarden.data.platform.util.asSuccess import com.x8bit.bitwarden.data.vault.datasource.network.model.createMockOrganization import com.x8bit.bitwarden.data.vault.datasource.sdk.VaultSdkSource import com.x8bit.bitwarden.data.vault.repository.VaultRepository -import com.x8bit.bitwarden.data.vault.repository.model.VaultState +import com.x8bit.bitwarden.data.vault.repository.model.VaultUnlockData import com.x8bit.bitwarden.data.vault.repository.model.VaultUnlockResult import io.mockk.coEvery import io.mockk.coVerify @@ -108,7 +108,7 @@ class AuthRepositoryTest { private val organizationService: OrganizationService = mockk() private val mutableVaultStateFlow = MutableStateFlow(VAULT_STATE) private val vaultRepository: VaultRepository = mockk { - every { vaultStateFlow } returns mutableVaultStateFlow + every { vaultUnlockDataStateFlow } returns mutableVaultStateFlow every { deleteVaultData(any()) } just runs every { clearUnlockedData() } just runs } @@ -295,10 +295,7 @@ class AuthRepositoryTest { repository.userStateFlow.value, ) - val emptyVaultState = VaultState( - unlockedVaultUserIds = emptySet(), - unlockingVaultUserIds = emptySet(), - ) + val emptyVaultState = emptyList() mutableVaultStateFlow.value = emptyVaultState assertEquals( MULTI_USER_STATE.toUserState( @@ -2915,9 +2912,11 @@ class AuthRepositoryTest { organizations = ORGANIZATIONS.toOrganizations(), ), ) - private val VAULT_STATE = VaultState( - unlockedVaultUserIds = setOf(USER_ID_1), - unlockingVaultUserIds = emptySet(), + private val VAULT_STATE = listOf( + VaultUnlockData( + userId = USER_ID_1, + status = VaultUnlockData.Status.UNLOCKED, + ), ) } } diff --git a/app/src/test/java/com/x8bit/bitwarden/data/auth/repository/util/UserStateJsonExtensionsTest.kt b/app/src/test/java/com/x8bit/bitwarden/data/auth/repository/util/UserStateJsonExtensionsTest.kt index a2ce0cfc76..3d7cfc52fa 100644 --- a/app/src/test/java/com/x8bit/bitwarden/data/auth/repository/util/UserStateJsonExtensionsTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/data/auth/repository/util/UserStateJsonExtensionsTest.kt @@ -9,7 +9,7 @@ import com.x8bit.bitwarden.data.auth.repository.model.UserOrganizations import com.x8bit.bitwarden.data.auth.repository.model.UserState import com.x8bit.bitwarden.data.auth.repository.model.VaultUnlockType import com.x8bit.bitwarden.data.platform.repository.model.Environment -import com.x8bit.bitwarden.data.vault.repository.model.VaultState +import com.x8bit.bitwarden.data.vault.repository.model.VaultUnlockData import io.mockk.every import io.mockk.mockk import org.junit.jupiter.api.Assertions.assertEquals @@ -140,9 +140,11 @@ class UserStateJsonExtensionsTest { ), ) .toUserState( - vaultState = VaultState( - unlockedVaultUserIds = setOf("activeUserId"), - unlockingVaultUserIds = emptySet(), + vaultState = listOf( + VaultUnlockData( + userId = "activeUserId", + status = VaultUnlockData.Status.UNLOCKED, + ), ), userOrganizationsList = listOf( UserOrganizations( @@ -212,10 +214,7 @@ class UserStateJsonExtensionsTest { ), ) .toUserState( - vaultState = VaultState( - unlockedVaultUserIds = emptySet(), - unlockingVaultUserIds = emptySet(), - ), + vaultState = emptyList(), userOrganizationsList = listOf( UserOrganizations( userId = "activeUserId", diff --git a/app/src/test/java/com/x8bit/bitwarden/data/autofill/processor/AutofillCipherProviderTest.kt b/app/src/test/java/com/x8bit/bitwarden/data/autofill/processor/AutofillCipherProviderTest.kt index 0ac9f572f8..6b4a87d176 100644 --- a/app/src/test/java/com/x8bit/bitwarden/data/autofill/processor/AutofillCipherProviderTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/data/autofill/processor/AutofillCipherProviderTest.kt @@ -12,7 +12,8 @@ import com.x8bit.bitwarden.data.platform.manager.ciphermatching.CipherMatchingMa import com.x8bit.bitwarden.data.platform.repository.model.DataState import com.x8bit.bitwarden.data.platform.util.subtitle import com.x8bit.bitwarden.data.vault.repository.VaultRepository -import com.x8bit.bitwarden.data.vault.repository.model.VaultState +import com.x8bit.bitwarden.data.vault.repository.model.VaultUnlockData +import com.x8bit.bitwarden.data.vault.repository.util.statusFor import io.mockk.coEvery import io.mockk.coVerify import io.mockk.every @@ -57,20 +58,17 @@ class AutofillCipherProviderTest { every { activeUserId } returns ACTIVE_USER_ID } private val cipherMatchingManager: CipherMatchingManager = mockk() - private val mutableVaultStateFlow = MutableStateFlow( - VaultState( - unlockingVaultUserIds = emptySet(), - unlockedVaultUserIds = emptySet(), - ), + private val mutableVaultStateFlow = MutableStateFlow>( + emptyList(), ) private val mutableCiphersStateFlow = MutableStateFlow>>( DataState.Loading, ) private val vaultRepository: VaultRepository = mockk { every { ciphersStateFlow } returns mutableCiphersStateFlow - every { vaultStateFlow } returns mutableVaultStateFlow + every { vaultUnlockDataStateFlow } returns mutableVaultStateFlow every { isVaultUnlocked(ACTIVE_USER_ID) } answers { - ACTIVE_USER_ID in mutableVaultStateFlow.value.unlockedVaultUserIds + mutableVaultStateFlow.value.statusFor(ACTIVE_USER_ID) == VaultUnlockData.Status.UNLOCKED } } @@ -111,9 +109,11 @@ class AutofillCipherProviderTest { fun `isVaultLocked when there is an active user should wait for pending unlocking to finish and return the locked state for that user`() = runTest { every { authRepository.activeUserId } returns ACTIVE_USER_ID - mutableVaultStateFlow.value = VaultState( - unlockedVaultUserIds = emptySet(), - unlockingVaultUserIds = setOf(ACTIVE_USER_ID), + mutableVaultStateFlow.value = listOf( + VaultUnlockData( + userId = ACTIVE_USER_ID, + status = VaultUnlockData.Status.UNLOCKING, + ), ) val result = async { @@ -123,9 +123,11 @@ class AutofillCipherProviderTest { testScheduler.advanceUntilIdle() assertFalse(result.isCompleted) - mutableVaultStateFlow.value = VaultState( - unlockedVaultUserIds = setOf(ACTIVE_USER_ID), - unlockingVaultUserIds = emptySet(), + mutableVaultStateFlow.value = listOf( + VaultUnlockData( + userId = ACTIVE_USER_ID, + status = VaultUnlockData.Status.UNLOCKED, + ), ) testScheduler.advanceUntilIdle() @@ -150,9 +152,11 @@ class AutofillCipherProviderTest { mutableCiphersStateFlow.value = DataState.Loaded( data = cipherViews, ) - mutableVaultStateFlow.value = VaultState( - unlockedVaultUserIds = setOf(ACTIVE_USER_ID), - unlockingVaultUserIds = emptySet(), + mutableVaultStateFlow.value = listOf( + VaultUnlockData( + userId = ACTIVE_USER_ID, + status = VaultUnlockData.Status.UNLOCKED, + ), ) val expected = listOf( CARD_AUTOFILL_CIPHER, @@ -167,10 +171,7 @@ class AutofillCipherProviderTest { @Test fun `getCardAutofillCiphers when locked should return an empty list`() = runTest { - mutableVaultStateFlow.value = VaultState( - unlockedVaultUserIds = emptySet(), - unlockingVaultUserIds = emptySet(), - ) + mutableVaultStateFlow.value = emptyList() // Test & Verify val actual = autofillCipherProvider.getCardAutofillCiphers() @@ -203,9 +204,11 @@ class AutofillCipherProviderTest { mutableCiphersStateFlow.value = DataState.Loaded( data = cipherViews, ) - mutableVaultStateFlow.value = VaultState( - unlockedVaultUserIds = setOf(ACTIVE_USER_ID), - unlockingVaultUserIds = emptySet(), + mutableVaultStateFlow.value = listOf( + VaultUnlockData( + userId = ACTIVE_USER_ID, + status = VaultUnlockData.Status.UNLOCKED, + ), ) val expected = listOf( LOGIN_AUTOFILL_CIPHER, @@ -229,10 +232,7 @@ class AutofillCipherProviderTest { @Test fun `getLoginAutofillCiphers when locked should return an empty list`() = runTest { - mutableVaultStateFlow.value = VaultState( - unlockedVaultUserIds = emptySet(), - unlockingVaultUserIds = emptySet(), - ) + mutableVaultStateFlow.value = emptyList() // Test & Verify val actual = autofillCipherProvider.getLoginAutofillCiphers( diff --git a/app/src/test/java/com/x8bit/bitwarden/data/vault/manager/VaultLockManagerTest.kt b/app/src/test/java/com/x8bit/bitwarden/data/vault/manager/VaultLockManagerTest.kt index cab81c4ca4..2634855dca 100644 --- a/app/src/test/java/com/x8bit/bitwarden/data/vault/manager/VaultLockManagerTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/data/vault/manager/VaultLockManagerTest.kt @@ -20,7 +20,7 @@ import com.x8bit.bitwarden.data.platform.util.asFailure import com.x8bit.bitwarden.data.platform.util.asSuccess import com.x8bit.bitwarden.data.vault.datasource.sdk.VaultSdkSource import com.x8bit.bitwarden.data.vault.datasource.sdk.model.InitializeCryptoResult -import com.x8bit.bitwarden.data.vault.repository.model.VaultState +import com.x8bit.bitwarden.data.vault.repository.model.VaultUnlockData import com.x8bit.bitwarden.data.vault.repository.model.VaultUnlockResult import io.mockk.awaits import io.mockk.clearMocks @@ -85,13 +85,12 @@ class VaultLockManagerTest { @Test fun `app going into background should update the current user's last active time`() { - val userId = "mockId-1" fakeAuthDiskSource.userState = MOCK_USER_STATE // Start in a foregrounded state fakeAppForegroundManager.appForegroundState = AppForegroundState.FOREGROUNDED fakeAuthDiskSource.assertLastActiveTimeMillis( - userId = userId, + userId = USER_ID, lastActiveTimeMillis = null, ) @@ -99,7 +98,7 @@ class VaultLockManagerTest { fakeAppForegroundManager.appForegroundState = AppForegroundState.BACKGROUNDED fakeAuthDiskSource.assertLastActiveTimeMillis( - userId = userId, + userId = USER_ID, lastActiveTimeMillis = 123L, ) } @@ -107,24 +106,23 @@ class VaultLockManagerTest { @Suppress("MaxLineLength") @Test fun `app coming into foreground for the first time for Never timeout should clear existing times and not perform timeout action`() { - val userId = "mockId-1" fakeAuthDiskSource.userState = MOCK_USER_STATE mutableVaultTimeoutActionStateFlow.value = VaultTimeoutAction.LOCK mutableVaultTimeoutStateFlow.value = VaultTimeout.Never fakeAppForegroundManager.appForegroundState = AppForegroundState.BACKGROUNDED fakeAuthDiskSource.storeLastActiveTimeMillis( - userId = userId, + userId = USER_ID, lastActiveTimeMillis = 123L, ) - verifyUnlockedVaultBlocking(userId = userId) - assertTrue(vaultLockManager.isVaultUnlocked(userId)) + verifyUnlockedVaultBlocking(userId = USER_ID) + assertTrue(vaultLockManager.isVaultUnlocked(USER_ID)) fakeAppForegroundManager.appForegroundState = AppForegroundState.FOREGROUNDED - assertTrue(vaultLockManager.isVaultUnlocked(userId)) + assertTrue(vaultLockManager.isVaultUnlocked(USER_ID)) fakeAuthDiskSource.assertLastActiveTimeMillis( - userId = userId, + userId = USER_ID, lastActiveTimeMillis = null, ) } @@ -132,24 +130,23 @@ class VaultLockManagerTest { @Suppress("MaxLineLength") @Test fun `app coming into foreground for the first time for OnAppRestart timeout should clear existing times and lock vaults if necessary`() { - val userId = "mockId-1" fakeAuthDiskSource.userState = MOCK_USER_STATE mutableVaultTimeoutActionStateFlow.value = VaultTimeoutAction.LOCK mutableVaultTimeoutStateFlow.value = VaultTimeout.OnAppRestart fakeAppForegroundManager.appForegroundState = AppForegroundState.BACKGROUNDED fakeAuthDiskSource.storeLastActiveTimeMillis( - userId = userId, + userId = USER_ID, lastActiveTimeMillis = 123L, ) - verifyUnlockedVaultBlocking(userId = userId) - assertTrue(vaultLockManager.isVaultUnlocked(userId)) + verifyUnlockedVaultBlocking(userId = USER_ID) + assertTrue(vaultLockManager.isVaultUnlocked(USER_ID)) fakeAppForegroundManager.appForegroundState = AppForegroundState.FOREGROUNDED - assertFalse(vaultLockManager.isVaultUnlocked(userId)) + assertFalse(vaultLockManager.isVaultUnlocked(USER_ID)) fakeAuthDiskSource.assertLastActiveTimeMillis( - userId = userId, + userId = USER_ID, lastActiveTimeMillis = null, ) } @@ -157,24 +154,23 @@ class VaultLockManagerTest { @Suppress("MaxLineLength") @Test fun `app coming into foreground for the first time for other timeout should clear existing times and lock vaults if necessary`() { - val userId = "mockId-1" fakeAuthDiskSource.userState = MOCK_USER_STATE mutableVaultTimeoutActionStateFlow.value = VaultTimeoutAction.LOCK mutableVaultTimeoutStateFlow.value = VaultTimeout.ThirtyMinutes fakeAppForegroundManager.appForegroundState = AppForegroundState.BACKGROUNDED fakeAuthDiskSource.storeLastActiveTimeMillis( - userId = userId, + userId = USER_ID, lastActiveTimeMillis = 123L, ) - verifyUnlockedVaultBlocking(userId = userId) - assertTrue(vaultLockManager.isVaultUnlocked(userId)) + verifyUnlockedVaultBlocking(userId = USER_ID) + assertTrue(vaultLockManager.isVaultUnlocked(USER_ID)) fakeAppForegroundManager.appForegroundState = AppForegroundState.FOREGROUNDED - assertFalse(vaultLockManager.isVaultUnlocked(userId)) + assertFalse(vaultLockManager.isVaultUnlocked(USER_ID)) fakeAuthDiskSource.assertLastActiveTimeMillis( - userId = userId, + userId = USER_ID, lastActiveTimeMillis = null, ) } @@ -182,24 +178,23 @@ class VaultLockManagerTest { @Suppress("MaxLineLength") @Test fun `app coming into foreground for the first time for non-Never timeout should clear existing times and perform timeout action`() { - val userId = "mockId-1" fakeAuthDiskSource.userState = MOCK_USER_STATE mutableVaultTimeoutActionStateFlow.value = VaultTimeoutAction.LOCK mutableVaultTimeoutStateFlow.value = VaultTimeout.ThirtyMinutes fakeAppForegroundManager.appForegroundState = AppForegroundState.BACKGROUNDED fakeAuthDiskSource.storeLastActiveTimeMillis( - userId = userId, + userId = USER_ID, lastActiveTimeMillis = 123L, ) - verifyUnlockedVaultBlocking(userId = userId) - assertTrue(vaultLockManager.isVaultUnlocked(userId)) + verifyUnlockedVaultBlocking(userId = USER_ID) + assertTrue(vaultLockManager.isVaultUnlocked(USER_ID)) fakeAppForegroundManager.appForegroundState = AppForegroundState.FOREGROUNDED - assertFalse(vaultLockManager.isVaultUnlocked(userId)) + assertFalse(vaultLockManager.isVaultUnlocked(USER_ID)) fakeAuthDiskSource.assertLastActiveTimeMillis( - userId = userId, + userId = USER_ID, lastActiveTimeMillis = null, ) } @@ -207,13 +202,12 @@ class VaultLockManagerTest { @Suppress("MaxLineLength") @Test fun `app coming into foreground subsequent times should perform timeout action if necessary and not clear existing times`() { - val userId = "mockId-1" fakeAuthDiskSource.userState = MOCK_USER_STATE // Start in a foregrounded state fakeAppForegroundManager.appForegroundState = AppForegroundState.FOREGROUNDED fakeAuthDiskSource.assertLastActiveTimeMillis( - userId = userId, + userId = USER_ID, lastActiveTimeMillis = null, ) @@ -228,11 +222,11 @@ class VaultLockManagerTest { mutableVaultTimeoutStateFlow.value = vaultTimeout fakeAppForegroundManager.appForegroundState = AppForegroundState.BACKGROUNDED fakeAuthDiskSource.storeLastActiveTimeMillis( - userId = userId, + userId = USER_ID, lastActiveTimeMillis = lastActiveTime, ) - verifyUnlockedVaultBlocking(userId = userId) - assertTrue(vaultLockManager.isVaultUnlocked(userId)) + verifyUnlockedVaultBlocking(userId = USER_ID) + assertTrue(vaultLockManager.isVaultUnlocked(USER_ID)) } // Test Lock action @@ -252,7 +246,7 @@ class VaultLockManagerTest { VaultTimeout.FourHours, is VaultTimeout.Custom, -> { - assertTrue(vaultLockManager.isVaultUnlocked(userId)) + assertTrue(vaultLockManager.isVaultUnlocked(USER_ID)) } // Before 6 minutes @@ -260,13 +254,13 @@ class VaultLockManagerTest { VaultTimeout.OneMinute, VaultTimeout.FiveMinutes, -> { - assertFalse(vaultLockManager.isVaultUnlocked(userId)) + assertFalse(vaultLockManager.isVaultUnlocked(USER_ID)) } } verify(exactly = 0) { userLogoutManager.softLogout(any()) } fakeAuthDiskSource.assertLastActiveTimeMillis( - userId = userId, + userId = USER_ID, lastActiveTimeMillis = lastActiveTime, ) } @@ -288,7 +282,7 @@ class VaultLockManagerTest { VaultTimeout.FourHours, is VaultTimeout.Custom, -> { - assertTrue(vaultLockManager.isVaultUnlocked(userId)) + assertTrue(vaultLockManager.isVaultUnlocked(USER_ID)) verify(exactly = 0) { userLogoutManager.softLogout(any()) } } @@ -297,13 +291,13 @@ class VaultLockManagerTest { VaultTimeout.OneMinute, VaultTimeout.FiveMinutes, -> { - assertFalse(vaultLockManager.isVaultUnlocked(userId)) - verify(exactly = 1) { userLogoutManager.softLogout(userId) } + assertFalse(vaultLockManager.isVaultUnlocked(USER_ID)) + verify(exactly = 1) { userLogoutManager.softLogout(USER_ID) } } } fakeAuthDiskSource.assertLastActiveTimeMillis( - userId = userId, + userId = USER_ID, lastActiveTimeMillis = lastActiveTime, ) } @@ -312,12 +306,11 @@ class VaultLockManagerTest { @Suppress("MaxLineLength") @Test fun `switching users should perform lock actions for each user if necessary and reset their last active times`() { - val userId1 = "mockId-1" val userId2 = "mockId-2" fakeAuthDiskSource.userState = UserStateJson( - activeUserId = userId1, + activeUserId = USER_ID, accounts = mapOf( - userId1 to MOCK_ACCOUNT, + USER_ID to MOCK_ACCOUNT, userId2 to MOCK_ACCOUNT.copy(profile = MOCK_PROFILE.copy(userId = userId2)), ), ) @@ -332,16 +325,16 @@ class VaultLockManagerTest { clearVerifications(userLogoutManager) mutableVaultTimeoutStateFlow.value = vaultTimeout fakeAuthDiskSource.storeLastActiveTimeMillis( - userId = userId1, + userId = USER_ID, lastActiveTimeMillis = lastActiveTime, ) fakeAuthDiskSource.storeLastActiveTimeMillis( userId = userId2, lastActiveTimeMillis = lastActiveTime, ) - verifyUnlockedVaultBlocking(userId = userId1) + verifyUnlockedVaultBlocking(userId = USER_ID) verifyUnlockedVaultBlocking(userId = userId2) - assertTrue(vaultLockManager.isVaultUnlocked(userId1)) + assertTrue(vaultLockManager.isVaultUnlocked(USER_ID)) assertTrue(vaultLockManager.isVaultUnlocked(userId2)) } @@ -351,10 +344,10 @@ class VaultLockManagerTest { resetTest(vaultTimeout = vaultTimeout) fakeAuthDiskSource.userState = fakeAuthDiskSource.userState?.copy( - activeUserId = if (fakeAuthDiskSource.userState?.activeUserId == userId1) { + activeUserId = if (fakeAuthDiskSource.userState?.activeUserId == USER_ID) { userId2 } else { - userId1 + USER_ID }, ) @@ -368,7 +361,7 @@ class VaultLockManagerTest { VaultTimeout.FourHours, is VaultTimeout.Custom, -> { - assertTrue(vaultLockManager.isVaultUnlocked(userId1)) + assertTrue(vaultLockManager.isVaultUnlocked(USER_ID)) assertTrue(vaultLockManager.isVaultUnlocked(userId2)) } @@ -377,14 +370,14 @@ class VaultLockManagerTest { VaultTimeout.OneMinute, VaultTimeout.FiveMinutes, -> { - assertFalse(vaultLockManager.isVaultUnlocked(userId1)) + assertFalse(vaultLockManager.isVaultUnlocked(USER_ID)) assertFalse(vaultLockManager.isVaultUnlocked(userId2)) } } verify(exactly = 0) { userLogoutManager.softLogout(any()) } fakeAuthDiskSource.assertLastActiveTimeMillis( - userId = userId1, + userId = USER_ID, lastActiveTimeMillis = elapsedRealtimeMillis, ) fakeAuthDiskSource.assertLastActiveTimeMillis( @@ -399,10 +392,10 @@ class VaultLockManagerTest { resetTest(vaultTimeout = vaultTimeout) fakeAuthDiskSource.userState = fakeAuthDiskSource.userState?.copy( - activeUserId = if (fakeAuthDiskSource.userState?.activeUserId == userId1) { + activeUserId = if (fakeAuthDiskSource.userState?.activeUserId == USER_ID) { userId2 } else { - userId1 + USER_ID }, ) @@ -416,7 +409,7 @@ class VaultLockManagerTest { VaultTimeout.FourHours, is VaultTimeout.Custom, -> { - assertTrue(vaultLockManager.isVaultUnlocked(userId1)) + assertTrue(vaultLockManager.isVaultUnlocked(USER_ID)) assertTrue(vaultLockManager.isVaultUnlocked(userId2)) verify(exactly = 0) { userLogoutManager.softLogout(any()) } } @@ -426,15 +419,15 @@ class VaultLockManagerTest { VaultTimeout.OneMinute, VaultTimeout.FiveMinutes, -> { - assertFalse(vaultLockManager.isVaultUnlocked(userId1)) + assertFalse(vaultLockManager.isVaultUnlocked(USER_ID)) assertFalse(vaultLockManager.isVaultUnlocked(userId2)) - verify(exactly = 1) { userLogoutManager.softLogout(userId1) } + verify(exactly = 1) { userLogoutManager.softLogout(USER_ID) } verify(exactly = 1) { userLogoutManager.softLogout(userId2) } } } fakeAuthDiskSource.assertLastActiveTimeMillis( - userId = userId1, + userId = USER_ID, lastActiveTimeMillis = elapsedRealtimeMillis, ) fakeAuthDiskSource.assertLastActiveTimeMillis( @@ -446,26 +439,25 @@ class VaultLockManagerTest { @Test fun `vaultTimeout updates to non-Never should clear the user's auto-unlock key`() = runTest { - val userId = "mockId-1" val userAutoUnlockKey = "userAutoUnlockKey" // Initialize Never state coEvery { - vaultSdkSource.getUserEncryptionKey(userId = userId) + vaultSdkSource.getUserEncryptionKey(userId = USER_ID) } returns userAutoUnlockKey.asSuccess() - verifyUnlockedVault(userId = userId) + verifyUnlockedVault(userId = USER_ID) fakeAuthDiskSource.userState = MOCK_USER_STATE mutableVaultTimeoutStateFlow.value = VaultTimeout.Never fakeAuthDiskSource.assertUserAutoUnlockKey( - userId = userId, + userId = USER_ID, userAutoUnlockKey = userAutoUnlockKey, ) mutableVaultTimeoutStateFlow.value = VaultTimeout.ThirtyMinutes fakeAuthDiskSource.assertUserAutoUnlockKey( - userId = userId, + userId = USER_ID, userAutoUnlockKey = null, ) } @@ -474,24 +466,23 @@ class VaultLockManagerTest { @Test fun `vaultTimeout update to Never for an unlocked account should store the user's encrypted key`() = runTest { - val userId = "mockId-1" val userAutoUnlockKey = "userAutoUnlockKey" fakeAuthDiskSource.userState = MOCK_USER_STATE coEvery { - vaultSdkSource.getUserEncryptionKey(userId = userId) + vaultSdkSource.getUserEncryptionKey(userId = USER_ID) } returns userAutoUnlockKey.asSuccess() - verifyUnlockedVault(userId = userId) + verifyUnlockedVault(userId = USER_ID) fakeAuthDiskSource.assertUserAutoUnlockKey( - userId = userId, + userId = USER_ID, userAutoUnlockKey = null, ) mutableVaultTimeoutStateFlow.value = VaultTimeout.Never fakeAuthDiskSource.assertUserAutoUnlockKey( - userId = userId, + userId = USER_ID, userAutoUnlockKey = userAutoUnlockKey, ) } @@ -499,54 +490,51 @@ class VaultLockManagerTest { @Suppress("MaxLineLength") @Test fun `vaultTimeout update to Never for a locked account when there is no stored private key should do nothing`() { - val userId = "mockId-1" fakeAuthDiskSource.userState = MOCK_USER_STATE - assertFalse(vaultLockManager.isVaultUnlocked(userId = userId)) + assertFalse(vaultLockManager.isVaultUnlocked(userId = USER_ID)) mutableVaultTimeoutStateFlow.value = VaultTimeout.Never - assertFalse(vaultLockManager.isVaultUnlocked(userId = userId)) + assertFalse(vaultLockManager.isVaultUnlocked(userId = USER_ID)) } @Suppress("MaxLineLength") @Test fun `vaultTimeout update to Never for a locked account when there is no stored auto-unlock key should do nothing`() { - val userId = "mockId-1" val privateKey = "privateKey" fakeAuthDiskSource.apply { userState = MOCK_USER_STATE storePrivateKey( - userId = userId, + userId = USER_ID, privateKey = privateKey, ) } - assertFalse(vaultLockManager.isVaultUnlocked(userId = userId)) + assertFalse(vaultLockManager.isVaultUnlocked(userId = USER_ID)) mutableVaultTimeoutStateFlow.value = VaultTimeout.Never - assertFalse(vaultLockManager.isVaultUnlocked(userId = userId)) + assertFalse(vaultLockManager.isVaultUnlocked(userId = USER_ID)) } @Suppress("MaxLineLength") @Test fun `vaultTimeout update to Never for a locked account when there is a stored auto-unlock key should unlock the vault`() { - val userId = "mockId-1" val privateKey = "privateKey" val userAutoUnlockKey = "userAutoUnlockKey" fakeAuthDiskSource.apply { userState = MOCK_USER_STATE storePrivateKey( - userId = userId, + userId = USER_ID, privateKey = privateKey, ) storeUserAutoUnlockKey( - userId = userId, + userId = USER_ID, userAutoUnlockKey = userAutoUnlockKey, ) } coEvery { vaultSdkSource.initializeCrypto( - userId = userId, + userId = USER_ID, request = InitUserCryptoRequest( kdfParams = MOCK_PROFILE.toSdkParams(), email = MOCK_PROFILE.email, @@ -558,15 +546,15 @@ class VaultLockManagerTest { ) } returns InitializeCryptoResult.Success.asSuccess() - assertFalse(vaultLockManager.isVaultUnlocked(userId = userId)) + assertFalse(vaultLockManager.isVaultUnlocked(userId = USER_ID)) mutableVaultTimeoutStateFlow.value = VaultTimeout.Never - assertTrue(vaultLockManager.isVaultUnlocked(userId = userId)) + assertTrue(vaultLockManager.isVaultUnlocked(userId = USER_ID)) coVerify { vaultSdkSource.initializeCrypto( - userId = userId, + userId = USER_ID, request = InitUserCryptoRequest( kdfParams = MOCK_PROFILE.toSdkParams(), email = MOCK_PROFILE.email, @@ -582,86 +570,80 @@ class VaultLockManagerTest { @Test fun `isVaultUnlocked should return the correct value based on the vault lock state`() = runTest { - val userId = "userId" - assertFalse(vaultLockManager.isVaultUnlocked(userId = userId)) + assertFalse(vaultLockManager.isVaultUnlocked(userId = USER_ID)) - verifyUnlockedVault(userId = userId) + verifyUnlockedVault(userId = USER_ID) - assertTrue(vaultLockManager.isVaultUnlocked(userId = userId)) + assertTrue(vaultLockManager.isVaultUnlocked(userId = USER_ID)) } @Test fun `isVaultLocking should return the correct value based on the vault unlocking state`() = runTest { - val userId = "userId" - assertFalse(vaultLockManager.isVaultUnlocking(userId = userId)) + assertFalse(vaultLockManager.isVaultUnlocking(userId = USER_ID)) val unlockingJob = async { - verifyUnlockingVault(userId = userId) + verifyUnlockingVault(userId = USER_ID) } this.testScheduler.advanceUntilIdle() - assertTrue(vaultLockManager.isVaultUnlocking(userId = userId)) + assertTrue(vaultLockManager.isVaultUnlocking(userId = USER_ID)) unlockingJob.cancel() this.testScheduler.advanceUntilIdle() - assertFalse(vaultLockManager.isVaultUnlocking(userId = userId)) + assertFalse(vaultLockManager.isVaultUnlocking(userId = USER_ID)) } @Suppress("MaxLineLength") @Test fun `lockVault when non-Never timeout should lock the given account if it is currently unlocked`() = runTest { - val userId = "userId" - verifyUnlockedVault(userId = userId) + verifyUnlockedVault(userId = USER_ID) mutableVaultTimeoutStateFlow.value = VaultTimeout.ThirtyMinutes assertEquals( - VaultState( - unlockedVaultUserIds = setOf(userId), - unlockingVaultUserIds = emptySet(), + listOf( + VaultUnlockData( + userId = USER_ID, + status = VaultUnlockData.Status.UNLOCKED, + ), ), - vaultLockManager.vaultStateFlow.value, + vaultLockManager.vaultUnlockDataStateFlow.value, ) - vaultLockManager.lockVault(userId = userId) + vaultLockManager.lockVault(userId = USER_ID) assertEquals( - VaultState( - unlockedVaultUserIds = emptySet(), - unlockingVaultUserIds = emptySet(), - ), - vaultLockManager.vaultStateFlow.value, + emptyList(), + vaultLockManager.vaultUnlockDataStateFlow.value, ) - verify { vaultSdkSource.clearCrypto(userId = userId) } + verify { vaultSdkSource.clearCrypto(userId = USER_ID) } } @Test fun `lockVault when Never timeout should lock the given account if it is currently unlocked`() = runTest { - val userId = "userId" - verifyUnlockedVault(userId = userId) + verifyUnlockedVault(userId = USER_ID) mutableVaultTimeoutStateFlow.value = VaultTimeout.Never assertEquals( - VaultState( - unlockedVaultUserIds = setOf(userId), - unlockingVaultUserIds = emptySet(), + listOf( + VaultUnlockData( + userId = USER_ID, + status = VaultUnlockData.Status.UNLOCKED, + ), ), - vaultLockManager.vaultStateFlow.value, + vaultLockManager.vaultUnlockDataStateFlow.value, ) - vaultLockManager.lockVault(userId = userId) + vaultLockManager.lockVault(userId = USER_ID) assertEquals( - VaultState( - unlockedVaultUserIds = emptySet(), - unlockingVaultUserIds = emptySet(), - ), - vaultLockManager.vaultStateFlow.value, + emptyList(), + vaultLockManager.vaultUnlockDataStateFlow.value, ) - verify { vaultSdkSource.clearCrypto(userId = userId) } + verify { vaultSdkSource.clearCrypto(userId = USER_ID) } } @Suppress("MaxLineLength") @@ -669,34 +651,31 @@ class VaultLockManagerTest { fun `lockVaultForCurrentUser should lock the vault for the current user if it is currently unlocked`() = runTest { fakeAuthDiskSource.userState = MOCK_USER_STATE - val userId = "mockId-1" - verifyUnlockedVault(userId = userId) + verifyUnlockedVault(userId = USER_ID) assertEquals( - VaultState( - unlockedVaultUserIds = setOf(userId), - unlockingVaultUserIds = emptySet(), + listOf( + VaultUnlockData( + userId = USER_ID, + status = VaultUnlockData.Status.UNLOCKED, + ), ), - vaultLockManager.vaultStateFlow.value, + vaultLockManager.vaultUnlockDataStateFlow.value, ) vaultLockManager.lockVaultForCurrentUser() assertEquals( - VaultState( - unlockedVaultUserIds = emptySet(), - unlockingVaultUserIds = emptySet(), - ), - vaultLockManager.vaultStateFlow.value, + emptyList(), + vaultLockManager.vaultUnlockDataStateFlow.value, ) - verify { vaultSdkSource.clearCrypto(userId = userId) } + verify { vaultSdkSource.clearCrypto(userId = USER_ID) } } @Suppress("MaxLineLength") @Test fun `unlockVault with initializeCrypto success for a non-Never VaultTimeout should return Success`() = runTest { - val userId = "userId" val kdf = MOCK_PROFILE.toSdkParams() val email = MOCK_PROFILE.email val masterPassword = "drowssap" @@ -705,7 +684,7 @@ class VaultLockManagerTest { val organizationKeys = mapOf("orgId1" to "orgKey1") coEvery { vaultSdkSource.initializeCrypto( - userId = userId, + userId = USER_ID, request = InitUserCryptoRequest( kdfParams = kdf, email = email, @@ -719,25 +698,22 @@ class VaultLockManagerTest { } returns InitializeCryptoResult.Success.asSuccess() coEvery { vaultSdkSource.initializeOrganizationCrypto( - userId = userId, + userId = USER_ID, request = InitOrgCryptoRequest(organizationKeys = organizationKeys), ) } returns InitializeCryptoResult.Success.asSuccess() assertEquals( - VaultState( - unlockedVaultUserIds = emptySet(), - unlockingVaultUserIds = emptySet(), - ), - vaultLockManager.vaultStateFlow.value, + emptyList(), + vaultLockManager.vaultUnlockDataStateFlow.value, ) mutableVaultTimeoutStateFlow.value = VaultTimeout.ThirtyMinutes fakeAuthDiskSource.storeUserAutoUnlockKey( - userId = userId, + userId = USER_ID, userAutoUnlockKey = null, ) val result = vaultLockManager.unlockVault( - userId = userId, + userId = USER_ID, kdf = kdf, email = email, initUserCryptoMethod = InitUserCryptoMethod.Password( @@ -750,24 +726,26 @@ class VaultLockManagerTest { assertEquals(VaultUnlockResult.Success, result) assertEquals( - VaultState( - unlockedVaultUserIds = setOf(userId), - unlockingVaultUserIds = emptySet(), + listOf( + VaultUnlockData( + userId = USER_ID, + status = VaultUnlockData.Status.UNLOCKED, + ), ), - vaultLockManager.vaultStateFlow.value, + vaultLockManager.vaultUnlockDataStateFlow.value, ) fakeAuthDiskSource.assertUserAutoUnlockKey( - userId = userId, + userId = USER_ID, userAutoUnlockKey = null, ) fakeAuthDiskSource.assertMasterPasswordHash( - userId = userId, + userId = USER_ID, passwordHash = "hashedPassword", ) coVerify(exactly = 1) { vaultSdkSource.initializeCrypto( - userId = userId, + userId = USER_ID, request = InitUserCryptoRequest( kdfParams = kdf, email = email, @@ -781,7 +759,7 @@ class VaultLockManagerTest { } coVerify(exactly = 1) { vaultSdkSource.initializeOrganizationCrypto( - userId = userId, + userId = USER_ID, request = InitOrgCryptoRequest(organizationKeys = organizationKeys), ) } @@ -791,7 +769,6 @@ class VaultLockManagerTest { @Test fun `unlockVault with initializeCrypto success for a Never VaultTimeout should return Success, save the auto-unlock key, and clear invalid unlock attempts`() = runTest { - val userId = "userId" val kdf = MOCK_PROFILE.toSdkParams() val email = MOCK_PROFILE.email val masterPassword = "drowssap" @@ -801,7 +778,7 @@ class VaultLockManagerTest { val userAutoUnlockKey = "userAutoUnlockKey" coEvery { vaultSdkSource.initializeCrypto( - userId = userId, + userId = USER_ID, request = InitUserCryptoRequest( kdfParams = kdf, email = email, @@ -815,34 +792,31 @@ class VaultLockManagerTest { } returns InitializeCryptoResult.Success.asSuccess() coEvery { vaultSdkSource.initializeOrganizationCrypto( - userId = userId, + userId = USER_ID, request = InitOrgCryptoRequest(organizationKeys = organizationKeys), ) } returns InitializeCryptoResult.Success.asSuccess() coEvery { - vaultSdkSource.getUserEncryptionKey(userId = userId) + vaultSdkSource.getUserEncryptionKey(userId = USER_ID) } returns userAutoUnlockKey.asSuccess() assertEquals( - VaultState( - unlockedVaultUserIds = emptySet(), - unlockingVaultUserIds = emptySet(), - ), - vaultLockManager.vaultStateFlow.value, + emptyList(), + vaultLockManager.vaultUnlockDataStateFlow.value, ) mutableVaultTimeoutStateFlow.value = VaultTimeout.Never fakeAuthDiskSource.apply { storeUserAutoUnlockKey( - userId = userId, + userId = USER_ID, userAutoUnlockKey = null, ) storeInvalidUnlockAttempts( - userId = userId, + userId = USER_ID, invalidUnlockAttempts = 4, ) } val result = vaultLockManager.unlockVault( - userId = userId, + userId = USER_ID, kdf = kdf, email = email, initUserCryptoMethod = InitUserCryptoMethod.Password( @@ -855,26 +829,28 @@ class VaultLockManagerTest { assertEquals(VaultUnlockResult.Success, result) assertEquals( - VaultState( - unlockedVaultUserIds = setOf(userId), - unlockingVaultUserIds = emptySet(), + listOf( + VaultUnlockData( + userId = USER_ID, + status = VaultUnlockData.Status.UNLOCKED, + ), ), - vaultLockManager.vaultStateFlow.value, + vaultLockManager.vaultUnlockDataStateFlow.value, ) fakeAuthDiskSource.apply { assertUserAutoUnlockKey( - userId = userId, + userId = USER_ID, userAutoUnlockKey = userAutoUnlockKey, ) assertInvalidUnlockAttempts( - userId = userId, + userId = USER_ID, invalidUnlockAttempts = null, ) } coVerify(exactly = 1) { vaultSdkSource.initializeCrypto( - userId = userId, + userId = USER_ID, request = InitUserCryptoRequest( kdfParams = kdf, email = email, @@ -888,12 +864,12 @@ class VaultLockManagerTest { } coVerify(exactly = 1) { vaultSdkSource.initializeOrganizationCrypto( - userId = userId, + userId = USER_ID, request = InitOrgCryptoRequest(organizationKeys = organizationKeys), ) } coVerify { - vaultSdkSource.getUserEncryptionKey(userId = userId) + vaultSdkSource.getUserEncryptionKey(userId = USER_ID) } } @@ -901,7 +877,6 @@ class VaultLockManagerTest { @Test fun `unlockVault with initializeCrypto authentication failure for users should return AuthenticationError and increment invalid unlock attempts`() = runTest { - val userId = "userId" val kdf = MOCK_PROFILE.toSdkParams() val email = MOCK_PROFILE.email val masterPassword = "drowssap" @@ -910,7 +885,7 @@ class VaultLockManagerTest { val organizationKeys = mapOf("orgId1" to "orgKey1") coEvery { vaultSdkSource.initializeCrypto( - userId = userId, + userId = USER_ID, request = InitUserCryptoRequest( kdfParams = kdf, email = email, @@ -924,19 +899,16 @@ class VaultLockManagerTest { } returns InitializeCryptoResult.AuthenticationError.asSuccess() assertEquals( - VaultState( - unlockedVaultUserIds = emptySet(), - unlockingVaultUserIds = emptySet(), - ), - vaultLockManager.vaultStateFlow.value, + emptyList(), + vaultLockManager.vaultUnlockDataStateFlow.value, ) fakeAuthDiskSource.storeInvalidUnlockAttempts( - userId = userId, + userId = USER_ID, invalidUnlockAttempts = 1, ) val result = vaultLockManager.unlockVault( - userId = userId, + userId = USER_ID, kdf = kdf, email = email, initUserCryptoMethod = InitUserCryptoMethod.Password( @@ -949,19 +921,16 @@ class VaultLockManagerTest { assertEquals(VaultUnlockResult.AuthenticationError, result) assertEquals( - VaultState( - unlockedVaultUserIds = emptySet(), - unlockingVaultUserIds = emptySet(), - ), - vaultLockManager.vaultStateFlow.value, + emptyList(), + vaultLockManager.vaultUnlockDataStateFlow.value, ) fakeAuthDiskSource.assertInvalidUnlockAttempts( - userId = userId, + userId = USER_ID, invalidUnlockAttempts = 2, ) coVerify(exactly = 1) { vaultSdkSource.initializeCrypto( - userId = userId, + userId = USER_ID, request = InitUserCryptoRequest( kdfParams = kdf, email = email, @@ -979,7 +948,6 @@ class VaultLockManagerTest { @Test fun `unlockVault with initializeCrypto authentication failure for orgs should return AuthenticationError and increment invalid unlock attempts`() = runTest { - val userId = "userId" val kdf = MOCK_PROFILE.toSdkParams() val email = MOCK_PROFILE.email val masterPassword = "drowssap" @@ -988,7 +956,7 @@ class VaultLockManagerTest { val organizationKeys = mapOf("orgId1" to "orgKey1") coEvery { vaultSdkSource.initializeCrypto( - userId = userId, + userId = USER_ID, request = InitUserCryptoRequest( kdfParams = kdf, email = email, @@ -1002,25 +970,22 @@ class VaultLockManagerTest { } returns InitializeCryptoResult.Success.asSuccess() coEvery { vaultSdkSource.initializeOrganizationCrypto( - userId = userId, + userId = USER_ID, request = InitOrgCryptoRequest(organizationKeys = organizationKeys), ) } returns InitializeCryptoResult.AuthenticationError.asSuccess() assertEquals( - VaultState( - unlockedVaultUserIds = emptySet(), - unlockingVaultUserIds = emptySet(), - ), - vaultLockManager.vaultStateFlow.value, + emptyList(), + vaultLockManager.vaultUnlockDataStateFlow.value, ) fakeAuthDiskSource.storeInvalidUnlockAttempts( - userId = userId, + userId = USER_ID, invalidUnlockAttempts = 1, ) val result = vaultLockManager.unlockVault( - userId = userId, + userId = USER_ID, kdf = kdf, email = email, initUserCryptoMethod = InitUserCryptoMethod.Password( @@ -1033,19 +998,16 @@ class VaultLockManagerTest { assertEquals(VaultUnlockResult.AuthenticationError, result) assertEquals( - VaultState( - unlockedVaultUserIds = emptySet(), - unlockingVaultUserIds = emptySet(), - ), - vaultLockManager.vaultStateFlow.value, + emptyList(), + vaultLockManager.vaultUnlockDataStateFlow.value, ) fakeAuthDiskSource.assertInvalidUnlockAttempts( - userId = userId, + userId = USER_ID, invalidUnlockAttempts = 2, ) coVerify(exactly = 1) { vaultSdkSource.initializeCrypto( - userId = userId, + userId = USER_ID, request = InitUserCryptoRequest( kdfParams = kdf, email = email, @@ -1059,7 +1021,7 @@ class VaultLockManagerTest { } coVerify(exactly = 1) { vaultSdkSource.initializeOrganizationCrypto( - userId = userId, + userId = USER_ID, request = InitOrgCryptoRequest(organizationKeys = organizationKeys), ) } @@ -1069,7 +1031,6 @@ class VaultLockManagerTest { @Test fun `unlockVault with initializeCrypto failure for users should return GenericError and increment invalid unlock attempts`() = runTest { - val userId = "userId" val kdf = MOCK_PROFILE.toSdkParams() val email = MOCK_PROFILE.email val masterPassword = "drowssap" @@ -1078,7 +1039,7 @@ class VaultLockManagerTest { val organizationKeys = mapOf("orgId1" to "orgKey1") coEvery { vaultSdkSource.initializeCrypto( - userId = userId, + userId = USER_ID, request = InitUserCryptoRequest( kdfParams = kdf, email = email, @@ -1091,19 +1052,16 @@ class VaultLockManagerTest { ) } returns Throwable("Fail").asFailure() assertEquals( - VaultState( - unlockedVaultUserIds = emptySet(), - unlockingVaultUserIds = emptySet(), - ), - vaultLockManager.vaultStateFlow.value, + emptyList(), + vaultLockManager.vaultUnlockDataStateFlow.value, ) fakeAuthDiskSource.storeInvalidUnlockAttempts( - userId = userId, + userId = USER_ID, invalidUnlockAttempts = 1, ) val result = vaultLockManager.unlockVault( - userId = userId, + userId = USER_ID, kdf = kdf, email = email, initUserCryptoMethod = InitUserCryptoMethod.Password( @@ -1116,19 +1074,16 @@ class VaultLockManagerTest { assertEquals(VaultUnlockResult.GenericError, result) assertEquals( - VaultState( - unlockedVaultUserIds = emptySet(), - unlockingVaultUserIds = emptySet(), - ), - vaultLockManager.vaultStateFlow.value, + emptyList(), + vaultLockManager.vaultUnlockDataStateFlow.value, ) fakeAuthDiskSource.assertInvalidUnlockAttempts( - userId = userId, + userId = USER_ID, invalidUnlockAttempts = 2, ) coVerify(exactly = 1) { vaultSdkSource.initializeCrypto( - userId = userId, + userId = USER_ID, request = InitUserCryptoRequest( kdfParams = kdf, email = email, @@ -1146,7 +1101,6 @@ class VaultLockManagerTest { @Test fun `unlockVault with initializeCrypto failure for orgs should return GenericError and increment invalid unlock attempts`() = runTest { - val userId = "userId" val kdf = MOCK_PROFILE.toSdkParams() val email = MOCK_PROFILE.email val masterPassword = "drowssap" @@ -1155,7 +1109,7 @@ class VaultLockManagerTest { val organizationKeys = mapOf("orgId1" to "orgKey1") coEvery { vaultSdkSource.initializeCrypto( - userId = userId, + userId = USER_ID, request = InitUserCryptoRequest( kdfParams = kdf, email = email, @@ -1169,24 +1123,21 @@ class VaultLockManagerTest { } returns InitializeCryptoResult.Success.asSuccess() coEvery { vaultSdkSource.initializeOrganizationCrypto( - userId = userId, + userId = USER_ID, request = InitOrgCryptoRequest(organizationKeys = organizationKeys), ) } returns Throwable("Fail").asFailure() assertEquals( - VaultState( - unlockedVaultUserIds = emptySet(), - unlockingVaultUserIds = emptySet(), - ), - vaultLockManager.vaultStateFlow.value, + emptyList(), + vaultLockManager.vaultUnlockDataStateFlow.value, ) fakeAuthDiskSource.storeInvalidUnlockAttempts( - userId = userId, + userId = USER_ID, invalidUnlockAttempts = 1, ) val result = vaultLockManager.unlockVault( - userId = userId, + userId = USER_ID, kdf = kdf, email = email, initUserCryptoMethod = InitUserCryptoMethod.Password( @@ -1199,19 +1150,16 @@ class VaultLockManagerTest { assertEquals(VaultUnlockResult.GenericError, result) assertEquals( - VaultState( - unlockedVaultUserIds = emptySet(), - unlockingVaultUserIds = emptySet(), - ), - vaultLockManager.vaultStateFlow.value, + emptyList(), + vaultLockManager.vaultUnlockDataStateFlow.value, ) fakeAuthDiskSource.assertInvalidUnlockAttempts( - userId = userId, + userId = USER_ID, invalidUnlockAttempts = 2, ) coVerify(exactly = 1) { vaultSdkSource.initializeCrypto( - userId = userId, + userId = USER_ID, request = InitUserCryptoRequest( kdfParams = kdf, email = email, @@ -1225,7 +1173,7 @@ class VaultLockManagerTest { } coVerify(exactly = 1) { vaultSdkSource.initializeOrganizationCrypto( - userId = userId, + userId = USER_ID, request = InitOrgCryptoRequest(organizationKeys = organizationKeys), ) } @@ -1235,7 +1183,6 @@ class VaultLockManagerTest { @Test fun `unlockVault error when reaching the maximum number of invalid unlock attempts should log out the user`() = runTest { - val userId = "userId" val kdf = MOCK_PROFILE.toSdkParams() val email = MOCK_PROFILE.email val masterPassword = "drowssap" @@ -1244,7 +1191,7 @@ class VaultLockManagerTest { val organizationKeys = mapOf("orgId1" to "orgKey1") coEvery { vaultSdkSource.initializeCrypto( - userId = userId, + userId = USER_ID, request = InitUserCryptoRequest( kdfParams = kdf, email = email, @@ -1258,24 +1205,21 @@ class VaultLockManagerTest { } returns InitializeCryptoResult.Success.asSuccess() coEvery { vaultSdkSource.initializeOrganizationCrypto( - userId = userId, + userId = USER_ID, request = InitOrgCryptoRequest(organizationKeys = organizationKeys), ) } returns Throwable("Fail").asFailure() assertEquals( - VaultState( - unlockedVaultUserIds = emptySet(), - unlockingVaultUserIds = emptySet(), - ), - vaultLockManager.vaultStateFlow.value, + emptyList(), + vaultLockManager.vaultUnlockDataStateFlow.value, ) fakeAuthDiskSource.storeInvalidUnlockAttempts( - userId = userId, + userId = USER_ID, invalidUnlockAttempts = 4, ) val result = vaultLockManager.unlockVault( - userId = userId, + userId = USER_ID, kdf = kdf, email = email, initUserCryptoMethod = InitUserCryptoMethod.Password( @@ -1287,22 +1231,19 @@ class VaultLockManagerTest { ) fakeAuthDiskSource.assertInvalidUnlockAttempts( - userId = userId, + userId = USER_ID, invalidUnlockAttempts = 5, ) - verify { userLogoutManager.logout(userId = userId) } + verify { userLogoutManager.logout(userId = USER_ID) } assertEquals(VaultUnlockResult.GenericError, result) assertEquals( - VaultState( - unlockedVaultUserIds = emptySet(), - unlockingVaultUserIds = emptySet(), - ), - vaultLockManager.vaultStateFlow.value, + emptyList(), + vaultLockManager.vaultUnlockDataStateFlow.value, ) coVerify(exactly = 1) { vaultSdkSource.initializeCrypto( - userId = userId, + userId = USER_ID, request = InitUserCryptoRequest( kdfParams = kdf, email = email, @@ -1316,7 +1257,7 @@ class VaultLockManagerTest { } coVerify(exactly = 1) { vaultSdkSource.initializeOrganizationCrypto( - userId = userId, + userId = USER_ID, request = InitOrgCryptoRequest(organizationKeys = organizationKeys), ) } @@ -1442,6 +1383,8 @@ class VaultLockManagerTest { } } +private const val USER_ID = "mockId-1" + private val MOCK_TIMEOUTS = VaultTimeout.Type.entries.map { when (it) { VaultTimeout.Type.IMMEDIATELY -> VaultTimeout.Immediately @@ -1458,7 +1401,7 @@ private val MOCK_TIMEOUTS = VaultTimeout.Type.entries.map { } private val MOCK_PROFILE = AccountJson.Profile( - userId = "mockId-1", + userId = USER_ID, email = "email", isEmailVerified = true, name = null, @@ -1486,8 +1429,8 @@ private val MOCK_ACCOUNT = AccountJson( ) private val MOCK_USER_STATE = UserStateJson( - activeUserId = "mockId-1", + activeUserId = USER_ID, accounts = mapOf( - "mockId-1" to MOCK_ACCOUNT, + USER_ID to MOCK_ACCOUNT, ), ) diff --git a/app/src/test/java/com/x8bit/bitwarden/data/vault/repository/VaultRepositoryTest.kt b/app/src/test/java/com/x8bit/bitwarden/data/vault/repository/VaultRepositoryTest.kt index be1d4d6964..6764c7ce05 100644 --- a/app/src/test/java/com/x8bit/bitwarden/data/vault/repository/VaultRepositoryTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/data/vault/repository/VaultRepositoryTest.kt @@ -91,7 +91,7 @@ import com.x8bit.bitwarden.data.vault.repository.model.UpdateCipherResult import com.x8bit.bitwarden.data.vault.repository.model.UpdateFolderResult import com.x8bit.bitwarden.data.vault.repository.model.UpdateSendResult import com.x8bit.bitwarden.data.vault.repository.model.VaultData -import com.x8bit.bitwarden.data.vault.repository.model.VaultState +import com.x8bit.bitwarden.data.vault.repository.model.VaultUnlockData import com.x8bit.bitwarden.data.vault.repository.model.VaultUnlockResult import com.x8bit.bitwarden.data.vault.repository.model.createMockDomainsData import com.x8bit.bitwarden.data.vault.repository.util.toDomainsData @@ -148,14 +148,11 @@ class VaultRepositoryTest { private val vaultSdkSource: VaultSdkSource = mockk { every { clearCrypto(userId = any()) } just runs } - private val mutableVaultStateFlow = MutableStateFlow( - VaultState( - unlockedVaultUserIds = emptySet(), - unlockingVaultUserIds = emptySet(), - ), + private val mutableVaultStateFlow = MutableStateFlow>( + emptyList(), ) private val vaultLockManager: VaultLockManager = mockk { - every { vaultStateFlow } returns mutableVaultStateFlow + every { vaultUnlockDataStateFlow } returns mutableVaultStateFlow every { isVaultUnlocked(any()) } returns false every { isVaultUnlocking(any()) } returns false every { lockVault(any()) } just runs @@ -700,22 +697,16 @@ class VaultRepositoryTest { runTest { fakeAuthDiskSource.userState = null assertEquals( - VaultState( - unlockedVaultUserIds = emptySet(), - unlockingVaultUserIds = emptySet(), - ), - vaultRepository.vaultStateFlow.value, + emptyList(), + vaultRepository.vaultUnlockDataStateFlow.value, ) val result = vaultRepository.unlockVaultWithBiometrics() assertEquals(VaultUnlockResult.InvalidStateError, result) assertEquals( - VaultState( - unlockedVaultUserIds = emptySet(), - unlockingVaultUserIds = emptySet(), - ), - vaultRepository.vaultStateFlow.value, + emptyList(), + vaultRepository.vaultUnlockDataStateFlow.value, ) } @@ -724,11 +715,8 @@ class VaultRepositoryTest { fun `unlockVaultWithBiometrics with missing biometrics key should return InvalidStateError`() = runTest { assertEquals( - VaultState( - unlockedVaultUserIds = emptySet(), - unlockingVaultUserIds = emptySet(), - ), - vaultRepository.vaultStateFlow.value, + emptyList(), + vaultRepository.vaultUnlockDataStateFlow.value, ) fakeAuthDiskSource.userState = MOCK_USER_STATE val userId = MOCK_USER_STATE.activeUserId @@ -738,11 +726,8 @@ class VaultRepositoryTest { assertEquals(VaultUnlockResult.InvalidStateError, result) assertEquals( - VaultState( - unlockedVaultUserIds = emptySet(), - unlockingVaultUserIds = emptySet(), - ), - vaultRepository.vaultStateFlow.value, + emptyList(), + vaultRepository.vaultUnlockDataStateFlow.value, ) } @@ -864,11 +849,8 @@ class VaultRepositoryTest { runTest { fakeAuthDiskSource.userState = null assertEquals( - VaultState( - unlockedVaultUserIds = emptySet(), - unlockingVaultUserIds = emptySet(), - ), - vaultRepository.vaultStateFlow.value, + emptyList(), + vaultRepository.vaultUnlockDataStateFlow.value, ) val result = vaultRepository.unlockVaultWithMasterPassword(masterPassword = "") @@ -878,11 +860,8 @@ class VaultRepositoryTest { result, ) assertEquals( - VaultState( - unlockedVaultUserIds = emptySet(), - unlockingVaultUserIds = emptySet(), - ), - vaultRepository.vaultStateFlow.value, + emptyList(), + vaultRepository.vaultUnlockDataStateFlow.value, ) } @@ -891,11 +870,8 @@ class VaultRepositoryTest { fun `unlockVaultWithMasterPassword with missing user key should return InvalidStateError`() = runTest { assertEquals( - VaultState( - unlockedVaultUserIds = emptySet(), - unlockingVaultUserIds = emptySet(), - ), - vaultRepository.vaultStateFlow.value, + emptyList(), + vaultRepository.vaultUnlockDataStateFlow.value, ) val result = vaultRepository.unlockVaultWithMasterPassword(masterPassword = "") @@ -913,11 +889,8 @@ class VaultRepositoryTest { result, ) assertEquals( - VaultState( - unlockedVaultUserIds = emptySet(), - unlockingVaultUserIds = emptySet(), - ), - vaultRepository.vaultStateFlow.value, + emptyList(), + vaultRepository.vaultUnlockDataStateFlow.value, ) } @@ -926,11 +899,8 @@ class VaultRepositoryTest { fun `unlockVaultWithMasterPassword with missing private key should return InvalidStateError`() = runTest { assertEquals( - VaultState( - unlockedVaultUserIds = emptySet(), - unlockingVaultUserIds = emptySet(), - ), - vaultRepository.vaultStateFlow.value, + emptyList(), + vaultRepository.vaultUnlockDataStateFlow.value, ) val result = vaultRepository.unlockVaultWithMasterPassword(masterPassword = "") fakeAuthDiskSource.storeUserKey( @@ -948,11 +918,8 @@ class VaultRepositoryTest { ) assertEquals( - VaultState( - unlockedVaultUserIds = emptySet(), - unlockingVaultUserIds = emptySet(), - ), - vaultRepository.vaultStateFlow.value, + emptyList(), + vaultRepository.vaultUnlockDataStateFlow.value, ) } @@ -1102,11 +1069,8 @@ class VaultRepositoryTest { runTest { fakeAuthDiskSource.userState = null assertEquals( - VaultState( - unlockedVaultUserIds = emptySet(), - unlockingVaultUserIds = emptySet(), - ), - vaultRepository.vaultStateFlow.value, + emptyList(), + vaultRepository.vaultUnlockDataStateFlow.value, ) val result = vaultRepository.unlockVaultWithPin(pin = "1234") @@ -1116,11 +1080,8 @@ class VaultRepositoryTest { result, ) assertEquals( - VaultState( - unlockedVaultUserIds = emptySet(), - unlockingVaultUserIds = emptySet(), - ), - vaultRepository.vaultStateFlow.value, + emptyList(), + vaultRepository.vaultUnlockDataStateFlow.value, ) } @@ -1129,11 +1090,8 @@ class VaultRepositoryTest { fun `unlockVaultWithPin with missing pin-protected user key should return InvalidStateError`() = runTest { assertEquals( - VaultState( - unlockedVaultUserIds = emptySet(), - unlockingVaultUserIds = emptySet(), - ), - vaultRepository.vaultStateFlow.value, + emptyList(), + vaultRepository.vaultUnlockDataStateFlow.value, ) val result = vaultRepository.unlockVaultWithPin(pin = "1234") @@ -1151,11 +1109,8 @@ class VaultRepositoryTest { result, ) assertEquals( - VaultState( - unlockedVaultUserIds = emptySet(), - unlockingVaultUserIds = emptySet(), - ), - vaultRepository.vaultStateFlow.value, + emptyList(), + vaultRepository.vaultUnlockDataStateFlow.value, ) } @@ -1164,11 +1119,8 @@ class VaultRepositoryTest { fun `unlockVaultWithPin with missing private key should return InvalidStateError`() = runTest { assertEquals( - VaultState( - unlockedVaultUserIds = emptySet(), - unlockingVaultUserIds = emptySet(), - ), - vaultRepository.vaultStateFlow.value, + emptyList(), + vaultRepository.vaultUnlockDataStateFlow.value, ) val result = vaultRepository.unlockVaultWithPin(pin = "1234") fakeAuthDiskSource.storePinProtectedUserKey( @@ -1185,11 +1137,8 @@ class VaultRepositoryTest { result, ) assertEquals( - VaultState( - unlockedVaultUserIds = emptySet(), - unlockingVaultUserIds = emptySet(), - ), - vaultRepository.vaultStateFlow.value, + emptyList(), + vaultRepository.vaultUnlockDataStateFlow.value, ) } diff --git a/app/src/test/java/com/x8bit/bitwarden/data/vault/repository/util/VaultUnlockDataExtensionsTest.kt b/app/src/test/java/com/x8bit/bitwarden/data/vault/repository/util/VaultUnlockDataExtensionsTest.kt new file mode 100644 index 0000000000..2de7b95d6d --- /dev/null +++ b/app/src/test/java/com/x8bit/bitwarden/data/vault/repository/util/VaultUnlockDataExtensionsTest.kt @@ -0,0 +1,56 @@ +package com.x8bit.bitwarden.data.vault.repository.util + +import com.x8bit.bitwarden.data.vault.repository.model.VaultUnlockData +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test + +class VaultUnlockDataExtensionsTest { + @Test + fun `statusFor returns the correct status for a userId in the list`() { + val list = listOf( + VaultUnlockData( + userId = USER_ID_1, + status = VaultUnlockData.Status.UNLOCKING, + ), + VaultUnlockData( + userId = USER_ID_2, + status = VaultUnlockData.Status.UNLOCKED, + ), + ) + + assertEquals( + VaultUnlockData.Status.UNLOCKING, + list.statusFor(USER_ID_1), + ) + assertEquals( + VaultUnlockData.Status.UNLOCKED, + list.statusFor(USER_ID_2), + ) + } + + @Test + fun `update updates the status for a user id in the list`() { + val list = listOf( + VaultUnlockData( + userId = USER_ID_1, + status = VaultUnlockData.Status.UNLOCKING, + ), + VaultUnlockData( + userId = USER_ID_2, + status = VaultUnlockData.Status.UNLOCKED, + ), + ) + + val updatedList = list.update( + userId = USER_ID_1, + status = VaultUnlockData.Status.UNLOCKED, + ) + assertEquals( + VaultUnlockData.Status.UNLOCKED, + updatedList.statusFor(USER_ID_1), + ) + } +} + +private const val USER_ID_1 = "userId_1" +private const val USER_ID_2 = "userId_2"