diff --git a/app/src/main/java/com/x8bit/bitwarden/data/platform/repository/AuthenticatorBridgeRepositoryImpl.kt b/app/src/main/java/com/x8bit/bitwarden/data/platform/repository/AuthenticatorBridgeRepositoryImpl.kt index 6bffc320ec..5b3582d820 100644 --- a/app/src/main/java/com/x8bit/bitwarden/data/platform/repository/AuthenticatorBridgeRepositoryImpl.kt +++ b/app/src/main/java/com/x8bit/bitwarden/data/platform/repository/AuthenticatorBridgeRepositoryImpl.kt @@ -116,7 +116,10 @@ class AuthenticatorBridgeRepositoryImpl( // Lock the user's vault if we unlocked it for this operation: if (!isVaultAlreadyUnlocked) { - vaultRepository.lockVault(userId) + vaultRepository.lockVault( + userId = userId, + isUserInitiated = false, + ) } SharedAccountData.Account( 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 e74bab2de0..c07bd64264 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 @@ -22,6 +22,11 @@ interface VaultLockManager { */ val vaultStateEventFlow: Flow + /** + * Whether the user is coming from the lock flow or not. + */ + var isFromLockFlow: Boolean + /** * Whether or not the vault is currently locked for the given [userId]. */ @@ -35,12 +40,12 @@ interface VaultLockManager { /** * Locks the vault for the user with the given [userId]. */ - fun lockVault(userId: String) + fun lockVault(userId: String, isUserInitiated: Boolean) /** * Locks the vault for the current user if currently unlocked. */ - fun lockVaultForCurrentUser() + fun lockVaultForCurrentUser(isUserInitiated: Boolean) /** * Attempt to unlock the vault with the specified user information. 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 985b6eca2d..4a42dd6ba1 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 @@ -101,6 +101,8 @@ class VaultLockManagerImpl( override val vaultStateEventFlow: Flow get() = mutableVaultStateEventSharedFlow.asSharedFlow() + override var isFromLockFlow: Boolean = false + init { observeAppCreationChanges() observeAppForegroundChanges() @@ -119,13 +121,17 @@ class VaultLockManagerImpl( override fun isVaultUnlocking(userId: String): Boolean = mutableVaultUnlockDataStateFlow.value.statusFor(userId) == VaultUnlockData.Status.UNLOCKING - override fun lockVault(userId: String) { + override fun lockVault(userId: String, isUserInitiated: Boolean) { + isFromLockFlow = isUserInitiated setVaultToLocked(userId = userId) } - override fun lockVaultForCurrentUser() { + override fun lockVaultForCurrentUser(isUserInitiated: Boolean) { activeUserId?.let { - lockVault(it) + lockVault( + userId = it, + isUserInitiated = isUserInitiated, + ) } } diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/auth/feature/landing/LandingViewModel.kt b/app/src/main/java/com/x8bit/bitwarden/ui/auth/feature/landing/LandingViewModel.kt index 99f758b65c..5c6847c909 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/auth/feature/landing/LandingViewModel.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/auth/feature/landing/LandingViewModel.kt @@ -127,7 +127,7 @@ class LandingViewModel @Inject constructor( } private fun handleLockAccountClicked(action: LandingAction.LockAccountClick) { - vaultRepository.lockVault(userId = action.accountSummary.userId) + vaultRepository.lockVault(userId = action.accountSummary.userId, isUserInitiated = true) } private fun handleLogoutAccountClicked(action: LandingAction.LogoutAccountClick) { diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/auth/feature/login/LoginViewModel.kt b/app/src/main/java/com/x8bit/bitwarden/ui/auth/feature/login/LoginViewModel.kt index e97ffcdeab..43775c1438 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/auth/feature/login/LoginViewModel.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/auth/feature/login/LoginViewModel.kt @@ -108,7 +108,7 @@ class LoginViewModel @Inject constructor( } private fun handleLockAccountClicked(action: LoginAction.LockAccountClick) { - vaultRepository.lockVault(userId = action.accountSummary.userId) + vaultRepository.lockVault(userId = action.accountSummary.userId, isUserInitiated = true) } private fun handleLogoutAccountClicked(action: LoginAction.LogoutAccountClick) { diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/auth/feature/vaultunlock/VaultUnlockViewModel.kt b/app/src/main/java/com/x8bit/bitwarden/ui/auth/feature/vaultunlock/VaultUnlockViewModel.kt index 85d507c347..ace0bf9980 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/auth/feature/vaultunlock/VaultUnlockViewModel.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/auth/feature/vaultunlock/VaultUnlockViewModel.kt @@ -16,6 +16,7 @@ import com.x8bit.bitwarden.data.platform.manager.BiometricsEncryptionManager import com.x8bit.bitwarden.data.platform.manager.SpecialCircumstanceManager import com.x8bit.bitwarden.data.platform.manager.model.SpecialCircumstance import com.x8bit.bitwarden.data.platform.repository.EnvironmentRepository +import com.x8bit.bitwarden.data.vault.manager.VaultLockManager import com.x8bit.bitwarden.data.vault.repository.VaultRepository import com.x8bit.bitwarden.data.vault.repository.model.VaultUnlockResult import com.x8bit.bitwarden.ui.auth.feature.vaultunlock.model.UnlockType @@ -54,6 +55,7 @@ class VaultUnlockViewModel @Inject constructor( private val specialCircumstanceManager: SpecialCircumstanceManager, private val fido2CredentialManager: Fido2CredentialManager, private val appResumeManager: AppResumeManager, + private val vaultLockManager: VaultLockManager, environmentRepo: EnvironmentRepository, savedStateHandle: SavedStateHandle, ) : BaseViewModel( @@ -100,6 +102,7 @@ class VaultUnlockViewModel @Inject constructor( // TODO: [PM-13076] Handle Fido2CredentialAssertionRequest special circumstance fido2CredentialAssertionRequest = null, hasMasterPassword = activeAccount.hasMasterPassword, + isFromLockFlow = vaultLockManager.isFromLockFlow, ) }, ) { @@ -120,6 +123,11 @@ class VaultUnlockViewModel @Inject constructor( .launchIn(viewModelScope) promptForBiometricsIfAvailable() + + // only when navigating from vault to lock we should not display biometrics + // subsequent views of the lock screen should display biometrics if available + vaultLockManager.isFromLockFlow = false + mutableStateFlow.update { it.copy(isFromLockFlow = false) } } override fun onCleared() { @@ -209,7 +217,7 @@ class VaultUnlockViewModel @Inject constructor( } private fun handleLockAccountClick(action: VaultUnlockAction.LockAccountClick) { - vaultRepo.lockVault(userId = action.accountSummary.userId) + vaultRepo.lockVault(userId = action.accountSummary.userId, isUserInitiated = true) } private fun handleLogoutAccountClick(action: VaultUnlockAction.LogoutAccountClick) { @@ -425,7 +433,7 @@ class VaultUnlockViewModel @Inject constructor( private fun promptForBiometricsIfAvailable() { val cipher = biometricsEncryptionManager.getOrCreateCipher(state.userId) - if (state.showBiometricLogin && cipher != null) { + if (state.showBiometricLogin && cipher != null && !state.isFromLockFlow) { sendEvent( VaultUnlockEvent.PromptForBiometrics( cipher = cipher, @@ -458,6 +466,7 @@ data class VaultUnlockState( val fido2GetCredentialsRequest: Fido2GetCredentialsRequest? = null, val fido2CredentialAssertionRequest: Fido2CredentialAssertionRequest? = null, private val hasMasterPassword: Boolean, + val isFromLockFlow: Boolean, ) : Parcelable { /** diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/platform/feature/settings/accountsecurity/AccountSecurityViewModel.kt b/app/src/main/java/com/x8bit/bitwarden/ui/platform/feature/settings/accountsecurity/AccountSecurityViewModel.kt index 4059e24efa..72a79e4362 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/platform/feature/settings/accountsecurity/AccountSecurityViewModel.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/platform/feature/settings/accountsecurity/AccountSecurityViewModel.kt @@ -265,7 +265,7 @@ class AccountSecurityViewModel @Inject constructor( } private fun handleLockNowClick() { - vaultRepository.lockVaultForCurrentUser() + vaultRepository.lockVaultForCurrentUser(isUserInitiated = true) } private fun handlePushNotificationConfirm() { diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/tools/feature/send/SendViewModel.kt b/app/src/main/java/com/x8bit/bitwarden/ui/tools/feature/send/SendViewModel.kt index 037a0ae827..741bfe2987 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/tools/feature/send/SendViewModel.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/tools/feature/send/SendViewModel.kt @@ -258,7 +258,7 @@ class SendViewModel @Inject constructor( } private fun handleLockClick() { - vaultRepo.lockVaultForCurrentUser() + vaultRepo.lockVaultForCurrentUser(isUserInitiated = true) } private fun handleRefreshClick() { diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/itemlisting/VaultItemListingViewModel.kt b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/itemlisting/VaultItemListingViewModel.kt index 7a325ea688..36669464c4 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/itemlisting/VaultItemListingViewModel.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/itemlisting/VaultItemListingViewModel.kt @@ -286,7 +286,7 @@ class VaultItemListingViewModel @Inject constructor( //region VaultItemListing Handlers private fun handleLockAccountClick(action: VaultItemListingsAction.LockAccountClick) { - vaultRepository.lockVault(userId = action.accountSummary.userId) + vaultRepository.lockVault(userId = action.accountSummary.userId, isUserInitiated = true) } private fun handleLogoutAccountClick(action: VaultItemListingsAction.LogoutAccountClick) { @@ -1023,7 +1023,7 @@ class VaultItemListingViewModel @Inject constructor( } private fun handleLockClick() { - vaultRepository.lockVaultForCurrentUser() + vaultRepository.lockVaultForCurrentUser(isUserInitiated = true) } private fun handleSyncClick() { diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/vault/VaultViewModel.kt b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/vault/VaultViewModel.kt index 4f2213d81b..12eb8cc093 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/vault/VaultViewModel.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/vault/VaultViewModel.kt @@ -327,7 +327,7 @@ class VaultViewModel @Inject constructor( } private fun handleLockAccountClick(action: VaultAction.LockAccountClick) { - vaultRepository.lockVault(userId = action.accountSummary.userId) + vaultRepository.lockVault(userId = action.accountSummary.userId, isUserInitiated = true) } private fun handleLogoutAccountClick(action: VaultAction.LogoutAccountClick) { @@ -374,7 +374,7 @@ class VaultViewModel @Inject constructor( } private fun handleLockClick() { - vaultRepository.lockVaultForCurrentUser() + vaultRepository.lockVaultForCurrentUser(isUserInitiated = true) } private fun handleExitConfirmationClick() { diff --git a/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/verificationcode/VerificationCodeViewModel.kt b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/verificationcode/VerificationCodeViewModel.kt index 0f308b6f41..d85ab4422c 100644 --- a/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/verificationcode/VerificationCodeViewModel.kt +++ b/app/src/main/java/com/x8bit/bitwarden/ui/vault/feature/verificationcode/VerificationCodeViewModel.kt @@ -119,7 +119,7 @@ class VerificationCodeViewModel @Inject constructor( } private fun handleLockClick() { - vaultRepository.lockVaultForCurrentUser() + vaultRepository.lockVaultForCurrentUser(isUserInitiated = true) } private fun handleRefreshClick() { diff --git a/app/src/test/java/com/x8bit/bitwarden/data/platform/repository/AuthenticatorBridgeRepositoryTest.kt b/app/src/test/java/com/x8bit/bitwarden/data/platform/repository/AuthenticatorBridgeRepositoryTest.kt index 5b4681570e..3c094318a7 100644 --- a/app/src/test/java/com/x8bit/bitwarden/data/platform/repository/AuthenticatorBridgeRepositoryTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/data/platform/repository/AuthenticatorBridgeRepositoryTest.kt @@ -88,7 +88,7 @@ class AuthenticatorBridgeRepositoryTest { every { vaultRepository.isVaultUnlocked(USER_1_ID) } returns true // But locked for user 2: every { vaultRepository.isVaultUnlocked(USER_2_ID) } returns false - every { vaultRepository.lockVault(USER_2_ID) } returns Unit + every { vaultRepository.lockVault(USER_2_ID, isUserInitiated = false) } returns Unit coEvery { vaultRepository.unlockVaultWithDecryptedUserKey( userId = USER_2_ID, @@ -147,7 +147,7 @@ class AuthenticatorBridgeRepositoryTest { decryptedUserKey = USER_2_UNLOCK_KEY, ) } - verify { vaultRepository.lockVault(USER_2_ID) } + verify { vaultRepository.lockVault(USER_2_ID, isUserInitiated = false) } coVerify { vaultSdkSource.decryptCipher(USER_1_ID, USER_1_ENCRYPTED_SDK_TOTP_CIPHER) } coVerify { vaultSdkSource.decryptCipher(USER_2_ID, USER_2_ENCRYPTED_SDK_TOTP_CIPHER) } } @@ -185,7 +185,7 @@ class AuthenticatorBridgeRepositoryTest { ) } verify { vaultRepository.vaultUnlockDataStateFlow } - verify { vaultRepository.lockVault(USER_2_ID) } + verify { vaultRepository.lockVault(USER_2_ID, isUserInitiated = false) } verify { vaultDiskSource.getCiphers(USER_2_ID) } coVerify { vaultSdkSource.decryptCipher(USER_2_ID, USER_2_ENCRYPTED_SDK_TOTP_CIPHER) } } @@ -198,7 +198,7 @@ class AuthenticatorBridgeRepositoryTest { coEvery { vaultRepository.unlockVaultWithDecryptedUserKey(USER_1_ID, USER_1_UNLOCK_KEY) } returns VaultUnlockResult.Success - every { vaultRepository.lockVault(USER_1_ID) } returns Unit + every { vaultRepository.lockVault(USER_1_ID, isUserInitiated = false) } returns Unit val sharedAccounts = authenticatorBridgeRepository.getSharedAccounts() assertEquals( @@ -216,7 +216,7 @@ class AuthenticatorBridgeRepositoryTest { decryptedUserKey = USER_1_UNLOCK_KEY, ) } - verify { vaultRepository.lockVault(USER_1_ID) } + verify { vaultRepository.lockVault(USER_1_ID, isUserInitiated = false) } verify { vaultRepository.isVaultUnlocked(USER_2_ID) } coVerify { vaultRepository.unlockVaultWithDecryptedUserKey( @@ -225,7 +225,7 @@ class AuthenticatorBridgeRepositoryTest { ) } verify { vaultRepository.vaultUnlockDataStateFlow } - verify { vaultRepository.lockVault(USER_2_ID) } + verify { vaultRepository.lockVault(USER_2_ID, isUserInitiated = false) } verify { vaultDiskSource.getCiphers(USER_2_ID) } coVerify { vaultSdkSource.decryptCipher(USER_2_ID, USER_2_ENCRYPTED_SDK_TOTP_CIPHER) } } @@ -260,7 +260,7 @@ class AuthenticatorBridgeRepositoryTest { ) } verify { vaultRepository.vaultUnlockDataStateFlow } - verify { vaultRepository.lockVault(USER_2_ID) } + verify { vaultRepository.lockVault(USER_2_ID, isUserInitiated = false) } verify { vaultDiskSource.getCiphers(USER_2_ID) } coVerify { vaultSdkSource.decryptCipher(USER_2_ID, USER_2_ENCRYPTED_SDK_TOTP_CIPHER) } } @@ -307,7 +307,7 @@ class AuthenticatorBridgeRepositoryTest { decryptedUserKey = USER_2_UNLOCK_KEY, ) } - verify { vaultRepository.lockVault(USER_2_ID) } + verify { vaultRepository.lockVault(USER_2_ID, isUserInitiated = false) } coVerify { vaultSdkSource.decryptCipher(USER_1_ID, USER_1_ENCRYPTED_SDK_TOTP_CIPHER) } coVerify { vaultSdkSource.decryptCipher(USER_2_ID, USER_2_ENCRYPTED_SDK_TOTP_CIPHER) } } 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 56dd3f5c75..a8b31f1011 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 @@ -165,7 +165,7 @@ class VaultLockManagerTest { verifyUnlockedVault(userId = USER_ID) vaultLockManager.vaultStateEventFlow.test { - vaultLockManager.lockVault(userId = USER_ID) + vaultLockManager.lockVault(userId = USER_ID, isUserInitiated = false) assertEquals(VaultStateEvent.Locked(userId = USER_ID), awaitItem()) fakeAuthDiskSource.assertLastLockTimestamp( userId = USER_ID, @@ -178,10 +178,10 @@ class VaultLockManagerTest { fun `vaultStateEventFlow should not emit Locked event when vault state remains locked`() = runTest { // Ensure the vault is locked - vaultLockManager.lockVault(userId = USER_ID) + vaultLockManager.lockVault(userId = USER_ID, isUserInitiated = false) vaultLockManager.vaultStateEventFlow.test { - vaultLockManager.lockVault(userId = USER_ID) + vaultLockManager.lockVault(userId = USER_ID, isUserInitiated = false) expectNoEvents() } } @@ -190,7 +190,7 @@ class VaultLockManagerTest { fun `vaultStateEventFlow should emit Unlocked event when vault state changes to unlocked`() = runTest { // Ensure the vault is locked - vaultLockManager.lockVault(userId = USER_ID) + vaultLockManager.lockVault(userId = USER_ID, isUserInitiated = false) vaultLockManager.vaultStateEventFlow.test { verifyUnlockedVault(userId = USER_ID) @@ -786,7 +786,7 @@ class VaultLockManagerTest { vaultLockManager.vaultUnlockDataStateFlow.value, ) - vaultLockManager.lockVault(userId = USER_ID) + vaultLockManager.lockVault(userId = USER_ID, isUserInitiated = false) assertEquals( emptyList(), @@ -811,7 +811,7 @@ class VaultLockManagerTest { vaultLockManager.vaultUnlockDataStateFlow.value, ) - vaultLockManager.lockVault(userId = USER_ID) + vaultLockManager.lockVault(userId = USER_ID, isUserInitiated = false) assertEquals( emptyList(), @@ -837,7 +837,7 @@ class VaultLockManagerTest { vaultLockManager.vaultUnlockDataStateFlow.value, ) - vaultLockManager.lockVaultForCurrentUser() + vaultLockManager.lockVaultForCurrentUser(isUserInitiated = true) assertEquals( emptyList(), 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 dc79047637..add5d07eed 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 @@ -191,8 +191,8 @@ class VaultRepositoryTest { userId in mutableUnlockedUserIdsStateFlow.value } every { isVaultUnlocking(any()) } returns false - every { lockVault(any()) } just runs - every { lockVaultForCurrentUser() } just runs + every { lockVault(any(), any()) } just runs + every { lockVaultForCurrentUser(any()) } just runs coEvery { waitUntilUnlocked(any()) } coAnswers { call -> @@ -1156,8 +1156,8 @@ class VaultRepositoryTest { @Test fun `lockVaultForCurrentUser should delegate to the VaultLockManager`() { - vaultRepository.lockVaultForCurrentUser() - verify { vaultLockManager.lockVaultForCurrentUser() } + vaultRepository.lockVaultForCurrentUser(isUserInitiated = true) + verify { vaultLockManager.lockVaultForCurrentUser(isUserInitiated = true) } } @Test diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/landing/LandingViewModelTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/landing/LandingViewModelTest.kt index 6f2272dcfe..09478df95b 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/landing/LandingViewModelTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/landing/LandingViewModelTest.kt @@ -35,7 +35,7 @@ class LandingViewModelTest : BaseViewModelTest() { every { logout(any()) } just runs } private val vaultRepository: VaultRepository = mockk(relaxed = true) { - every { lockVault(any()) } just runs + every { lockVault(any(), any()) } just runs } private val fakeEnvironmentRepository = FakeEnvironmentRepository() @@ -126,7 +126,7 @@ class LandingViewModelTest : BaseViewModelTest() { viewModel.trySendAction(LandingAction.LockAccountClick(accountSummary)) - verify { vaultRepository.lockVault(userId = accountUserId) } + verify { vaultRepository.lockVault(userId = accountUserId, isUserInitiated = true) } } @Test diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/login/LoginViewModelTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/login/LoginViewModelTest.kt index ebef998261..1b6aab8e1d 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/login/LoginViewModelTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/login/LoginViewModelTest.kt @@ -51,7 +51,7 @@ class LoginViewModelTest : BaseViewModelTest() { every { logout(any()) } just runs } private val vaultRepository: VaultRepository = mockk(relaxed = true) { - every { lockVault(any()) } just runs + every { lockVault(any(), any()) } just runs } private val fakeEnvironmentRepository = FakeEnvironmentRepository() @@ -204,7 +204,7 @@ class LoginViewModelTest : BaseViewModelTest() { viewModel.trySendAction(LoginAction.LockAccountClick(accountSummary)) - verify { vaultRepository.lockVault(userId = accountUserId) } + verify { vaultRepository.lockVault(userId = accountUserId, isUserInitiated = true) } } @Test diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/vaultunlock/VaultUnlockScreenTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/vaultunlock/VaultUnlockScreenTest.kt index 212588a7c5..57baf1a755 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/vaultunlock/VaultUnlockScreenTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/vaultunlock/VaultUnlockScreenTest.kt @@ -646,4 +646,5 @@ private val DEFAULT_STATE: VaultUnlockState = VaultUnlockState( userId = ACTIVE_ACCOUNT_SUMMARY.userId, vaultUnlockType = VaultUnlockType.MASTER_PASSWORD, hasMasterPassword = true, + isFromLockFlow = false, ) diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/vaultunlock/VaultUnlockViewModelTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/vaultunlock/VaultUnlockViewModelTest.kt index 20afa39564..2fb776974e 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/vaultunlock/VaultUnlockViewModelTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/auth/feature/vaultunlock/VaultUnlockViewModelTest.kt @@ -21,6 +21,7 @@ import com.x8bit.bitwarden.data.platform.repository.EnvironmentRepository import com.x8bit.bitwarden.data.platform.repository.model.Environment import com.x8bit.bitwarden.data.platform.repository.util.FakeEnvironmentRepository import com.x8bit.bitwarden.data.platform.repository.util.bufferedMutableSharedFlow +import com.x8bit.bitwarden.data.vault.manager.VaultLockManager import com.x8bit.bitwarden.data.vault.repository.VaultRepository import com.x8bit.bitwarden.data.vault.repository.model.VaultUnlockResult import com.x8bit.bitwarden.ui.auth.feature.vaultunlock.model.UnlockType @@ -60,7 +61,7 @@ class VaultUnlockViewModelTest : BaseViewModelTest() { every { switchAccount(any()) } returns SwitchAccountResult.AccountSwitched } private val vaultRepository: VaultRepository = mockk(relaxed = true) { - every { lockVault(any()) } just runs + every { lockVault(any(), any()) } just runs } private val encryptionManager: BiometricsEncryptionManager = mockk { every { getOrCreateCipher(USER_ID) } returns CIPHER @@ -91,6 +92,10 @@ class VaultUnlockViewModelTest : BaseViewModelTest() { every { getResumeSpecialCircumstance() } returns null } + private val vaultLockManager: VaultLockManager = mockk(relaxed = true) { + every { isFromLockFlow } returns false + } + @Test fun `on init with biometrics enabled and valid should emit PromptForBiometrics`() = runTest { val initialState = DEFAULT_STATE.copy( @@ -502,6 +507,25 @@ class VaultUnlockViewModelTest : BaseViewModelTest() { verify { encryptionManager.getOrCreateCipher(USER_ID) } } + @Test + @Suppress("MaxLineLength") + fun `on BiometricsUnlockClick should not emit PromptForBiometrics when isFromLockFlow is true`() = + runTest { + val initialState = + DEFAULT_STATE.copy( + isBiometricsValid = true, + isBiometricEnabled = true, + isFromLockFlow = true, + ) + val viewModel = createViewModel( + state = initialState, + ) + + viewModel.eventFlow.test { + expectNoEvents() + } + } + @Suppress("MaxLineLength") @Test fun `on BiometricsUnlockClick should disable isBiometricsValid and show message when cipher is null and integrity check returns false`() { @@ -629,7 +653,7 @@ class VaultUnlockViewModelTest : BaseViewModelTest() { viewModel.trySendAction(VaultUnlockAction.LockAccountClick(accountSummary)) - verify { vaultRepository.lockVault(userId = accountUserId) } + verify { vaultRepository.lockVault(userId = accountUserId, isUserInitiated = true) } } @Test @@ -1301,12 +1325,14 @@ class VaultUnlockViewModelTest : BaseViewModelTest() { } } + @Suppress("LongParameterList") private fun createViewModel( state: VaultUnlockState? = null, unlockType: UnlockType = UnlockType.STANDARD, environmentRepo: EnvironmentRepository = environmentRepository, vaultRepo: VaultRepository = vaultRepository, biometricsEncryptionManager: BiometricsEncryptionManager = encryptionManager, + lockManager: VaultLockManager = vaultLockManager, ): VaultUnlockViewModel = VaultUnlockViewModel( savedStateHandle = SavedStateHandle().apply { set("state", state) @@ -1319,6 +1345,7 @@ class VaultUnlockViewModelTest : BaseViewModelTest() { fido2CredentialManager = fido2CredentialManager, specialCircumstanceManager = specialCircumstanceManager, appResumeManager = appResumeManager, + vaultLockManager = lockManager, ) } @@ -1351,6 +1378,7 @@ private val DEFAULT_STATE: VaultUnlockState = VaultUnlockState( userId = USER_ID, vaultUnlockType = VaultUnlockType.MASTER_PASSWORD, hasMasterPassword = true, + isFromLockFlow = false, ) private val TRUSTED_DEVICE: UserState.TrustedDevice = UserState.TrustedDevice( diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/accountsecurity/AccountSecurityViewModelTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/accountsecurity/AccountSecurityViewModelTest.kt index 9b467010bb..87145ac73b 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/accountsecurity/AccountSecurityViewModelTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/platform/feature/settings/accountsecurity/AccountSecurityViewModelTest.kt @@ -383,10 +383,10 @@ class AccountSecurityViewModelTest : BaseViewModelTest() { @Test fun `on LockNowClick should call lockVaultForCurrentUser`() { - every { vaultRepository.lockVaultForCurrentUser() } just runs + every { vaultRepository.lockVaultForCurrentUser(any()) } just runs val viewModel = createViewModel() viewModel.trySendAction(AccountSecurityAction.LockNowClick) - verify { vaultRepository.lockVaultForCurrentUser() } + verify { vaultRepository.lockVaultForCurrentUser(any()) } } @Test diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/tools/feature/send/SendViewModelTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/tools/feature/send/SendViewModelTest.kt index 292867e6a9..52de57e649 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/tools/feature/send/SendViewModelTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/tools/feature/send/SendViewModelTest.kt @@ -102,12 +102,12 @@ class SendViewModelTest : BaseViewModelTest() { @Test fun `LockClick should lock the vault`() { val viewModel = createViewModel() - every { vaultRepo.lockVaultForCurrentUser() } just runs + every { vaultRepo.lockVaultForCurrentUser(any()) } just runs viewModel.trySendAction(SendAction.LockClick) verify { - vaultRepo.lockVaultForCurrentUser() + vaultRepo.lockVaultForCurrentUser(isUserInitiated = true) } } diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/itemlisting/VaultItemListingViewModelTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/itemlisting/VaultItemListingViewModelTest.kt index c83c6640f5..75086154fc 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/itemlisting/VaultItemListingViewModelTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/itemlisting/VaultItemListingViewModelTest.kt @@ -143,7 +143,7 @@ class VaultItemListingViewModelTest : BaseViewModelTest() { private val vaultRepository: VaultRepository = mockk { every { vaultFilterType } returns VaultFilterType.AllVaults every { vaultDataStateFlow } returns mutableVaultDataStateFlow - every { lockVault(any()) } just runs + every { lockVault(any(), any()) } just runs every { sync(forced = any()) } just runs coEvery { getDecryptedFido2CredentialAutofillViews(any()) @@ -247,7 +247,7 @@ class VaultItemListingViewModelTest : BaseViewModelTest() { viewModel.trySendAction(VaultItemListingsAction.LockAccountClick(accountSummary)) - verify { vaultRepository.lockVault(userId = accountUserId) } + verify { vaultRepository.lockVault(userId = accountUserId, isUserInitiated = true) } } @Test @@ -360,13 +360,13 @@ class VaultItemListingViewModelTest : BaseViewModelTest() { @Test fun `LockClick should call lockVaultForCurrentUser`() { - every { vaultRepository.lockVaultForCurrentUser() } just runs + every { vaultRepository.lockVaultForCurrentUser(any()) } just runs val viewModel = createVaultItemListingViewModel() viewModel.trySendAction(VaultItemListingsAction.LockClick) verify(exactly = 1) { - vaultRepository.lockVaultForCurrentUser() + vaultRepository.lockVaultForCurrentUser(isUserInitiated = true) } } diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/vault/VaultViewModelTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/vault/VaultViewModelTest.kt index 9b6a08ae45..4849519829 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/vault/VaultViewModelTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/vault/VaultViewModelTest.kt @@ -131,8 +131,8 @@ class VaultViewModelTest : BaseViewModelTest() { every { vaultDataStateFlow } returns mutableVaultDataStateFlow every { sync(forced = any()) } just runs every { syncIfNecessary() } just runs - every { lockVaultForCurrentUser() } just runs - every { lockVault(any()) } just runs + every { lockVaultForCurrentUser(any()) } just runs + every { lockVault(any(), any()) } just runs } private val organizationEventManager = mockk { @@ -382,7 +382,7 @@ class VaultViewModelTest : BaseViewModelTest() { viewModel.trySendAction(VaultAction.LockAccountClick(accountSummary)) - verify { vaultRepository.lockVault(userId = accountUserId) } + verify { vaultRepository.lockVault(userId = accountUserId, isUserInitiated = true) } } @Suppress("MaxLineLength") @@ -520,7 +520,7 @@ class VaultViewModelTest : BaseViewModelTest() { val viewModel = createViewModel() viewModel.trySendAction(VaultAction.LockClick) verify { - vaultRepository.lockVaultForCurrentUser() + vaultRepository.lockVaultForCurrentUser(isUserInitiated = true) } } diff --git a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/verificationcode/VerificationCodeViewModelTest.kt b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/verificationcode/VerificationCodeViewModelTest.kt index 5ff2f82e2f..df569826a3 100644 --- a/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/verificationcode/VerificationCodeViewModelTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/ui/vault/feature/verificationcode/VerificationCodeViewModelTest.kt @@ -131,13 +131,13 @@ class VerificationCodeViewModelTest : BaseViewModelTest() { @Test fun `LockClick should call lockVaultForCurrentUser`() { - every { vaultRepository.lockVaultForCurrentUser() } just runs + every { vaultRepository.lockVaultForCurrentUser(any()) } just runs val viewModel = createViewModel() viewModel.trySendAction(VerificationCodeAction.LockClick) verify(exactly = 1) { - vaultRepository.lockVaultForCurrentUser() + vaultRepository.lockVaultForCurrentUser(isUserInitiated = true) } }