mirror of
https://github.com/bitwarden/android.git
synced 2025-03-16 03:08:50 +03:00
Clear passwordToCheck at appropriate times (#1385)
This commit is contained in:
parent
0149da361c
commit
03a97258e5
3 changed files with 54 additions and 25 deletions
|
@ -70,6 +70,7 @@ import com.x8bit.bitwarden.data.auth.repository.util.userAccountTokens
|
|||
import com.x8bit.bitwarden.data.auth.repository.util.userAccountTokensFlow
|
||||
import com.x8bit.bitwarden.data.auth.repository.util.userOrganizationsList
|
||||
import com.x8bit.bitwarden.data.auth.repository.util.userOrganizationsListFlow
|
||||
import com.x8bit.bitwarden.data.auth.repository.util.userSwitchingChangesFlow
|
||||
import com.x8bit.bitwarden.data.auth.util.KdfParamsConstants.DEFAULT_PBKDF2_ITERATIONS
|
||||
import com.x8bit.bitwarden.data.auth.util.YubiKeyResult
|
||||
import com.x8bit.bitwarden.data.auth.util.toSdkParams
|
||||
|
@ -99,11 +100,13 @@ import kotlinx.coroutines.flow.SharingStarted
|
|||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asSharedFlow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.filter
|
||||
import kotlinx.coroutines.flow.filterNot
|
||||
import kotlinx.coroutines.flow.flatMapLatest
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.mapNotNull
|
||||
import kotlinx.coroutines.flow.merge
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.flow.receiveAsFlow
|
||||
|
@ -188,9 +191,9 @@ class AuthRepositoryImpl(
|
|||
|
||||
/**
|
||||
* The password that needs to be checked against any organization policies before
|
||||
* the user can complete the login flow.
|
||||
* the user can complete the login flow. This value is stored using the user ID.
|
||||
*/
|
||||
private var passwordToCheck: String? = null
|
||||
private var passwordsToCheckMap = mutableMapOf<String, String>()
|
||||
|
||||
override var twoFactorResponse: GetTokenResponseJson.TwoFactorRequired? = null
|
||||
|
||||
|
@ -331,6 +334,9 @@ class AuthRepositoryImpl(
|
|||
.onEach { policies ->
|
||||
val userId = activeUserId ?: return@onEach
|
||||
|
||||
// If the user is logging on without a password, the check should complete.
|
||||
val passwordToCheck = passwordsToCheckMap.remove(key = userId) ?: return@onEach
|
||||
|
||||
// If the password already has to be reset for some other reason, there's no
|
||||
// need to check the password policies.
|
||||
if (passwordResetReason != null) return@onEach
|
||||
|
@ -339,13 +345,34 @@ class AuthRepositoryImpl(
|
|||
// clear the force reset reason accordingly.
|
||||
storeUserResetPasswordReason(
|
||||
userId = userId,
|
||||
reason = ForcePasswordResetReason.WEAK_MASTER_PASSWORD_ON_LOGIN
|
||||
reason = ForcePasswordResetReason
|
||||
.WEAK_MASTER_PASSWORD_ON_LOGIN
|
||||
.takeIf {
|
||||
!passwordPassesPolicies(policies)
|
||||
!passwordPassesPolicies(
|
||||
password = passwordToCheck,
|
||||
policies = policies,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
.launchIn(unconfinedScope)
|
||||
|
||||
// Clear the cached password whenever the user is no longer active
|
||||
// or the vault is locked for that user.
|
||||
merge(
|
||||
authDiskSource
|
||||
.userSwitchingChangesFlow
|
||||
.mapNotNull { it.previousActiveUserId },
|
||||
vaultRepository
|
||||
.vaultUnlockDataStateFlow
|
||||
.filter { vaultUnlockDataList ->
|
||||
// Clear if the active user is not currently unlocking or unlocked
|
||||
vaultUnlockDataList.none { it.userId == activeUserId }
|
||||
}
|
||||
.mapNotNull { activeUserId },
|
||||
)
|
||||
.onEach { userId -> passwordsToCheckMap.remove(key = userId) }
|
||||
.launchIn(unconfinedScope)
|
||||
}
|
||||
|
||||
override fun clearPendingAccountDeletion() {
|
||||
|
@ -1102,16 +1129,13 @@ class AuthRepositoryImpl(
|
|||
* Return true if there are any [PolicyInformation.MasterPassword] policies that the user's
|
||||
* master password has failed to pass.
|
||||
*/
|
||||
@Suppress("ReturnCount")
|
||||
private suspend fun passwordPassesPolicies(policies: List<SyncResponseJson.Policy>?): Boolean {
|
||||
// If the user is logging on without a password or if there are no policies,
|
||||
// the check should complete.
|
||||
val password = passwordToCheck ?: return true
|
||||
val policyList = policies ?: return true
|
||||
|
||||
private suspend fun passwordPassesPolicies(
|
||||
password: String,
|
||||
policies: List<SyncResponseJson.Policy>,
|
||||
): Boolean {
|
||||
// If there are no master password policies that are enabled and should be
|
||||
// enforced on login, the check should complete.
|
||||
val passwordPolicies = policyList
|
||||
val passwordPolicies = policies
|
||||
.mapNotNull { it.policyInformation as? PolicyInformation.MasterPassword }
|
||||
.filter { it.enforceOnLogin == true }
|
||||
|
||||
|
@ -1351,7 +1375,7 @@ class AuthRepositoryImpl(
|
|||
}
|
||||
|
||||
// Cache the password to verify against any password policies after the sync completes.
|
||||
passwordToCheck = it
|
||||
passwordsToCheckMap.put(userId, it)
|
||||
}
|
||||
|
||||
// Attempt to unlock the vault with auth request if possible.
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package com.x8bit.bitwarden.data.platform.manager
|
||||
|
||||
import com.x8bit.bitwarden.data.auth.datasource.disk.AuthDiskSource
|
||||
import com.x8bit.bitwarden.data.auth.repository.util.activeUserIdChangesFlow
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.OrganizationStatusType
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.OrganizationType
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.PolicyTypeJson
|
||||
|
@ -10,7 +11,7 @@ import kotlinx.coroutines.flow.Flow
|
|||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.emptyFlow
|
||||
import kotlinx.coroutines.flow.flatMapLatest
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.mapNotNull
|
||||
|
||||
/**
|
||||
* The default [PolicyManager] implementation. This class is responsible for
|
||||
|
@ -22,15 +23,15 @@ class PolicyManagerImpl(
|
|||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
override fun getActivePoliciesFlow(type: PolicyTypeJson): Flow<List<SyncResponseJson.Policy>> =
|
||||
authDiskSource
|
||||
.userStateFlow
|
||||
.flatMapLatest { userStateJson ->
|
||||
userStateJson
|
||||
?.activeUserId
|
||||
?.let { activeUserId ->
|
||||
authDiskSource.getPoliciesFlow(activeUserId)
|
||||
.map {
|
||||
.activeUserIdChangesFlow
|
||||
.flatMapLatest { activeUserId ->
|
||||
activeUserId
|
||||
?.let { userId ->
|
||||
authDiskSource
|
||||
.getPoliciesFlow(userId)
|
||||
.mapNotNull {
|
||||
filterPolicies(
|
||||
userId = activeUserId,
|
||||
userId = userId,
|
||||
type = type,
|
||||
policies = it,
|
||||
)
|
||||
|
@ -56,12 +57,14 @@ class PolicyManagerImpl(
|
|||
/**
|
||||
* A helper method to filter policies.
|
||||
*/
|
||||
@Suppress("ReturnCount")
|
||||
private fun filterPolicies(
|
||||
userId: String,
|
||||
type: PolicyTypeJson,
|
||||
policies: List<SyncResponseJson.Policy>?,
|
||||
): List<SyncResponseJson.Policy> {
|
||||
if (policies.isNullOrEmpty()) return emptyList()
|
||||
): List<SyncResponseJson.Policy>? {
|
||||
policies ?: return null
|
||||
if (policies.isEmpty()) return emptyList()
|
||||
|
||||
// Get a list of the user's organizations that enforce policies.
|
||||
val organizationIdsWithActivePolicies = authDiskSource
|
||||
|
|
|
@ -385,9 +385,11 @@ class VaultRepositoryImpl(
|
|||
|
||||
unlockVaultForOrganizationsIfNecessary(syncResponse = syncResponse)
|
||||
storeProfileData(syncResponse = syncResponse)
|
||||
// Treat absent network policies as known empty data to
|
||||
// distinguish between unknown null data.
|
||||
authDiskSource.storePolicies(
|
||||
userId = userId,
|
||||
policies = syncResponse.policies,
|
||||
policies = syncResponse.policies.orEmpty(),
|
||||
)
|
||||
settingsDiskSource.storeLastSyncTime(
|
||||
userId = userId,
|
||||
|
|
Loading…
Add table
Reference in a new issue