diff --git a/app/src/main/kotlin/com/x8bit/bitwarden/data/auth/repository/AuthRepository.kt b/app/src/main/kotlin/com/x8bit/bitwarden/data/auth/repository/AuthRepository.kt index 7718077231..c9f8295664 100644 --- a/app/src/main/kotlin/com/x8bit/bitwarden/data/auth/repository/AuthRepository.kt +++ b/app/src/main/kotlin/com/x8bit/bitwarden/data/auth/repository/AuthRepository.kt @@ -38,6 +38,7 @@ import com.x8bit.bitwarden.data.auth.repository.util.SsoCallbackResult import com.x8bit.bitwarden.data.auth.repository.util.WebAuthResult import com.x8bit.bitwarden.data.auth.util.YubiKeyResult import com.x8bit.bitwarden.data.platform.datasource.network.authenticator.AuthenticatorProvider +import com.x8bit.bitwarden.data.platform.manager.BiometricsEncryptionManager import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.StateFlow @@ -48,6 +49,7 @@ import kotlinx.coroutines.flow.StateFlow interface AuthRepository : AuthenticatorProvider, AuthRequestManager, + BiometricsEncryptionManager, KdfManager, UserStateManager { /** diff --git a/app/src/main/kotlin/com/x8bit/bitwarden/data/auth/repository/AuthRepositoryImpl.kt b/app/src/main/kotlin/com/x8bit/bitwarden/data/auth/repository/AuthRepositoryImpl.kt index aa61cbc853..e42ea194f6 100644 --- a/app/src/main/kotlin/com/x8bit/bitwarden/data/auth/repository/AuthRepositoryImpl.kt +++ b/app/src/main/kotlin/com/x8bit/bitwarden/data/auth/repository/AuthRepositoryImpl.kt @@ -102,6 +102,7 @@ import com.x8bit.bitwarden.data.auth.util.YubiKeyResult import com.x8bit.bitwarden.data.auth.util.toSdkParams import com.x8bit.bitwarden.data.platform.datasource.disk.SettingsDiskSource import com.x8bit.bitwarden.data.platform.error.NoActiveUserException +import com.x8bit.bitwarden.data.platform.manager.BiometricsEncryptionManager import com.x8bit.bitwarden.data.platform.manager.LogsManager import com.x8bit.bitwarden.data.platform.manager.PolicyManager import com.x8bit.bitwarden.data.platform.manager.PushManager @@ -157,6 +158,7 @@ class AuthRepositoryImpl( private val settingsRepository: SettingsRepository, private val vaultRepository: VaultRepository, private val authRequestManager: AuthRequestManager, + private val biometricsEncryptionManager: BiometricsEncryptionManager, private val keyConnectorManager: KeyConnectorManager, private val trustedDeviceManager: TrustedDeviceManager, private val userLogoutManager: UserLogoutManager, @@ -168,6 +170,7 @@ class AuthRepositoryImpl( dispatcherManager: DispatcherManager, ) : AuthRepository, AuthRequestManager by authRequestManager, + BiometricsEncryptionManager by biometricsEncryptionManager, KdfManager by kdfManager, UserStateManager by userStateManager { /** diff --git a/app/src/main/kotlin/com/x8bit/bitwarden/data/auth/repository/di/AuthRepositoryModule.kt b/app/src/main/kotlin/com/x8bit/bitwarden/data/auth/repository/di/AuthRepositoryModule.kt index 46748dfc95..2e5f5320aa 100644 --- a/app/src/main/kotlin/com/x8bit/bitwarden/data/auth/repository/di/AuthRepositoryModule.kt +++ b/app/src/main/kotlin/com/x8bit/bitwarden/data/auth/repository/di/AuthRepositoryModule.kt @@ -19,6 +19,7 @@ import com.x8bit.bitwarden.data.auth.manager.UserStateManagerImpl import com.x8bit.bitwarden.data.auth.repository.AuthRepository import com.x8bit.bitwarden.data.auth.repository.AuthRepositoryImpl import com.x8bit.bitwarden.data.platform.datasource.disk.SettingsDiskSource +import com.x8bit.bitwarden.data.platform.manager.BiometricsEncryptionManager import com.x8bit.bitwarden.data.platform.manager.FirstTimeActionManager import com.x8bit.bitwarden.data.platform.manager.LogsManager import com.x8bit.bitwarden.data.platform.manager.PolicyManager @@ -60,6 +61,7 @@ object AuthRepositoryModule { environmentRepository: EnvironmentRepository, settingsRepository: SettingsRepository, vaultRepository: VaultRepository, + biometricsEncryptionManager: BiometricsEncryptionManager, keyConnectorManager: KeyConnectorManager, authRequestManager: AuthRequestManager, trustedDeviceManager: TrustedDeviceManager, @@ -85,6 +87,7 @@ object AuthRepositoryModule { environmentRepository = environmentRepository, settingsRepository = settingsRepository, vaultRepository = vaultRepository, + biometricsEncryptionManager = biometricsEncryptionManager, keyConnectorManager = keyConnectorManager, authRequestManager = authRequestManager, trustedDeviceManager = trustedDeviceManager, diff --git a/app/src/main/kotlin/com/x8bit/bitwarden/data/credentials/builder/CredentialEntryBuilderImpl.kt b/app/src/main/kotlin/com/x8bit/bitwarden/data/credentials/builder/CredentialEntryBuilderImpl.kt index 08259a1477..614fcc38fc 100644 --- a/app/src/main/kotlin/com/x8bit/bitwarden/data/credentials/builder/CredentialEntryBuilderImpl.kt +++ b/app/src/main/kotlin/com/x8bit/bitwarden/data/credentials/builder/CredentialEntryBuilderImpl.kt @@ -11,10 +11,10 @@ import com.bitwarden.fido.Fido2CredentialAutofillView import com.bitwarden.ui.platform.resource.BitwardenDrawable import com.bitwarden.ui.platform.resource.BitwardenString import com.bitwarden.vault.CipherListView +import com.x8bit.bitwarden.data.auth.repository.AuthRepository import com.x8bit.bitwarden.data.autofill.util.login import com.x8bit.bitwarden.data.credentials.manager.CredentialManagerPendingIntentManager import com.x8bit.bitwarden.data.credentials.util.setBiometricPromptDataIfSupported -import com.x8bit.bitwarden.data.platform.manager.BiometricsEncryptionManager /** * Primary implementation of [CredentialEntryBuilder]. @@ -22,7 +22,7 @@ import com.x8bit.bitwarden.data.platform.manager.BiometricsEncryptionManager class CredentialEntryBuilderImpl( private val context: Context, private val pendingIntentManager: CredentialManagerPendingIntentManager, - private val biometricsEncryptionManager: BiometricsEncryptionManager, + private val authRepository: AuthRepository, ) : CredentialEntryBuilder { override fun buildPublicKeyCredentialEntries( @@ -82,7 +82,7 @@ class CredentialEntryBuilderImpl( .also { builder -> if (!isUserVerified) { builder.setBiometricPromptDataIfSupported( - cipher = biometricsEncryptionManager.getOrCreateCipher(userId), + cipher = authRepository.getOrCreateCipher(userId), ) } } @@ -113,8 +113,7 @@ class CredentialEntryBuilderImpl( .apply { if (!isUserVerified) { setBiometricPromptDataIfSupported( - cipher = biometricsEncryptionManager - .getOrCreateCipher(userId), + cipher = authRepository.getOrCreateCipher(userId), ) } } diff --git a/app/src/main/kotlin/com/x8bit/bitwarden/data/credentials/di/CredentialProviderModule.kt b/app/src/main/kotlin/com/x8bit/bitwarden/data/credentials/di/CredentialProviderModule.kt index 876f66c182..f1247ca280 100644 --- a/app/src/main/kotlin/com/x8bit/bitwarden/data/credentials/di/CredentialProviderModule.kt +++ b/app/src/main/kotlin/com/x8bit/bitwarden/data/credentials/di/CredentialProviderModule.kt @@ -25,7 +25,6 @@ import com.x8bit.bitwarden.data.credentials.repository.PrivilegedAppRepositoryIm import com.x8bit.bitwarden.data.credentials.sanitizer.PasskeyAttestationOptionsSanitizer import com.x8bit.bitwarden.data.credentials.sanitizer.PasskeyAttestationOptionsSanitizerImpl import com.x8bit.bitwarden.data.platform.manager.AssetManager -import com.x8bit.bitwarden.data.platform.manager.BiometricsEncryptionManager import com.x8bit.bitwarden.data.platform.manager.ciphermatching.CipherMatchingManager import com.x8bit.bitwarden.data.vault.datasource.sdk.VaultSdkSource import com.x8bit.bitwarden.data.vault.repository.VaultRepository @@ -54,7 +53,6 @@ object CredentialProviderModule { bitwardenCredentialManager: BitwardenCredentialManager, dispatcherManager: DispatcherManager, pendingIntentManager: CredentialManagerPendingIntentManager, - biometricsEncryptionManager: BiometricsEncryptionManager, clock: Clock, ): CredentialProviderProcessor = CredentialProviderProcessorImpl( @@ -63,7 +61,6 @@ object CredentialProviderModule { bitwardenCredentialManager = bitwardenCredentialManager, pendingIntentManager = pendingIntentManager, clock = clock, - biometricsEncryptionManager = biometricsEncryptionManager, dispatcherManager = dispatcherManager, ) @@ -108,11 +105,11 @@ object CredentialProviderModule { fun provideCredentialEntryBuilder( @ApplicationContext context: Context, pendingIntentManager: CredentialManagerPendingIntentManager, - biometricsEncryptionManager: BiometricsEncryptionManager, + authRepository: AuthRepository, ): CredentialEntryBuilder = CredentialEntryBuilderImpl( context = context, pendingIntentManager = pendingIntentManager, - biometricsEncryptionManager = biometricsEncryptionManager, + authRepository = authRepository, ) @Provides diff --git a/app/src/main/kotlin/com/x8bit/bitwarden/data/credentials/processor/CredentialProviderProcessorImpl.kt b/app/src/main/kotlin/com/x8bit/bitwarden/data/credentials/processor/CredentialProviderProcessorImpl.kt index be43a19bd3..3effb1e18e 100644 --- a/app/src/main/kotlin/com/x8bit/bitwarden/data/credentials/processor/CredentialProviderProcessorImpl.kt +++ b/app/src/main/kotlin/com/x8bit/bitwarden/data/credentials/processor/CredentialProviderProcessorImpl.kt @@ -34,7 +34,6 @@ import com.x8bit.bitwarden.data.auth.repository.model.UserState import com.x8bit.bitwarden.data.credentials.manager.BitwardenCredentialManager import com.x8bit.bitwarden.data.credentials.manager.CredentialManagerPendingIntentManager import com.x8bit.bitwarden.data.credentials.model.GetCredentialsRequest -import com.x8bit.bitwarden.data.platform.manager.BiometricsEncryptionManager import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch import timber.log.Timber @@ -53,7 +52,6 @@ class CredentialProviderProcessorImpl( private val bitwardenCredentialManager: BitwardenCredentialManager, private val pendingIntentManager: CredentialManagerPendingIntentManager, private val clock: Clock, - private val biometricsEncryptionManager: BiometricsEncryptionManager, dispatcherManager: DispatcherManager, ) : CredentialProviderProcessor { @@ -203,7 +201,7 @@ class CredentialProviderProcessorImpl( .setAutoSelectAllowed(true) if (isVaultUnlocked) { - biometricsEncryptionManager + authRepository .getOrCreateCipher(userId) ?.let { entryBuilder.setBiometricPromptDataIfSupported(cipher = it) } } @@ -251,7 +249,7 @@ class CredentialProviderProcessorImpl( .setAutoSelectAllowed(true) if (isVaultUnlocked) { - biometricsEncryptionManager + authRepository .getOrCreateCipher(userId) ?.let { entryBuilder.setBiometricPromptDataIfSupported(cipher = it) } } diff --git a/app/src/main/kotlin/com/x8bit/bitwarden/data/platform/manager/BiometricsEncryptionManager.kt b/app/src/main/kotlin/com/x8bit/bitwarden/data/platform/manager/BiometricsEncryptionManager.kt index 31062ff24b..c0dda3df31 100644 --- a/app/src/main/kotlin/com/x8bit/bitwarden/data/platform/manager/BiometricsEncryptionManager.kt +++ b/app/src/main/kotlin/com/x8bit/bitwarden/data/platform/manager/BiometricsEncryptionManager.kt @@ -32,7 +32,6 @@ interface BiometricsEncryptionManager { */ fun isBiometricIntegrityValid( userId: String, - cipher: Cipher?, ): Boolean /** diff --git a/app/src/main/kotlin/com/x8bit/bitwarden/data/platform/manager/BiometricsEncryptionManagerImpl.kt b/app/src/main/kotlin/com/x8bit/bitwarden/data/platform/manager/BiometricsEncryptionManagerImpl.kt index 39e223051d..f9d2c8ec58 100644 --- a/app/src/main/kotlin/com/x8bit/bitwarden/data/platform/manager/BiometricsEncryptionManagerImpl.kt +++ b/app/src/main/kotlin/com/x8bit/bitwarden/data/platform/manager/BiometricsEncryptionManagerImpl.kt @@ -90,8 +90,8 @@ class BiometricsEncryptionManagerImpl( return cipher?.takeIf { isCipherInitialized } } - override fun isBiometricIntegrityValid(userId: String, cipher: Cipher?): Boolean = - isSystemBiometricIntegrityValid(userId, cipher) && isAccountBiometricIntegrityValid(userId) + override fun isBiometricIntegrityValid(userId: String): Boolean = + isSystemBiometricIntegrityValid(userId) && isAccountBiometricIntegrityValid(userId) override fun isAccountBiometricIntegrityValid(userId: String): Boolean { val systemBioIntegrityState = settingsDiskSource @@ -203,11 +203,13 @@ class BiometricsEncryptionManagerImpl( } /** - * Validates the keystore key and decrypts it using the user-provided [cipher]. + * Validates the keystore key and decrypts it, if decryption is successful `true` is returned, + * `false` otherwise. */ - private fun isSystemBiometricIntegrityValid(userId: String, cipher: Cipher?): Boolean { + private fun isSystemBiometricIntegrityValid(userId: String): Boolean { // Attempt to get the user scoped key. If that fails, we check to see if a legacy key // is available. + val cipher = getOrCreateCipher(userId = userId) val secretKey = getSecretKeyOrNull(userId = userId) ?: getSecretKeyOrNull(userId = null) return if (cipher != null && secretKey != null) { cipher.initializeCipher(userId = userId, secretKey = secretKey) diff --git a/app/src/main/kotlin/com/x8bit/bitwarden/ui/auth/feature/accountsetup/SetupUnlockViewModel.kt b/app/src/main/kotlin/com/x8bit/bitwarden/ui/auth/feature/accountsetup/SetupUnlockViewModel.kt index 7e39124488..c57e409c80 100644 --- a/app/src/main/kotlin/com/x8bit/bitwarden/ui/auth/feature/accountsetup/SetupUnlockViewModel.kt +++ b/app/src/main/kotlin/com/x8bit/bitwarden/ui/auth/feature/accountsetup/SetupUnlockViewModel.kt @@ -10,7 +10,6 @@ import com.bitwarden.ui.util.asText import com.x8bit.bitwarden.data.auth.datasource.disk.model.OnboardingStatus import com.x8bit.bitwarden.data.auth.repository.AuthRepository import com.x8bit.bitwarden.data.autofill.manager.browser.BrowserThirdPartyAutofillEnabledManager -import com.x8bit.bitwarden.data.platform.manager.BiometricsEncryptionManager import com.x8bit.bitwarden.data.platform.manager.FirstTimeActionManager import com.x8bit.bitwarden.data.platform.repository.SettingsRepository import com.x8bit.bitwarden.data.platform.repository.model.BiometricsKeyResult @@ -33,17 +32,12 @@ class SetupUnlockViewModel @Inject constructor( savedStateHandle: SavedStateHandle, private val authRepository: AuthRepository, private val settingsRepository: SettingsRepository, - private val biometricsEncryptionManager: BiometricsEncryptionManager, private val firstTimeActionManager: FirstTimeActionManager, private val browserThirdPartyAutofillEnabledManager: BrowserThirdPartyAutofillEnabledManager, ) : BaseViewModel( // We load the state from the savedStateHandle for testing purposes. initialState = savedStateHandle[KEY_STATE] ?: run { val userId = requireNotNull(authRepository.userStateFlow.value).activeUserId - val isBiometricsValid = biometricsEncryptionManager.isBiometricIntegrityValid( - userId = userId, - cipher = biometricsEncryptionManager.getOrCreateCipher(userId = userId), - ) // whether or not the user has completed the initial setup prior to this. val isInitialSetup = savedStateHandle.toSetupUnlockArgs().isInitialSetup SetupUnlockState( @@ -55,7 +49,7 @@ class SetupUnlockViewModel @Inject constructor( ?.hasMasterPassword != false, isUnlockWithPinEnabled = settingsRepository.isUnlockWithPinEnabled, isUnlockWithBiometricsEnabled = settingsRepository.isUnlockWithBiometricsEnabled && - isBiometricsValid, + authRepository.isBiometricIntegrityValid(userId = userId), dialogState = null, isInitialSetup = isInitialSetup, ) @@ -100,7 +94,7 @@ class SetupUnlockViewModel @Inject constructor( } private fun handleEnableBiometricsClick() { - biometricsEncryptionManager + authRepository .createCipherOrNull(userId = state.userId) ?.let { sendEvent( @@ -134,7 +128,7 @@ class SetupUnlockViewModel @Inject constructor( } private fun handleUnlockWithBiometricToggleDisabled() { - biometricsEncryptionManager.clearBiometrics(userId = state.userId) + authRepository.clearBiometrics(userId = state.userId) mutableStateFlow.update { it.copy(isUnlockWithBiometricsEnabled = false) } } diff --git a/app/src/main/kotlin/com/x8bit/bitwarden/ui/auth/feature/vaultunlock/VaultUnlockViewModel.kt b/app/src/main/kotlin/com/x8bit/bitwarden/ui/auth/feature/vaultunlock/VaultUnlockViewModel.kt index defa78ec50..1fb2ac77ad 100644 --- a/app/src/main/kotlin/com/x8bit/bitwarden/ui/auth/feature/vaultunlock/VaultUnlockViewModel.kt +++ b/app/src/main/kotlin/com/x8bit/bitwarden/ui/auth/feature/vaultunlock/VaultUnlockViewModel.kt @@ -21,7 +21,6 @@ import com.x8bit.bitwarden.data.credentials.model.CreateCredentialRequest import com.x8bit.bitwarden.data.credentials.model.Fido2CredentialAssertionRequest import com.x8bit.bitwarden.data.credentials.model.GetCredentialsRequest import com.x8bit.bitwarden.data.platform.manager.AppResumeManager -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.manager.util.toCreateCredentialRequestOrNull @@ -55,7 +54,6 @@ private const val KEY_STATE = "state" class VaultUnlockViewModel @Inject constructor( private val authRepository: AuthRepository, private val vaultRepo: VaultRepository, - private val biometricsEncryptionManager: BiometricsEncryptionManager, private val specialCircumstanceManager: SpecialCircumstanceManager, private val bitwardenCredentialManager: BitwardenCredentialManager, private val appResumeManager: AppResumeManager, @@ -68,10 +66,6 @@ class VaultUnlockViewModel @Inject constructor( val activeAccount = userState.activeAccount val accountSummaries = userState.toAccountSummaries() val activeAccountSummary = userState.toActiveAccountSummary() - val isBiometricsValid = biometricsEncryptionManager.isBiometricIntegrityValid( - userId = userState.activeUserId, - cipher = biometricsEncryptionManager.getOrCreateCipher(userState.activeUserId), - ) val vaultUnlockType = activeAccount.vaultUnlockType val hasNoMasterPassword = !activeAccount.hasMasterPassword if (!activeAccount.hasManualUnlockMechanism) { @@ -82,7 +76,6 @@ class VaultUnlockViewModel @Inject constructor( } val specialCircumstance = specialCircumstanceManager.specialCircumstance - val showAccountMenu = savedStateHandle.toVaultUnlockArgs().unlockType == UnlockType.STANDARD && (specialCircumstance !is SpecialCircumstance.ProviderGetCredentials && @@ -97,7 +90,7 @@ class VaultUnlockViewModel @Inject constructor( environmentUrl = activeAccount.environment.label, input = "", isBiometricEnabled = activeAccount.isBiometricsEnabled, - isBiometricsValid = isBiometricsValid, + isBiometricsValid = authRepository.isBiometricIntegrityValid(userState.activeUserId), showAccountMenu = showAccountMenu, showBiometricInvalidatedMessage = false, vaultUnlockType = vaultUnlockType, @@ -233,7 +226,7 @@ class VaultUnlockViewModel @Inject constructor( } private fun handleBiometricsUnlockClick() { - val cipher = biometricsEncryptionManager.getOrCreateCipher(state.userId) + val cipher = authRepository.getOrCreateCipher(state.userId) if (cipher != null) { sendEvent( event = VaultUnlockEvent.PromptForBiometrics( @@ -244,7 +237,7 @@ class VaultUnlockViewModel @Inject constructor( mutableStateFlow.update { it.copy( isBiometricsValid = false, - showBiometricInvalidatedMessage = !biometricsEncryptionManager + showBiometricInvalidatedMessage = !authRepository .isAccountBiometricIntegrityValid(state.userId), ) } @@ -351,7 +344,7 @@ class VaultUnlockViewModel @Inject constructor( } is VaultUnlockResult.BiometricDecodingError -> { - biometricsEncryptionManager.clearBiometrics(userId = state.userId) + authRepository.clearBiometrics(userId = state.userId) mutableStateFlow.update { it.copy( isBiometricsValid = false, @@ -433,13 +426,9 @@ class VaultUnlockViewModel @Inject constructor( } private fun promptForBiometricsIfAvailable() { - val cipher = biometricsEncryptionManager.getOrCreateCipher(state.userId) + val cipher = authRepository.getOrCreateCipher(state.userId) if (state.showBiometricLogin && cipher != null && !state.isFromLockFlow) { - sendEvent( - VaultUnlockEvent.PromptForBiometrics( - cipher = cipher, - ), - ) + sendEvent(VaultUnlockEvent.PromptForBiometrics(cipher = cipher)) } } } diff --git a/app/src/main/kotlin/com/x8bit/bitwarden/ui/platform/feature/settings/accountsecurity/AccountSecurityViewModel.kt b/app/src/main/kotlin/com/x8bit/bitwarden/ui/platform/feature/settings/accountsecurity/AccountSecurityViewModel.kt index 34c4618bef..a4d8a82a2e 100644 --- a/app/src/main/kotlin/com/x8bit/bitwarden/ui/platform/feature/settings/accountsecurity/AccountSecurityViewModel.kt +++ b/app/src/main/kotlin/com/x8bit/bitwarden/ui/platform/feature/settings/accountsecurity/AccountSecurityViewModel.kt @@ -18,7 +18,6 @@ import com.x8bit.bitwarden.data.auth.repository.model.LogoutReason import com.x8bit.bitwarden.data.auth.repository.model.PolicyInformation import com.x8bit.bitwarden.data.auth.repository.model.UserFingerprintResult import com.x8bit.bitwarden.data.auth.repository.util.policyInformation -import com.x8bit.bitwarden.data.platform.manager.BiometricsEncryptionManager import com.x8bit.bitwarden.data.platform.manager.FirstTimeActionManager import com.x8bit.bitwarden.data.platform.manager.PolicyManager import com.x8bit.bitwarden.data.platform.repository.EnvironmentRepository @@ -51,28 +50,20 @@ class AccountSecurityViewModel @Inject constructor( private val vaultRepository: VaultRepository, private val settingsRepository: SettingsRepository, private val environmentRepository: EnvironmentRepository, - private val biometricsEncryptionManager: BiometricsEncryptionManager, private val firstTimeActionManager: FirstTimeActionManager, policyManager: PolicyManager, savedStateHandle: SavedStateHandle, ) : BaseViewModel( initialState = savedStateHandle[KEY_STATE] ?: run { - val userId = requireNotNull(authRepository.userStateFlow.value).activeUserId - val isBiometricsValid = biometricsEncryptionManager.isBiometricIntegrityValid( - userId = userId, - cipher = biometricsEncryptionManager.getOrCreateCipher(userId), - ) + val userState = requireNotNull(authRepository.userStateFlow.value) + val userId = userState.activeUserId AccountSecurityState( dialog = null, fingerprintPhrase = "".asText(), // This will be filled in dynamically isAuthenticatorSyncChecked = settingsRepository.isAuthenticatorSyncEnabled, isUnlockWithBiometricsEnabled = settingsRepository.isUnlockWithBiometricsEnabled && - isBiometricsValid, - isUnlockWithPasswordEnabled = authRepository - .userStateFlow - .value - ?.activeAccount - ?.hasMasterPassword != false, + authRepository.isBiometricIntegrityValid(userId = userId), + isUnlockWithPasswordEnabled = userState.activeAccount.hasMasterPassword, isUnlockWithPinEnabled = settingsRepository.isUnlockWithPinEnabled, shouldShowEnableAuthenticatorSync = isBuildVersionAtLeast(Build.VERSION_CODES.S), userId = userId, @@ -230,7 +221,7 @@ class AccountSecurityViewModel @Inject constructor( } private fun handleEnableBiometricsClick() { - biometricsEncryptionManager + authRepository .createCipherOrNull(userId = state.userId) ?.let { sendEvent( @@ -319,7 +310,7 @@ class AccountSecurityViewModel @Inject constructor( } private fun handleUnlockWithBiometricToggleDisabled() { - biometricsEncryptionManager.clearBiometrics(userId = state.userId) + authRepository.clearBiometrics(userId = state.userId) mutableStateFlow.update { it.copy(isUnlockWithBiometricsEnabled = false) } validateVaultTimeoutAction() } diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/data/auth/repository/AuthRepositoryTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/data/auth/repository/AuthRepositoryTest.kt index cc49f9add0..d72fd2a06e 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/data/auth/repository/AuthRepositoryTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/data/auth/repository/AuthRepositoryTest.kt @@ -280,6 +280,7 @@ class AuthRepositoryTest { updateKdfToMinimumsIfNeeded(password = any()) } returns UpdateKdfMinimumsResult.Success } + private val repository: AuthRepository = AuthRepositoryImpl( clock = FIXED_CLOCK, accountsService = accountsService, @@ -296,6 +297,7 @@ class AuthRepositoryTest { settingsRepository = settingsRepository, vaultRepository = vaultRepository, authRequestManager = authRequestManager, + biometricsEncryptionManager = mockk(), keyConnectorManager = keyConnectorManager, trustedDeviceManager = trustedDeviceManager, userLogoutManager = userLogoutManager, diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/data/credentials/builder/CredentialEntryBuilderTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/data/credentials/builder/CredentialEntryBuilderTest.kt index 80b4b782d5..47eeab2d2b 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/data/credentials/builder/CredentialEntryBuilderTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/data/credentials/builder/CredentialEntryBuilderTest.kt @@ -12,8 +12,8 @@ import com.bitwarden.core.util.isBuildVersionAtLeast import com.bitwarden.fido.Fido2CredentialAutofillView import com.bitwarden.vault.CipherListView import com.bitwarden.vault.CipherListViewType +import com.x8bit.bitwarden.data.auth.repository.AuthRepository import com.x8bit.bitwarden.data.credentials.manager.CredentialManagerPendingIntentManager -import com.x8bit.bitwarden.data.platform.manager.BiometricsEncryptionManager import com.x8bit.bitwarden.data.util.mockBuilder import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockCipherListView import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockFido2CredentialAutofillView @@ -53,13 +53,13 @@ class CredentialEntryBuilderTest { ) } returns mockGetPasswordCredentialIntent } - private val mockBiometricsEncryptionManager = mockk() + private val mockAuthRepository = mockk() private val mockBeginGetPublicKeyOption = mockk() private val mockBeginGetPasswordOption = mockk() private val credentialEntryBuilder = CredentialEntryBuilderImpl( context = mockContext, pendingIntentManager = mockPendingIntentManager, - biometricsEncryptionManager = mockBiometricsEncryptionManager, + authRepository = mockAuthRepository, ) private val mockPublicKeyCredentialEntry = mockk(relaxed = true) private val mockPasswordCredentialEntry = mockk(relaxed = true) @@ -136,9 +136,7 @@ class CredentialEntryBuilderTest { createMockFido2CredentialAutofillView(number = 1), ) - every { - mockBiometricsEncryptionManager.getOrCreateCipher("userId") - } returns null + every { mockAuthRepository.getOrCreateCipher("userId") } returns null val result = credentialEntryBuilder .buildPublicKeyCredentialEntries( @@ -172,9 +170,7 @@ class CredentialEntryBuilderTest { // Verify biometric prompt data is not set when buildVersion is at least 35 // and cipher is null. - every { - mockBiometricsEncryptionManager.getOrCreateCipher("userId") - } returns null + every { mockAuthRepository.getOrCreateCipher("userId") } returns null every { isBuildVersionAtLeast(any()) } returns true credentialEntryBuilder @@ -216,9 +212,7 @@ class CredentialEntryBuilderTest { } // Verify biometric prompt data is not set when user is verified - every { - mockBiometricsEncryptionManager.getOrCreateCipher(any()) - } returns mockk(relaxed = true) + every { mockAuthRepository.getOrCreateCipher(any()) } returns mockk(relaxed = true) credentialEntryBuilder .buildPublicKeyCredentialEntries( userId = "userId", @@ -305,9 +299,7 @@ class CredentialEntryBuilderTest { ), ), ) - every { - mockBiometricsEncryptionManager.getOrCreateCipher("userId") - } returns null + every { mockAuthRepository.getOrCreateCipher("userId") } returns null val result = credentialEntryBuilder .buildPasswordCredentialEntries( @@ -347,9 +339,7 @@ class CredentialEntryBuilderTest { ), ) - every { - mockBiometricsEncryptionManager.getOrCreateCipher(any()) - } returns mockk(relaxed = true) + every { mockAuthRepository.getOrCreateCipher(any()) } returns mockk(relaxed = true) every { isBuildVersionAtLeast(any()) } returns true // Verify biometric prompt data is set when buildVersion is >= 35, cipher is diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/data/credentials/processor/CredentialProviderProcessorTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/data/credentials/processor/CredentialProviderProcessorTest.kt index 30bb3e9506..c0a57fe6dd 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/data/credentials/processor/CredentialProviderProcessorTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/data/credentials/processor/CredentialProviderProcessorTest.kt @@ -32,7 +32,6 @@ import com.x8bit.bitwarden.data.auth.repository.model.UserState import com.x8bit.bitwarden.data.auth.repository.model.VaultUnlockType import com.x8bit.bitwarden.data.credentials.manager.BitwardenCredentialManager import com.x8bit.bitwarden.data.credentials.manager.CredentialManagerPendingIntentManager -import com.x8bit.bitwarden.data.platform.manager.BiometricsEncryptionManager import com.x8bit.bitwarden.data.platform.manager.model.FirstTimeState import io.mockk.coEvery import io.mockk.every @@ -71,7 +70,6 @@ class CredentialProviderProcessorTest { } private val pendingIntentManager: CredentialManagerPendingIntentManager = mockk() private val dispatcherManager: DispatcherManager = FakeDispatcherManager() - private val biometricsEncryptionManager: BiometricsEncryptionManager = mockk() private val cancellationSignal: CancellationSignal = mockk() private val clock = FIXED_CLOCK @@ -84,7 +82,6 @@ class CredentialProviderProcessorTest { bitwardenCredentialManager = bitwardenCredentialManager, pendingIntentManager = pendingIntentManager, clock = clock, - biometricsEncryptionManager = biometricsEncryptionManager, dispatcherManager = dispatcherManager, ) @@ -167,9 +164,7 @@ class CredentialProviderProcessorTest { userId = any(), ) } returns mockIntent - every { - biometricsEncryptionManager.getOrCreateCipher(userId = any()) - } returns mockk() + every { authRepository.getOrCreateCipher(userId = any()) } returns mockk() every { cancellationSignal.setOnCancelListener(any()) } just runs every { callback.onResult(capture(captureSlot)) } just runs @@ -208,9 +203,7 @@ class CredentialProviderProcessorTest { userId = any(), ) } returns mockIntent - every { - biometricsEncryptionManager.getOrCreateCipher(any()) - } returns mockk() + every { authRepository.getOrCreateCipher(any()) } returns mockk() every { isBuildVersionAtLeast(Build.VERSION_CODES.VANILLA_ICE_CREAM) } returns true credentialProviderProcessor.processCreateCredentialRequest( @@ -249,7 +242,7 @@ class CredentialProviderProcessorTest { ) { "Expected all entries to have BIOMETRIC_STRONG authenticators." } // Verify entries have no biometric prompt data when cipher is null - every { biometricsEncryptionManager.getOrCreateCipher(any()) } returns null + every { authRepository.getOrCreateCipher(any()) } returns null credentialProviderProcessor.processCreateCredentialRequest( request = request, cancellationSignal = cancellationSignal, @@ -279,9 +272,7 @@ class CredentialProviderProcessorTest { userId = any(), ) } returns mockIntent - every { - biometricsEncryptionManager.getOrCreateCipher(userId = any()) - } returns mockk() + every { authRepository.getOrCreateCipher(userId = any()) } returns mockk() every { cancellationSignal.setOnCancelListener(any()) } just runs every { callback.onResult(capture(captureSlot)) } just runs every { isBuildVersionAtLeast(Build.VERSION_CODES.VANILLA_ICE_CREAM) } returns false @@ -332,7 +323,7 @@ class CredentialProviderProcessorTest { ) verify(exactly = 1) { callback.onResult(any()) } - verify(exactly = 0) { biometricsEncryptionManager.getOrCreateCipher(any()) } + verify(exactly = 0) { authRepository.getOrCreateCipher(any()) } // Verify entries have no biometric prompt data when vault is locked assertTrue(captureSlot.captured.createEntries.all { it.biometricPromptData == null }) { @@ -416,9 +407,7 @@ class CredentialProviderProcessorTest { userId = any(), ) } returns mockIntent - every { - biometricsEncryptionManager.getOrCreateCipher(userId = any()) - } returns mockk() + every { authRepository.getOrCreateCipher(userId = any()) } returns mockk() every { cancellationSignal.setOnCancelListener(any()) } just runs every { request.candidateQueryData } returns candidateQueryData every { @@ -464,9 +453,7 @@ class CredentialProviderProcessorTest { userId = any(), ) } returns mockIntent - every { - biometricsEncryptionManager.getOrCreateCipher(any()) - } returns mockk() + every { authRepository.getOrCreateCipher(any()) } returns mockk() every { isBuildVersionAtLeast(Build.VERSION_CODES.VANILLA_ICE_CREAM) } returns true credentialProviderProcessor.processCreateCredentialRequest( @@ -505,7 +492,7 @@ class CredentialProviderProcessorTest { ) { "Expected all entries to have BIOMETRIC_STRONG authenticators." } // Verify entries have no biometric prompt data when cipher is null - every { biometricsEncryptionManager.getOrCreateCipher(any()) } returns null + every { authRepository.getOrCreateCipher(any()) } returns null credentialProviderProcessor.processCreateCredentialRequest( request = request, cancellationSignal = cancellationSignal, diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/ui/auth/feature/accountsetup/SetupUnlockViewModelTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/ui/auth/feature/accountsetup/SetupUnlockViewModelTest.kt index b3a7c71ac2..0e7852f763 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/ui/auth/feature/accountsetup/SetupUnlockViewModelTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/ui/auth/feature/accountsetup/SetupUnlockViewModelTest.kt @@ -10,7 +10,6 @@ import com.x8bit.bitwarden.data.auth.datasource.disk.model.OnboardingStatus import com.x8bit.bitwarden.data.auth.repository.AuthRepository import com.x8bit.bitwarden.data.auth.repository.model.UserState import com.x8bit.bitwarden.data.autofill.manager.browser.BrowserThirdPartyAutofillEnabledManager -import com.x8bit.bitwarden.data.platform.manager.BiometricsEncryptionManager import com.x8bit.bitwarden.data.platform.manager.FirstTimeActionManager import com.x8bit.bitwarden.data.platform.manager.model.FirstTimeState import com.x8bit.bitwarden.data.platform.repository.SettingsRepository @@ -40,6 +39,8 @@ class SetupUnlockViewModelTest : BaseViewModelTest() { private val authRepository: AuthRepository = mockk { every { userStateFlow } returns mutableUserStateFlow every { setOnboardingStatus(status = any()) } just runs + every { isBiometricIntegrityValid(userId = DEFAULT_USER_ID) } returns false + every { createCipherOrNull(DEFAULT_USER_ID) } returns CIPHER } private val mutableAutofillEnabledStateFlow = MutableStateFlow(false) @@ -53,13 +54,6 @@ class SetupUnlockViewModelTest : BaseViewModelTest() { every { firstTimeStateFlow } returns mutableFirstTimeStateFlow every { storeShowUnlockSettingBadge(any()) } just runs } - private val biometricsEncryptionManager: BiometricsEncryptionManager = mockk { - every { getOrCreateCipher(userId = DEFAULT_USER_ID) } returns CIPHER - every { - isBiometricIntegrityValid(userId = DEFAULT_USER_ID, cipher = CIPHER) - } returns false - every { createCipherOrNull(DEFAULT_USER_ID) } returns CIPHER - } private val thirdPartyAutofillEnabledManager: BrowserThirdPartyAutofillEnabledManager = mockk { every { browserThirdPartyAutofillStatus @@ -180,7 +174,7 @@ class SetupUnlockViewModelTest : BaseViewModelTest() { fun `on UnlockWithBiometricToggleDisabled should call clearBiometricsKey and update the state`() { val initialState = DEFAULT_STATE.copy(isUnlockWithBiometricsEnabled = true) every { settingsRepository.isUnlockWithBiometricsEnabled } returns true - every { biometricsEncryptionManager.clearBiometrics(userId = DEFAULT_USER_ID) } just runs + every { authRepository.clearBiometrics(userId = DEFAULT_USER_ID) } just runs val viewModel = createViewModel(initialState) assertEquals(initialState, viewModel.stateFlow.value) @@ -191,7 +185,7 @@ class SetupUnlockViewModelTest : BaseViewModelTest() { viewModel.stateFlow.value, ) verify(exactly = 1) { - biometricsEncryptionManager.clearBiometrics(userId = DEFAULT_USER_ID) + authRepository.clearBiometrics(userId = DEFAULT_USER_ID) } } @@ -340,25 +334,21 @@ class SetupUnlockViewModelTest : BaseViewModelTest() { runTest { val viewModel = createViewModel() - viewModel.trySendAction(SetupUnlockAction.EnableBiometricsClick) - - verify { - biometricsEncryptionManager.getOrCreateCipher(DEFAULT_USER_ID) - } - viewModel.eventFlow.test { + viewModel.trySendAction(SetupUnlockAction.EnableBiometricsClick) assertEquals( SetupUnlockEvent.ShowBiometricsPrompt(CIPHER), awaitItem(), ) } + verify { + authRepository.createCipherOrNull(DEFAULT_USER_ID) + } } @Test fun `EnableBiometricsClick actin should show error dialog when cipher is null`() { - every { - biometricsEncryptionManager.createCipherOrNull(DEFAULT_USER_ID) - } returns null + every { authRepository.createCipherOrNull(DEFAULT_USER_ID) } returns null val viewModel = createViewModel() viewModel.trySendAction(SetupUnlockAction.EnableBiometricsClick) @@ -411,7 +401,6 @@ class SetupUnlockViewModelTest : BaseViewModelTest() { }, authRepository = authRepository, settingsRepository = settingsRepository, - biometricsEncryptionManager = biometricsEncryptionManager, firstTimeActionManager = firstTimeActionManager, browserThirdPartyAutofillEnabledManager = thirdPartyAutofillEnabledManager, ) diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/ui/auth/feature/vaultunlock/VaultUnlockViewModelTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/ui/auth/feature/vaultunlock/VaultUnlockViewModelTest.kt index 34f7c69df0..ff87087072 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/ui/auth/feature/vaultunlock/VaultUnlockViewModelTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/ui/auth/feature/vaultunlock/VaultUnlockViewModelTest.kt @@ -18,7 +18,6 @@ import com.x8bit.bitwarden.data.credentials.manager.BitwardenCredentialManager import com.x8bit.bitwarden.data.credentials.model.createMockFido2CredentialAssertionRequest import com.x8bit.bitwarden.data.credentials.model.createMockGetCredentialsRequest import com.x8bit.bitwarden.data.platform.manager.AppResumeManager -import com.x8bit.bitwarden.data.platform.manager.BiometricsEncryptionManager import com.x8bit.bitwarden.data.platform.manager.SpecialCircumstanceManager import com.x8bit.bitwarden.data.platform.manager.model.FirstTimeState import com.x8bit.bitwarden.data.platform.manager.model.SpecialCircumstance @@ -60,25 +59,12 @@ class VaultUnlockViewModelTest : BaseViewModelTest() { every { logout(reason = any()) } just runs every { logout(userId = any(), reason = any()) } just runs every { switchAccount(any()) } returns SwitchAccountResult.AccountSwitched + every { getOrCreateCipher(USER_ID) } returns CIPHER + every { isBiometricIntegrityValid(userId = DEFAULT_USER_STATE.activeUserId) } returns true } private val vaultRepository: VaultRepository = mockk(relaxed = true) { every { lockVault(any(), any()) } just runs } - private val encryptionManager: BiometricsEncryptionManager = mockk { - every { getOrCreateCipher(USER_ID) } returns CIPHER - every { - isBiometricIntegrityValid( - userId = DEFAULT_USER_STATE.activeUserId, - cipher = CIPHER, - ) - } returns true - every { - isBiometricIntegrityValid( - userId = DEFAULT_USER_STATE.activeUserId, - cipher = null, - ) - } returns false - } private val bitwardenCredentialManager: BitwardenCredentialManager = mockk { every { isUserVerified } returns true every { isUserVerified = any() } just runs @@ -124,7 +110,7 @@ class VaultUnlockViewModelTest : BaseViewModelTest() { fun `initial state should be correct when not set`() { val viewModel = createViewModel() assertEquals(DEFAULT_STATE, viewModel.stateFlow.value) - verify { encryptionManager.getOrCreateCipher(USER_ID) } + verify { authRepository.getOrCreateCipher(USER_ID) } } @Test @@ -506,7 +492,7 @@ class VaultUnlockViewModelTest : BaseViewModelTest() { viewModel.trySendAction(VaultUnlockAction.BiometricsUnlockClick) assertEquals(VaultUnlockEvent.PromptForBiometrics(CIPHER), awaitItem()) } - verify { encryptionManager.getOrCreateCipher(USER_ID) } + verify { authRepository.getOrCreateCipher(USER_ID) } } @Test @@ -533,8 +519,8 @@ class VaultUnlockViewModelTest : BaseViewModelTest() { fun `on BiometricsUnlockClick should disable isBiometricsValid and show message when cipher is null and integrity check returns false`() { val initialState = DEFAULT_STATE.copy(isBiometricsValid = true) val viewModel = createViewModel(state = initialState) - every { encryptionManager.getOrCreateCipher(USER_ID) } returns null - every { encryptionManager.isAccountBiometricIntegrityValid(USER_ID) } returns false + every { authRepository.getOrCreateCipher(USER_ID) } returns null + every { authRepository.isAccountBiometricIntegrityValid(USER_ID) } returns false viewModel.trySendAction(VaultUnlockAction.BiometricsUnlockClick) assertEquals( @@ -544,7 +530,7 @@ class VaultUnlockViewModelTest : BaseViewModelTest() { ), viewModel.stateFlow.value, ) - verify { encryptionManager.getOrCreateCipher(USER_ID) } + verify { authRepository.getOrCreateCipher(USER_ID) } } @Suppress("MaxLineLength") @@ -552,8 +538,8 @@ class VaultUnlockViewModelTest : BaseViewModelTest() { fun `on BiometricsUnlockClick should disable isBiometricsValid and not show message when cipher is null and integrity check returns true`() { val initialState = DEFAULT_STATE.copy(isBiometricsValid = true) val viewModel = createViewModel(state = initialState) - every { encryptionManager.getOrCreateCipher(USER_ID) } returns null - every { encryptionManager.isAccountBiometricIntegrityValid(USER_ID) } returns true + every { authRepository.getOrCreateCipher(USER_ID) } returns null + every { authRepository.isAccountBiometricIntegrityValid(USER_ID) } returns true viewModel.trySendAction(VaultUnlockAction.BiometricsUnlockClick) assertEquals( @@ -563,7 +549,7 @@ class VaultUnlockViewModelTest : BaseViewModelTest() { ), viewModel.stateFlow.value, ) - verify { encryptionManager.getOrCreateCipher(USER_ID) } + verify { authRepository.getOrCreateCipher(USER_ID) } } @Test @@ -718,7 +704,7 @@ class VaultUnlockViewModelTest : BaseViewModelTest() { } // The initial state causes this to be called as well as the change. verify(exactly = 2) { - encryptionManager.getOrCreateCipher(USER_ID) + authRepository.getOrCreateCipher(USER_ID) } } @@ -745,7 +731,7 @@ class VaultUnlockViewModelTest : BaseViewModelTest() { } // Only the call for the initial state should be called. verify(exactly = 1) { - encryptionManager.getOrCreateCipher(USER_ID) + authRepository.getOrCreateCipher(USER_ID) } } @@ -1168,7 +1154,7 @@ class VaultUnlockViewModelTest : BaseViewModelTest() { coEvery { vaultRepository.unlockVaultWithBiometrics(cipher = CIPHER) } returns VaultUnlockResult.BiometricDecodingError(error = Throwable("Fail")) - every { encryptionManager.clearBiometrics(userId = USER_ID) } just runs + every { authRepository.clearBiometrics(userId = USER_ID) } just runs viewModel.trySendAction(VaultUnlockAction.BiometricsUnlockSuccess(CIPHER)) @@ -1183,7 +1169,7 @@ class VaultUnlockViewModelTest : BaseViewModelTest() { viewModel.stateFlow.value, ) coVerify(exactly = 1) { - encryptionManager.clearBiometrics(userId = USER_ID) + authRepository.clearBiometrics(userId = USER_ID) vaultRepository.unlockVaultWithBiometrics(cipher = CIPHER) } } @@ -1341,7 +1327,6 @@ class VaultUnlockViewModelTest : BaseViewModelTest() { state: VaultUnlockState? = null, unlockType: UnlockType = UnlockType.STANDARD, vaultRepo: VaultRepository = vaultRepository, - biometricsEncryptionManager: BiometricsEncryptionManager = encryptionManager, lockManager: VaultLockManager = vaultLockManager, ): VaultUnlockViewModel = VaultUnlockViewModel( savedStateHandle = SavedStateHandle().apply { @@ -1350,7 +1335,6 @@ class VaultUnlockViewModelTest : BaseViewModelTest() { }, authRepository = authRepository, vaultRepo = vaultRepo, - biometricsEncryptionManager = biometricsEncryptionManager, bitwardenCredentialManager = bitwardenCredentialManager, specialCircumstanceManager = specialCircumstanceManager, appResumeManager = appResumeManager, diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/ui/platform/feature/settings/accountsecurity/AccountSecurityViewModelTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/ui/platform/feature/settings/accountsecurity/AccountSecurityViewModelTest.kt index fb308297c3..94a2b60e84 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/ui/platform/feature/settings/accountsecurity/AccountSecurityViewModelTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/ui/platform/feature/settings/accountsecurity/AccountSecurityViewModelTest.kt @@ -21,7 +21,6 @@ import com.x8bit.bitwarden.data.auth.repository.model.PolicyInformation import com.x8bit.bitwarden.data.auth.repository.model.UserFingerprintResult import com.x8bit.bitwarden.data.auth.repository.model.UserState import com.x8bit.bitwarden.data.platform.error.NoActiveUserException -import com.x8bit.bitwarden.data.platform.manager.BiometricsEncryptionManager import com.x8bit.bitwarden.data.platform.manager.FirstTimeActionManager import com.x8bit.bitwarden.data.platform.manager.PolicyManager import com.x8bit.bitwarden.data.platform.manager.model.FirstTimeState @@ -62,6 +61,8 @@ class AccountSecurityViewModelTest : BaseViewModelTest() { private val mutableUserStateFlow = MutableStateFlow(DEFAULT_USER_STATE) private val authRepository: AuthRepository = mockk(relaxed = true) { every { userStateFlow } returns mutableUserStateFlow + every { createCipherOrNull(DEFAULT_USER_STATE.activeUserId) } returns CIPHER + every { isBiometricIntegrityValid(userId = DEFAULT_USER_STATE.activeUserId) } returns true } private val vaultRepository: VaultRepository = mockk(relaxed = true) private val mutableBiometricsUnlockEnabledFlow = bufferedMutableSharedFlow() @@ -84,16 +85,6 @@ class AccountSecurityViewModelTest : BaseViewModelTest() { } private val mutableActivePolicyFlow = bufferedMutableSharedFlow>() private val mutableRemovePinPolicyFlow = bufferedMutableSharedFlow>() - private val biometricsEncryptionManager: BiometricsEncryptionManager = mockk { - every { createCipherOrNull(DEFAULT_USER_STATE.activeUserId) } returns CIPHER - every { getOrCreateCipher(DEFAULT_USER_STATE.activeUserId) } returns CIPHER - every { - isBiometricIntegrityValid( - userId = DEFAULT_USER_STATE.activeUserId, - cipher = CIPHER, - ) - } returns true - } private val policyManager: PolicyManager = mockk { every { getActivePoliciesFlow(type = PolicyTypeJson.MAXIMUM_VAULT_TIMEOUT) @@ -123,16 +114,9 @@ class AccountSecurityViewModelTest : BaseViewModelTest() { @Test fun `initial state should be correct when saved state is not set`() { val viewModel = createViewModel(initialState = null) - assertEquals( - DEFAULT_STATE, - viewModel.stateFlow.value, - ) + assertEquals(DEFAULT_STATE, viewModel.stateFlow.value) verify { - biometricsEncryptionManager.getOrCreateCipher(DEFAULT_USER_STATE.activeUserId) - biometricsEncryptionManager.isBiometricIntegrityValid( - userId = DEFAULT_USER_STATE.activeUserId, - cipher = CIPHER, - ) + settingsRepository.isUnlockWithBiometricsEnabled } coVerify { settingsRepository.getUserFingerprint() } } @@ -510,9 +494,7 @@ class AccountSecurityViewModelTest : BaseViewModelTest() { @Test fun `on EnableBiometricsClick should show Error dialog when cipher is null`() { - every { - biometricsEncryptionManager.createCipherOrNull(DEFAULT_USER_STATE.activeUserId) - } returns null + every { authRepository.createCipherOrNull(DEFAULT_USER_STATE.activeUserId) } returns null val viewModel = createViewModel() viewModel.trySendAction(AccountSecurityAction.EnableBiometricsClick) @@ -534,9 +516,7 @@ class AccountSecurityViewModelTest : BaseViewModelTest() { runTest { val initialState = DEFAULT_STATE.copy(isUnlockWithBiometricsEnabled = true) every { settingsRepository.isUnlockWithBiometricsEnabled } returns true - every { - biometricsEncryptionManager.clearBiometrics(userId = DEFAULT_USER_ID) - } just runs + every { authRepository.clearBiometrics(userId = DEFAULT_USER_ID) } just runs val viewModel = createViewModel(initialState) assertEquals(initialState, viewModel.stateFlow.value) @@ -547,7 +527,7 @@ class AccountSecurityViewModelTest : BaseViewModelTest() { viewModel.stateFlow.value, ) verify(exactly = 1) { - biometricsEncryptionManager.clearBiometrics(userId = DEFAULT_USER_ID) + authRepository.clearBiometrics(userId = DEFAULT_USER_ID) } } @@ -560,9 +540,7 @@ class AccountSecurityViewModelTest : BaseViewModelTest() { isUnlockWithBiometricsEnabled = true, ) every { settingsRepository.isUnlockWithBiometricsEnabled } returns true - every { - biometricsEncryptionManager.clearBiometrics(userId = DEFAULT_USER_ID) - } just runs + every { authRepository.clearBiometrics(userId = DEFAULT_USER_ID) } just runs every { settingsRepository.vaultTimeoutAction = VaultTimeoutAction.LOGOUT } just runs val viewModel = createViewModel(initialState) assertEquals(initialState, viewModel.stateFlow.value) @@ -577,7 +555,7 @@ class AccountSecurityViewModelTest : BaseViewModelTest() { viewModel.stateFlow.value, ) verify(exactly = 1) { - biometricsEncryptionManager.clearBiometrics(userId = DEFAULT_USER_ID) + authRepository.clearBiometrics(userId = DEFAULT_USER_ID) settingsRepository.vaultTimeoutAction = VaultTimeoutAction.LOGOUT } } @@ -903,14 +881,12 @@ class AccountSecurityViewModelTest : BaseViewModelTest() { vaultRepository: VaultRepository = this.vaultRepository, environmentRepository: EnvironmentRepository = this.fakeEnvironmentRepository, settingsRepository: SettingsRepository = this.settingsRepository, - biometricsEncryptionManager: BiometricsEncryptionManager = this.biometricsEncryptionManager, policyManager: PolicyManager = this.policyManager, ): AccountSecurityViewModel = AccountSecurityViewModel( authRepository = authRepository, vaultRepository = vaultRepository, settingsRepository = settingsRepository, environmentRepository = environmentRepository, - biometricsEncryptionManager = biometricsEncryptionManager, policyManager = policyManager, savedStateHandle = SavedStateHandle().apply { set("state", initialState)