mirror of
https://github.com/bitwarden/android.git
synced 2025-12-11 13:57:03 -06:00
[PM-13257] Checking if user is navigating from vault before showing prompt for biometrics (#4846)
This commit is contained in:
parent
f4f669683e
commit
ad6bc883b8
@ -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(
|
||||
|
||||
@ -22,6 +22,11 @@ interface VaultLockManager {
|
||||
*/
|
||||
val vaultStateEventFlow: Flow<VaultStateEvent>
|
||||
|
||||
/**
|
||||
* 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.
|
||||
|
||||
@ -101,6 +101,8 @@ class VaultLockManagerImpl(
|
||||
override val vaultStateEventFlow: Flow<VaultStateEvent>
|
||||
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,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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<VaultUnlockState, VaultUnlockEvent, VaultUnlockAction>(
|
||||
@ -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 {
|
||||
|
||||
/**
|
||||
|
||||
@ -265,7 +265,7 @@ class AccountSecurityViewModel @Inject constructor(
|
||||
}
|
||||
|
||||
private fun handleLockNowClick() {
|
||||
vaultRepository.lockVaultForCurrentUser()
|
||||
vaultRepository.lockVaultForCurrentUser(isUserInitiated = true)
|
||||
}
|
||||
|
||||
private fun handlePushNotificationConfirm() {
|
||||
|
||||
@ -258,7 +258,7 @@ class SendViewModel @Inject constructor(
|
||||
}
|
||||
|
||||
private fun handleLockClick() {
|
||||
vaultRepo.lockVaultForCurrentUser()
|
||||
vaultRepo.lockVaultForCurrentUser(isUserInitiated = true)
|
||||
}
|
||||
|
||||
private fun handleRefreshClick() {
|
||||
|
||||
@ -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() {
|
||||
|
||||
@ -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() {
|
||||
|
||||
@ -119,7 +119,7 @@ class VerificationCodeViewModel @Inject constructor(
|
||||
}
|
||||
|
||||
private fun handleLockClick() {
|
||||
vaultRepository.lockVaultForCurrentUser()
|
||||
vaultRepository.lockVaultForCurrentUser(isUserInitiated = true)
|
||||
}
|
||||
|
||||
private fun handleRefreshClick() {
|
||||
|
||||
@ -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) }
|
||||
}
|
||||
|
||||
@ -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<VaultUnlockData>(),
|
||||
@ -811,7 +811,7 @@ class VaultLockManagerTest {
|
||||
vaultLockManager.vaultUnlockDataStateFlow.value,
|
||||
)
|
||||
|
||||
vaultLockManager.lockVault(userId = USER_ID)
|
||||
vaultLockManager.lockVault(userId = USER_ID, isUserInitiated = false)
|
||||
|
||||
assertEquals(
|
||||
emptyList<VaultUnlockData>(),
|
||||
@ -837,7 +837,7 @@ class VaultLockManagerTest {
|
||||
vaultLockManager.vaultUnlockDataStateFlow.value,
|
||||
)
|
||||
|
||||
vaultLockManager.lockVaultForCurrentUser()
|
||||
vaultLockManager.lockVaultForCurrentUser(isUserInitiated = true)
|
||||
|
||||
assertEquals(
|
||||
emptyList<VaultUnlockData>(),
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -646,4 +646,5 @@ private val DEFAULT_STATE: VaultUnlockState = VaultUnlockState(
|
||||
userId = ACTIVE_ACCOUNT_SUMMARY.userId,
|
||||
vaultUnlockType = VaultUnlockType.MASTER_PASSWORD,
|
||||
hasMasterPassword = true,
|
||||
isFromLockFlow = false,
|
||||
)
|
||||
|
||||
@ -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(
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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<OrganizationEventManager> {
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user