Refactor vault unlock state to enum (#853)

This commit is contained in:
Shannon Draeker
2024-01-29 20:16:23 -07:00
committed by Álison Fernandes
parent d8ee29a0a4
commit a317174db7
14 changed files with 423 additions and 440 deletions

View File

@@ -144,7 +144,7 @@ class AuthRepositoryImpl(
override val userStateFlow: StateFlow<UserState?> = combine(
authDiskSource.userStateFlow,
authDiskSource.userOrganizationsListFlow,
vaultRepository.vaultStateFlow,
vaultRepository.vaultUnlockDataStateFlow,
mutableHasPendingAccountAdditionStateFlow,
mutableHasPendingAccountDeletionStateFlow,
) {
@@ -174,7 +174,7 @@ class AuthRepositoryImpl(
initialValue = authDiskSource
.userState
?.toUserState(
vaultState = vaultRepository.vaultStateFlow.value,
vaultState = vaultRepository.vaultUnlockDataStateFlow.value,
userOrganizationsList = authDiskSource.userOrganizationsList,
hasPendingAccountAddition = mutableHasPendingAccountAdditionStateFlow.value,
isBiometricsEnabledProvider = ::isBiometricsEnabled,

View File

@@ -6,7 +6,8 @@ import com.x8bit.bitwarden.data.auth.repository.model.UserState
import com.x8bit.bitwarden.data.auth.repository.model.VaultUnlockType
import com.x8bit.bitwarden.data.platform.repository.util.toEnvironmentUrlsOrDefault
import com.x8bit.bitwarden.data.vault.datasource.network.model.SyncResponseJson
import com.x8bit.bitwarden.data.vault.repository.model.VaultState
import com.x8bit.bitwarden.data.vault.repository.model.VaultUnlockData
import com.x8bit.bitwarden.data.vault.repository.util.statusFor
import com.x8bit.bitwarden.ui.platform.base.util.toHexColorRepresentation
/**
@@ -44,7 +45,7 @@ fun UserStateJson.toUpdatedUserStateJson(
* Converts the given [UserStateJson] to a [UserState] using the given [vaultState].
*/
fun UserStateJson.toUserState(
vaultState: VaultState,
vaultState: List<VaultUnlockData>,
userOrganizationsList: List<UserOrganizations>,
hasPendingAccountAddition: Boolean,
isBiometricsEnabledProvider: (userId: String) -> Boolean,
@@ -69,7 +70,8 @@ fun UserStateJson.toUserState(
.toEnvironmentUrlsOrDefault(),
isPremium = accountJson.profile.hasPremium == true,
isLoggedIn = accountJson.isLoggedIn,
isVaultUnlocked = userId in vaultState.unlockedVaultUserIds,
isVaultUnlocked = vaultState.statusFor(userId) ==
VaultUnlockData.Status.UNLOCKED,
organizations = userOrganizationsList
.find { it.userId == userId }
?.organizations

View File

@@ -7,10 +7,12 @@ import com.x8bit.bitwarden.data.autofill.model.AutofillCipher
import com.x8bit.bitwarden.data.platform.manager.ciphermatching.CipherMatchingManager
import com.x8bit.bitwarden.data.platform.util.subtitle
import com.x8bit.bitwarden.data.vault.repository.VaultRepository
import com.x8bit.bitwarden.data.vault.repository.model.VaultUnlockData
import com.x8bit.bitwarden.data.vault.repository.util.statusFor
import kotlinx.coroutines.flow.first
/**
* The default [AutofillCipherProvider] implementation. This service is used for getting currrent
* The default [AutofillCipherProvider] implementation. This service is used for getting current
* [AutofillCipher]s.
*/
class AutofillCipherProviderImpl(
@@ -25,7 +27,9 @@ class AutofillCipherProviderImpl(
// Wait for any unlocking actions to finish. This can be relevant on startup for Never lock
// accounts.
vaultRepository.vaultStateFlow.first { userId !in it.unlockingVaultUserIds }
vaultRepository.vaultUnlockDataStateFlow.first {
it.statusFor(userId) != VaultUnlockData.Status.UNLOCKING
}
return !vaultRepository.isVaultUnlocked(userId = userId)
}

View File

@@ -2,7 +2,7 @@ package com.x8bit.bitwarden.data.vault.manager
import com.bitwarden.core.InitUserCryptoMethod
import com.bitwarden.crypto.Kdf
import com.x8bit.bitwarden.data.vault.repository.model.VaultState
import com.x8bit.bitwarden.data.vault.repository.model.VaultUnlockData
import com.x8bit.bitwarden.data.vault.repository.model.VaultUnlockResult
import kotlinx.coroutines.flow.StateFlow
@@ -11,9 +11,9 @@ import kotlinx.coroutines.flow.StateFlow
*/
interface VaultLockManager {
/**
* Flow that represents the current vault state.
* Flow that represents the current vault lock state for each user.
*/
val vaultStateFlow: StateFlow<VaultState>
val vaultUnlockDataStateFlow: StateFlow<List<VaultUnlockData>>
/**
* Whether or not the vault is currently locked for the given [userId].

View File

@@ -20,9 +20,11 @@ import com.x8bit.bitwarden.data.platform.util.asSuccess
import com.x8bit.bitwarden.data.platform.util.flatMap
import com.x8bit.bitwarden.data.vault.datasource.sdk.VaultSdkSource
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.InitializeCryptoResult
import com.x8bit.bitwarden.data.vault.repository.model.VaultState
import com.x8bit.bitwarden.data.vault.repository.model.VaultUnlockData
import com.x8bit.bitwarden.data.vault.repository.model.VaultUnlockResult
import com.x8bit.bitwarden.data.vault.repository.util.statusFor
import com.x8bit.bitwarden.data.vault.repository.util.toVaultUnlockResult
import com.x8bit.bitwarden.data.vault.repository.util.update
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
@@ -68,16 +70,11 @@ class VaultLockManagerImpl(
private val activeUserId: String? get() = authDiskSource.userState?.activeUserId
private val userIds: Set<String> get() = authDiskSource.userState?.accounts?.keys.orEmpty()
private val mutableVaultStateStateFlow =
MutableStateFlow(
VaultState(
unlockedVaultUserIds = emptySet(),
unlockingVaultUserIds = emptySet(),
),
)
private val mutableVaultUnlockDataStateFlow =
MutableStateFlow<List<VaultUnlockData>>(emptyList())
override val vaultStateFlow: StateFlow<VaultState>
get() = mutableVaultStateStateFlow.asStateFlow()
override val vaultUnlockDataStateFlow: StateFlow<List<VaultUnlockData>>
get() = mutableVaultUnlockDataStateFlow.asStateFlow()
init {
observeAppForegroundChanges()
@@ -86,10 +83,10 @@ class VaultLockManagerImpl(
}
override fun isVaultUnlocked(userId: String): Boolean =
userId in mutableVaultStateStateFlow.value.unlockedVaultUserIds
mutableVaultUnlockDataStateFlow.value.statusFor(userId) == VaultUnlockData.Status.UNLOCKED
override fun isVaultUnlocking(userId: String): Boolean =
userId in mutableVaultStateStateFlow.value.unlockingVaultUserIds
mutableVaultUnlockDataStateFlow.value.statusFor(userId) == VaultUnlockData.Status.UNLOCKING
override fun lockVault(userId: String) {
setVaultToLocked(userId = userId)
@@ -205,10 +202,8 @@ class VaultLockManagerImpl(
}
private fun setVaultToUnlocked(userId: String) {
mutableVaultStateStateFlow.update {
it.copy(
unlockedVaultUserIds = it.unlockedVaultUserIds + userId,
)
mutableVaultUnlockDataStateFlow.update {
it.update(userId, VaultUnlockData.Status.UNLOCKED)
}
// If we are unlocking an account with a timeout of Never, we should make sure to store the
// auto-unlock key.
@@ -217,10 +212,8 @@ class VaultLockManagerImpl(
private fun setVaultToLocked(userId: String) {
vaultSdkSource.clearCrypto(userId = userId)
mutableVaultStateStateFlow.update {
it.copy(
unlockedVaultUserIds = it.unlockedVaultUserIds - userId,
)
mutableVaultUnlockDataStateFlow.update {
it.update(userId, null)
}
authDiskSource.storeUserAutoUnlockKey(
userId = userId,
@@ -229,18 +222,16 @@ class VaultLockManagerImpl(
}
private fun setVaultToUnlocking(userId: String) {
mutableVaultStateStateFlow.update {
it.copy(
unlockingVaultUserIds = it.unlockingVaultUserIds + userId,
)
mutableVaultUnlockDataStateFlow.update {
it.update(userId, VaultUnlockData.Status.UNLOCKING)
}
}
private fun setVaultToNotUnlocking(userId: String) {
mutableVaultStateStateFlow.update {
it.copy(
unlockingVaultUserIds = it.unlockingVaultUserIds - userId,
)
val status = mutableVaultUnlockDataStateFlow.value.statusFor(userId)
if (status != VaultUnlockData.Status.UNLOCKING) return
mutableVaultUnlockDataStateFlow.update {
it.update(userId, null)
}
}

View File

@@ -1,13 +0,0 @@
package com.x8bit.bitwarden.data.vault.repository.model
/**
* General description of the vault across multiple users.
*
* @property unlockedVaultUserIds The user IDs for all users that currently have unlocked vaults.
* @property unlockedVaultUserIds The user IDs for all users that are actively unlocking their
* vaults.
*/
data class VaultState(
val unlockedVaultUserIds: Set<String>,
val unlockingVaultUserIds: Set<String>,
)

View File

@@ -0,0 +1,21 @@
package com.x8bit.bitwarden.data.vault.repository.model
/**
* The vault state for a given user ID.
*
* @property userId The user ID.
* @property status The lock status of the user's vault.
*/
data class VaultUnlockData(
val userId: String,
val status: Status,
) {
/**
* The lock status of a user's vault.
*/
enum class Status {
PENDING,
UNLOCKED,
UNLOCKING,
}
}

View File

@@ -0,0 +1,32 @@
package com.x8bit.bitwarden.data.vault.repository.util
import com.x8bit.bitwarden.data.vault.repository.model.VaultUnlockData
/**
* Get the vault unlock status for a [userId] from a list of [VaultUnlockData].
*/
fun List<VaultUnlockData>.statusFor(userId: String): VaultUnlockData.Status? =
firstOrNull { it.userId == userId }?.status
/**
* Update the vault unlock status for a [userId] in a list of [VaultUnlockData].
*/
fun List<VaultUnlockData>.update(
userId: String,
status: VaultUnlockData.Status?,
): List<VaultUnlockData> {
val updatedList = filter {
it.userId != userId
}
return if (status == null) {
updatedList
} else {
updatedList
.plus(
VaultUnlockData(
userId = userId,
status = status,
),
)
}
}