mirror of
https://github.com/bitwarden/android.git
synced 2025-02-17 04:19:54 +03:00
Add VaultLockManager (#550)
This commit is contained in:
parent
8c2e2f8af6
commit
8ff3207f7a
8 changed files with 1143 additions and 1000 deletions
|
@ -0,0 +1,58 @@
|
|||
package com.x8bit.bitwarden.data.vault.manager
|
||||
|
||||
import com.bitwarden.core.InitUserCryptoMethod
|
||||
import com.bitwarden.core.Kdf
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.VaultState
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.VaultUnlockResult
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
|
||||
/**
|
||||
* Manages the locking and unlocking of user vaults.
|
||||
*/
|
||||
interface VaultLockManager {
|
||||
/**
|
||||
* Flow that represents the current vault state.
|
||||
*/
|
||||
val vaultStateFlow: StateFlow<VaultState>
|
||||
|
||||
/**
|
||||
* Whether or not the vault is currently locked for the given [userId].
|
||||
*/
|
||||
fun isVaultUnlocked(userId: String): Boolean
|
||||
|
||||
/**
|
||||
* Whether or not the vault is currently unlocking for the given [userId].
|
||||
*/
|
||||
fun isVaultUnlocking(userId: String): Boolean
|
||||
|
||||
/**
|
||||
* Locks the vault for the user with the given [userId].
|
||||
*/
|
||||
fun lockVault(userId: String)
|
||||
|
||||
/**
|
||||
* Locks the vault for the user with the given [userId] only if necessary.
|
||||
*/
|
||||
fun lockVaultIfNecessary(userId: String)
|
||||
|
||||
/**
|
||||
* Locks the vault for the current user if currently unlocked.
|
||||
*/
|
||||
fun lockVaultForCurrentUser()
|
||||
|
||||
/**
|
||||
* Attempt to unlock the vault with the specified user information.
|
||||
*
|
||||
* Note that when [organizationKeys] is absent, no attempt will be made to unlock the vault
|
||||
* for organization data.
|
||||
*/
|
||||
@Suppress("LongParameterList")
|
||||
suspend fun unlockVault(
|
||||
userId: String,
|
||||
email: String,
|
||||
kdf: Kdf,
|
||||
privateKey: String,
|
||||
initUserCryptoMethod: InitUserCryptoMethod,
|
||||
organizationKeys: Map<String, String>?,
|
||||
): VaultUnlockResult
|
||||
}
|
|
@ -0,0 +1,149 @@
|
|||
package com.x8bit.bitwarden.data.vault.manager
|
||||
|
||||
import com.bitwarden.core.InitOrgCryptoRequest
|
||||
import com.bitwarden.core.InitUserCryptoMethod
|
||||
import com.bitwarden.core.InitUserCryptoRequest
|
||||
import com.bitwarden.core.Kdf
|
||||
import com.x8bit.bitwarden.data.auth.datasource.disk.AuthDiskSource
|
||||
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.VaultUnlockResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.util.toVaultUnlockResult
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import kotlinx.coroutines.flow.onCompletion
|
||||
import kotlinx.coroutines.flow.update
|
||||
|
||||
/**
|
||||
* Primary implementation [VaultLockManager].
|
||||
*/
|
||||
class VaultLockManagerImpl(
|
||||
private val authDiskSource: AuthDiskSource,
|
||||
private val vaultSdkSource: VaultSdkSource,
|
||||
) : VaultLockManager {
|
||||
private val activeUserId: String? get() = authDiskSource.userState?.activeUserId
|
||||
|
||||
private val mutableVaultStateStateFlow =
|
||||
MutableStateFlow(
|
||||
VaultState(
|
||||
unlockedVaultUserIds = emptySet(),
|
||||
unlockingVaultUserIds = emptySet(),
|
||||
),
|
||||
)
|
||||
|
||||
override val vaultStateFlow: StateFlow<VaultState>
|
||||
get() = mutableVaultStateStateFlow.asStateFlow()
|
||||
|
||||
override fun isVaultUnlocked(userId: String): Boolean =
|
||||
userId in mutableVaultStateStateFlow.value.unlockedVaultUserIds
|
||||
|
||||
override fun isVaultUnlocking(userId: String): Boolean =
|
||||
userId in mutableVaultStateStateFlow.value.unlockingVaultUserIds
|
||||
|
||||
override fun lockVault(userId: String) {
|
||||
setVaultToLocked(userId = userId)
|
||||
}
|
||||
|
||||
override fun lockVaultForCurrentUser() {
|
||||
activeUserId?.let {
|
||||
lockVaultIfNecessary(it)
|
||||
}
|
||||
}
|
||||
|
||||
override fun lockVaultIfNecessary(userId: String) {
|
||||
// TODO: Check for VaultTimeout.Never (BIT-1019)
|
||||
lockVault(userId = userId)
|
||||
}
|
||||
|
||||
override suspend fun unlockVault(
|
||||
userId: String,
|
||||
email: String,
|
||||
kdf: Kdf,
|
||||
privateKey: String,
|
||||
initUserCryptoMethod: InitUserCryptoMethod,
|
||||
organizationKeys: Map<String, String>?,
|
||||
): VaultUnlockResult =
|
||||
flow {
|
||||
setVaultToUnlocking(userId = userId)
|
||||
emit(
|
||||
vaultSdkSource
|
||||
.initializeCrypto(
|
||||
userId = userId,
|
||||
request = InitUserCryptoRequest(
|
||||
kdfParams = kdf,
|
||||
email = email,
|
||||
privateKey = privateKey,
|
||||
method = initUserCryptoMethod,
|
||||
),
|
||||
)
|
||||
.flatMap { result ->
|
||||
// Initialize the SDK for organizations if necessary
|
||||
if (organizationKeys != null &&
|
||||
result is InitializeCryptoResult.Success
|
||||
) {
|
||||
vaultSdkSource.initializeOrganizationCrypto(
|
||||
userId = userId,
|
||||
request = InitOrgCryptoRequest(
|
||||
organizationKeys = organizationKeys,
|
||||
),
|
||||
)
|
||||
} else {
|
||||
result.asSuccess()
|
||||
}
|
||||
}
|
||||
.fold(
|
||||
onFailure = { VaultUnlockResult.GenericError },
|
||||
onSuccess = { initializeCryptoResult ->
|
||||
initializeCryptoResult
|
||||
.toVaultUnlockResult()
|
||||
.also {
|
||||
if (it is VaultUnlockResult.Success) {
|
||||
setVaultToUnlocked(userId = userId)
|
||||
}
|
||||
}
|
||||
},
|
||||
),
|
||||
)
|
||||
}
|
||||
.onCompletion { setVaultToNotUnlocking(userId = userId) }
|
||||
.first()
|
||||
|
||||
private fun setVaultToUnlocked(userId: String) {
|
||||
mutableVaultStateStateFlow.update {
|
||||
it.copy(
|
||||
unlockedVaultUserIds = it.unlockedVaultUserIds + userId,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setVaultToLocked(userId: String) {
|
||||
vaultSdkSource.clearCrypto(userId = userId)
|
||||
mutableVaultStateStateFlow.update {
|
||||
it.copy(
|
||||
unlockedVaultUserIds = it.unlockedVaultUserIds - userId,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setVaultToUnlocking(userId: String) {
|
||||
mutableVaultStateStateFlow.update {
|
||||
it.copy(
|
||||
unlockingVaultUserIds = it.unlockingVaultUserIds + userId,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setVaultToNotUnlocking(userId: String) {
|
||||
mutableVaultStateStateFlow.update {
|
||||
it.copy(
|
||||
unlockingVaultUserIds = it.unlockingVaultUserIds - userId,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
package com.x8bit.bitwarden.data.vault.manager.di
|
||||
|
||||
import com.x8bit.bitwarden.data.auth.datasource.disk.AuthDiskSource
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.VaultSdkSource
|
||||
import com.x8bit.bitwarden.data.vault.manager.VaultLockManager
|
||||
import com.x8bit.bitwarden.data.vault.manager.VaultLockManagerImpl
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import javax.inject.Singleton
|
||||
|
||||
/**
|
||||
* Provides managers in the vault package.
|
||||
*/
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
object VaultManagerModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideVaultLockManager(
|
||||
authDiskSource: AuthDiskSource,
|
||||
vaultSdkSource: VaultSdkSource,
|
||||
): VaultLockManager =
|
||||
VaultLockManagerImpl(
|
||||
authDiskSource = authDiskSource,
|
||||
vaultSdkSource = vaultSdkSource,
|
||||
)
|
||||
}
|
|
@ -6,14 +6,14 @@ import com.bitwarden.core.FolderView
|
|||
import com.bitwarden.core.Kdf
|
||||
import com.bitwarden.core.SendView
|
||||
import com.x8bit.bitwarden.data.platform.repository.model.DataState
|
||||
import com.x8bit.bitwarden.data.vault.manager.VaultLockManager
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.CreateCipherResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.CreateSendResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.SendData
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.TotpCodeResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.UpdateCipherResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.UpdateSendResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.VaultData
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.VaultState
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.TotpCodeResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.VaultUnlockResult
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
|
@ -22,7 +22,7 @@ import kotlinx.coroutines.flow.StateFlow
|
|||
* Responsible for managing vault data inside the network layer.
|
||||
*/
|
||||
@Suppress("TooManyFunctions")
|
||||
interface VaultRepository {
|
||||
interface VaultRepository : VaultLockManager {
|
||||
|
||||
/**
|
||||
* Flow that represents the current vault data.
|
||||
|
@ -56,11 +56,6 @@ interface VaultRepository {
|
|||
*/
|
||||
val foldersStateFlow: StateFlow<DataState<List<FolderView>>>
|
||||
|
||||
/**
|
||||
* Flow that represents the current vault state.
|
||||
*/
|
||||
val vaultStateFlow: StateFlow<VaultState>
|
||||
|
||||
/**
|
||||
* Flow that represents the current send data.
|
||||
*/
|
||||
|
@ -98,16 +93,6 @@ interface VaultRepository {
|
|||
*/
|
||||
fun getVaultFolderStateFlow(folderId: String): StateFlow<DataState<FolderView?>>
|
||||
|
||||
/**
|
||||
* Locks the vault for the current user if currently unlocked.
|
||||
*/
|
||||
fun lockVaultForCurrentUser()
|
||||
|
||||
/**
|
||||
* Locks the vault for the user with the given [userId] if necessary.
|
||||
*/
|
||||
fun lockVaultIfNecessary(userId: String)
|
||||
|
||||
/**
|
||||
* Emits the totp code result flow to listeners.
|
||||
*/
|
||||
|
|
|
@ -5,7 +5,6 @@ import com.bitwarden.core.CollectionView
|
|||
import com.bitwarden.core.FolderView
|
||||
import com.bitwarden.core.InitOrgCryptoRequest
|
||||
import com.bitwarden.core.InitUserCryptoMethod
|
||||
import com.bitwarden.core.InitUserCryptoRequest
|
||||
import com.bitwarden.core.Kdf
|
||||
import com.bitwarden.core.SendView
|
||||
import com.x8bit.bitwarden.data.auth.datasource.disk.AuthDiskSource
|
||||
|
@ -19,7 +18,6 @@ import com.x8bit.bitwarden.data.platform.repository.util.combineDataStates
|
|||
import com.x8bit.bitwarden.data.platform.repository.util.map
|
||||
import com.x8bit.bitwarden.data.platform.repository.util.observeWhenSubscribedAndLoggedIn
|
||||
import com.x8bit.bitwarden.data.platform.repository.util.updateToPendingOrLoading
|
||||
import com.x8bit.bitwarden.data.platform.util.asSuccess
|
||||
import com.x8bit.bitwarden.data.platform.util.flatMap
|
||||
import com.x8bit.bitwarden.data.vault.datasource.disk.VaultDiskSource
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.SyncResponseJson
|
||||
|
@ -29,15 +27,14 @@ import com.x8bit.bitwarden.data.vault.datasource.network.service.CiphersService
|
|||
import com.x8bit.bitwarden.data.vault.datasource.network.service.SendsService
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.service.SyncService
|
||||
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.manager.VaultLockManager
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.CreateCipherResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.CreateSendResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.SendData
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.TotpCodeResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.UpdateCipherResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.UpdateSendResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.VaultData
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.VaultState
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.TotpCodeResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.VaultUnlockResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.util.toEncryptedNetworkCipher
|
||||
import com.x8bit.bitwarden.data.vault.repository.util.toEncryptedNetworkSend
|
||||
|
@ -46,7 +43,6 @@ import com.x8bit.bitwarden.data.vault.repository.util.toEncryptedSdkCollectionLi
|
|||
import com.x8bit.bitwarden.data.vault.repository.util.toEncryptedSdkFolderList
|
||||
import com.x8bit.bitwarden.data.vault.repository.util.toEncryptedSdkSend
|
||||
import com.x8bit.bitwarden.data.vault.repository.util.toEncryptedSdkSendList
|
||||
import com.x8bit.bitwarden.data.vault.repository.util.toVaultUnlockResult
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
@ -56,11 +52,8 @@ import kotlinx.coroutines.flow.StateFlow
|
|||
import kotlinx.coroutines.flow.asSharedFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.onCompletion
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.flow.onStart
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
|
@ -84,8 +77,10 @@ class VaultRepositoryImpl(
|
|||
private val vaultDiskSource: VaultDiskSource,
|
||||
private val vaultSdkSource: VaultSdkSource,
|
||||
private val authDiskSource: AuthDiskSource,
|
||||
private val vaultLockManager: VaultLockManager,
|
||||
dispatcherManager: DispatcherManager,
|
||||
) : VaultRepository {
|
||||
) : VaultRepository,
|
||||
VaultLockManager by vaultLockManager {
|
||||
|
||||
private val unconfinedScope = CoroutineScope(dispatcherManager.unconfined)
|
||||
private val ioScope = CoroutineScope(dispatcherManager.io)
|
||||
|
@ -96,14 +91,6 @@ class VaultRepositoryImpl(
|
|||
|
||||
private val mutableTotpCodeResultFlow = bufferedMutableSharedFlow<TotpCodeResult>()
|
||||
|
||||
private val mutableVaultStateStateFlow =
|
||||
MutableStateFlow(
|
||||
VaultState(
|
||||
unlockedVaultUserIds = emptySet(),
|
||||
unlockingVaultUserIds = emptySet(),
|
||||
),
|
||||
)
|
||||
|
||||
private val mutableSendDataStateFlow = MutableStateFlow<DataState<SendData>>(DataState.Loading)
|
||||
|
||||
private val mutableCiphersStateFlow =
|
||||
|
@ -151,9 +138,6 @@ class VaultRepositoryImpl(
|
|||
override val collectionsStateFlow: StateFlow<DataState<List<CollectionView>>>
|
||||
get() = mutableCollectionsStateFlow.asStateFlow()
|
||||
|
||||
override val vaultStateFlow: StateFlow<VaultState>
|
||||
get() = mutableVaultStateStateFlow.asStateFlow()
|
||||
|
||||
override val sendDataStateFlow: StateFlow<DataState<SendData>>
|
||||
get() = mutableSendDataStateFlow.asStateFlow()
|
||||
|
||||
|
@ -276,16 +260,6 @@ class VaultRepositoryImpl(
|
|||
initialValue = DataState.Loading,
|
||||
)
|
||||
|
||||
override fun lockVaultForCurrentUser() {
|
||||
authDiskSource.userState?.activeUserId?.let {
|
||||
lockVaultIfNecessary(it)
|
||||
}
|
||||
}
|
||||
|
||||
override fun lockVaultIfNecessary(userId: String) {
|
||||
setVaultToLocked(userId = userId)
|
||||
}
|
||||
|
||||
override fun emitTotpCodeResult(totpCodeResult: TotpCodeResult) {
|
||||
mutableTotpCodeResultFlow.tryEmit(totpCodeResult)
|
||||
}
|
||||
|
@ -320,7 +294,7 @@ class VaultRepositoryImpl(
|
|||
privateKey: String,
|
||||
organizationKeys: Map<String, String>?,
|
||||
): VaultUnlockResult =
|
||||
unlockVaultInternal(
|
||||
unlockVault(
|
||||
userId = userId,
|
||||
email = email,
|
||||
kdf = kdf,
|
||||
|
@ -446,46 +420,6 @@ class VaultRepositoryImpl(
|
|||
)
|
||||
}
|
||||
|
||||
// TODO: This is temporary. Eventually this needs to be based on the presence of various
|
||||
// user keys but this will likely require SDK updates to support this (BIT-1190).
|
||||
private fun setVaultToUnlocked(userId: String) {
|
||||
mutableVaultStateStateFlow.update {
|
||||
it.copy(
|
||||
unlockedVaultUserIds = it.unlockedVaultUserIds + userId,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: This is temporary. Eventually this needs to be based on the presence of various
|
||||
// user keys but this will likely require SDK updates to support this (BIT-1190).
|
||||
private fun setVaultToLocked(userId: String) {
|
||||
vaultSdkSource.clearCrypto(userId = userId)
|
||||
mutableVaultStateStateFlow.update {
|
||||
it.copy(
|
||||
unlockedVaultUserIds = it.unlockedVaultUserIds - userId,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setVaultToUnlocking(userId: String) {
|
||||
mutableVaultStateStateFlow.update {
|
||||
it.copy(
|
||||
unlockingVaultUserIds = it.unlockingVaultUserIds + userId,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setVaultToNotUnlocking(userId: String) {
|
||||
mutableVaultStateStateFlow.update {
|
||||
it.copy(
|
||||
unlockingVaultUserIds = it.unlockingVaultUserIds - userId,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun isVaultUnlocking(userId: String) =
|
||||
userId in mutableVaultStateStateFlow.value.unlockingVaultUserIds
|
||||
|
||||
private fun storeProfileData(
|
||||
syncResponse: SyncResponseJson,
|
||||
) {
|
||||
|
@ -527,7 +461,7 @@ class VaultRepositoryImpl(
|
|||
?: return VaultUnlockResult.InvalidStateError
|
||||
val organizationKeys = authDiskSource
|
||||
.getOrganizationKeys(userId = userId)
|
||||
return unlockVaultInternal(
|
||||
return unlockVault(
|
||||
userId = userId,
|
||||
email = account.profile.email,
|
||||
kdf = account.profile.toSdkParams(),
|
||||
|
@ -537,59 +471,6 @@ class VaultRepositoryImpl(
|
|||
)
|
||||
}
|
||||
|
||||
private suspend fun unlockVaultInternal(
|
||||
userId: String,
|
||||
email: String,
|
||||
kdf: Kdf,
|
||||
privateKey: String,
|
||||
initUserCryptoMethod: InitUserCryptoMethod,
|
||||
organizationKeys: Map<String, String>?,
|
||||
): VaultUnlockResult =
|
||||
flow {
|
||||
setVaultToUnlocking(userId = userId)
|
||||
emit(
|
||||
vaultSdkSource
|
||||
.initializeCrypto(
|
||||
userId = userId,
|
||||
request = InitUserCryptoRequest(
|
||||
kdfParams = kdf,
|
||||
email = email,
|
||||
privateKey = privateKey,
|
||||
method = initUserCryptoMethod,
|
||||
),
|
||||
)
|
||||
.flatMap { result ->
|
||||
// Initialize the SDK for organizations if necessary
|
||||
if (organizationKeys != null &&
|
||||
result is InitializeCryptoResult.Success
|
||||
) {
|
||||
vaultSdkSource.initializeOrganizationCrypto(
|
||||
userId = userId,
|
||||
request = InitOrgCryptoRequest(
|
||||
organizationKeys = organizationKeys,
|
||||
),
|
||||
)
|
||||
} else {
|
||||
result.asSuccess()
|
||||
}
|
||||
}
|
||||
.fold(
|
||||
onFailure = { VaultUnlockResult.GenericError },
|
||||
onSuccess = { initializeCryptoResult ->
|
||||
initializeCryptoResult
|
||||
.toVaultUnlockResult()
|
||||
.also {
|
||||
if (it is VaultUnlockResult.Success) {
|
||||
setVaultToUnlocked(userId = userId)
|
||||
}
|
||||
}
|
||||
},
|
||||
),
|
||||
)
|
||||
}
|
||||
.onCompletion { setVaultToNotUnlocking(userId = userId) }
|
||||
.first()
|
||||
|
||||
private suspend fun unlockVaultForOrganizationsIfNecessary(
|
||||
syncResponse: SyncResponseJson,
|
||||
) {
|
||||
|
|
|
@ -7,6 +7,7 @@ import com.x8bit.bitwarden.data.vault.datasource.network.service.CiphersService
|
|||
import com.x8bit.bitwarden.data.vault.datasource.network.service.SendsService
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.service.SyncService
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.VaultSdkSource
|
||||
import com.x8bit.bitwarden.data.vault.manager.VaultLockManager
|
||||
import com.x8bit.bitwarden.data.vault.repository.VaultRepository
|
||||
import com.x8bit.bitwarden.data.vault.repository.VaultRepositoryImpl
|
||||
import dagger.Module
|
||||
|
@ -31,6 +32,7 @@ object VaultRepositoryModule {
|
|||
vaultDiskSource: VaultDiskSource,
|
||||
vaultSdkSource: VaultSdkSource,
|
||||
authDiskSource: AuthDiskSource,
|
||||
vaultLockManager: VaultLockManager,
|
||||
dispatcherManager: DispatcherManager,
|
||||
): VaultRepository = VaultRepositoryImpl(
|
||||
syncService = syncService,
|
||||
|
@ -39,6 +41,7 @@ object VaultRepositoryModule {
|
|||
vaultDiskSource = vaultDiskSource,
|
||||
vaultSdkSource = vaultSdkSource,
|
||||
authDiskSource = authDiskSource,
|
||||
vaultLockManager = vaultLockManager,
|
||||
dispatcherManager = dispatcherManager,
|
||||
)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,631 @@
|
|||
package com.x8bit.bitwarden.data.vault.manager
|
||||
|
||||
import com.bitwarden.core.InitOrgCryptoRequest
|
||||
import com.bitwarden.core.InitUserCryptoMethod
|
||||
import com.bitwarden.core.InitUserCryptoRequest
|
||||
import com.x8bit.bitwarden.data.auth.datasource.disk.model.AccountJson
|
||||
import com.x8bit.bitwarden.data.auth.datasource.disk.model.UserStateJson
|
||||
import com.x8bit.bitwarden.data.auth.datasource.disk.util.FakeAuthDiskSource
|
||||
import com.x8bit.bitwarden.data.auth.repository.util.toSdkParams
|
||||
import com.x8bit.bitwarden.data.platform.util.asFailure
|
||||
import com.x8bit.bitwarden.data.platform.util.asSuccess
|
||||
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.VaultUnlockResult
|
||||
import io.mockk.awaits
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.coVerify
|
||||
import io.mockk.every
|
||||
import io.mockk.just
|
||||
import io.mockk.mockk
|
||||
import io.mockk.runs
|
||||
import io.mockk.verify
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Assertions.assertFalse
|
||||
import org.junit.jupiter.api.Assertions.assertTrue
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class VaultLockManagerTest {
|
||||
private val fakeAuthDiskSource = FakeAuthDiskSource()
|
||||
private val vaultSdkSource: VaultSdkSource = mockk {
|
||||
every { clearCrypto(userId = any()) } just runs
|
||||
}
|
||||
|
||||
private val vaultLockManager: VaultLockManager = VaultLockManagerImpl(
|
||||
authDiskSource = fakeAuthDiskSource,
|
||||
vaultSdkSource = vaultSdkSource,
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `isVaultUnlocked should return the correct value based on the vault lock state`() =
|
||||
runTest {
|
||||
val userId = "userId"
|
||||
assertFalse(vaultLockManager.isVaultUnlocked(userId = userId))
|
||||
|
||||
verifyUnlockedVault(userId = userId)
|
||||
|
||||
assertTrue(vaultLockManager.isVaultUnlocked(userId = userId))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `isVaultLocking should return the correct value based on the vault unlocking state`() =
|
||||
runTest {
|
||||
val userId = "userId"
|
||||
assertFalse(vaultLockManager.isVaultUnlocking(userId = userId))
|
||||
|
||||
val unlockingJob = async {
|
||||
verifyUnlockingVault(userId = userId)
|
||||
}
|
||||
this.testScheduler.advanceUntilIdle()
|
||||
|
||||
assertTrue(vaultLockManager.isVaultUnlocking(userId = userId))
|
||||
|
||||
unlockingJob.cancel()
|
||||
this.testScheduler.advanceUntilIdle()
|
||||
|
||||
assertFalse(vaultLockManager.isVaultUnlocking(userId = userId))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `lockVaultIfNecessary should lock the given account if it is currently unlocked`() =
|
||||
runTest {
|
||||
val userId = "userId"
|
||||
verifyUnlockedVault(userId = userId)
|
||||
|
||||
assertEquals(
|
||||
VaultState(
|
||||
unlockedVaultUserIds = setOf(userId),
|
||||
unlockingVaultUserIds = emptySet(),
|
||||
),
|
||||
vaultLockManager.vaultStateFlow.value,
|
||||
)
|
||||
|
||||
vaultLockManager.lockVaultIfNecessary(userId = userId)
|
||||
|
||||
assertEquals(
|
||||
VaultState(
|
||||
unlockedVaultUserIds = emptySet(),
|
||||
unlockingVaultUserIds = emptySet(),
|
||||
),
|
||||
vaultLockManager.vaultStateFlow.value,
|
||||
)
|
||||
verify { vaultSdkSource.clearCrypto(userId = userId) }
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `lockVaultForCurrentUser should lock the vault for the current user if it is currently unlocked`() =
|
||||
runTest {
|
||||
fakeAuthDiskSource.userState = MOCK_USER_STATE
|
||||
val userId = "mockId-1"
|
||||
verifyUnlockedVault(userId = userId)
|
||||
|
||||
assertEquals(
|
||||
VaultState(
|
||||
unlockedVaultUserIds = setOf(userId),
|
||||
unlockingVaultUserIds = emptySet(),
|
||||
),
|
||||
vaultLockManager.vaultStateFlow.value,
|
||||
)
|
||||
|
||||
vaultLockManager.lockVaultForCurrentUser()
|
||||
|
||||
assertEquals(
|
||||
VaultState(
|
||||
unlockedVaultUserIds = emptySet(),
|
||||
unlockingVaultUserIds = emptySet(),
|
||||
),
|
||||
vaultLockManager.vaultStateFlow.value,
|
||||
)
|
||||
verify { vaultSdkSource.clearCrypto(userId = userId) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `unlockVault with initializeCrypto success should return Success`() = runTest {
|
||||
val userId = "userId"
|
||||
val kdf = MOCK_PROFILE.toSdkParams()
|
||||
val email = MOCK_PROFILE.email
|
||||
val masterPassword = "drowssap"
|
||||
val userKey = "12345"
|
||||
val privateKey = "54321"
|
||||
val organizationKeys = mapOf("orgId1" to "orgKey1")
|
||||
coEvery {
|
||||
vaultSdkSource.initializeCrypto(
|
||||
userId = userId,
|
||||
request = InitUserCryptoRequest(
|
||||
kdfParams = kdf,
|
||||
email = email,
|
||||
privateKey = privateKey,
|
||||
method = InitUserCryptoMethod.Password(
|
||||
password = masterPassword,
|
||||
userKey = userKey,
|
||||
),
|
||||
),
|
||||
)
|
||||
} returns InitializeCryptoResult.Success.asSuccess()
|
||||
coEvery {
|
||||
vaultSdkSource.initializeOrganizationCrypto(
|
||||
userId = userId,
|
||||
request = InitOrgCryptoRequest(organizationKeys = organizationKeys),
|
||||
)
|
||||
} returns InitializeCryptoResult.Success.asSuccess()
|
||||
assertEquals(
|
||||
VaultState(
|
||||
unlockedVaultUserIds = emptySet(),
|
||||
unlockingVaultUserIds = emptySet(),
|
||||
),
|
||||
vaultLockManager.vaultStateFlow.value,
|
||||
)
|
||||
|
||||
val result = vaultLockManager.unlockVault(
|
||||
userId = userId,
|
||||
kdf = kdf,
|
||||
email = email,
|
||||
initUserCryptoMethod = InitUserCryptoMethod.Password(
|
||||
password = masterPassword,
|
||||
userKey = userKey,
|
||||
),
|
||||
privateKey = privateKey,
|
||||
organizationKeys = organizationKeys,
|
||||
)
|
||||
|
||||
assertEquals(VaultUnlockResult.Success, result)
|
||||
assertEquals(
|
||||
VaultState(
|
||||
unlockedVaultUserIds = setOf(userId),
|
||||
unlockingVaultUserIds = emptySet(),
|
||||
),
|
||||
vaultLockManager.vaultStateFlow.value,
|
||||
)
|
||||
coVerify(exactly = 1) {
|
||||
vaultSdkSource.initializeCrypto(
|
||||
userId = userId,
|
||||
request = InitUserCryptoRequest(
|
||||
kdfParams = kdf,
|
||||
email = email,
|
||||
privateKey = privateKey,
|
||||
method = InitUserCryptoMethod.Password(
|
||||
password = masterPassword,
|
||||
userKey = userKey,
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
coVerify(exactly = 1) {
|
||||
vaultSdkSource.initializeOrganizationCrypto(
|
||||
userId = userId,
|
||||
request = InitOrgCryptoRequest(organizationKeys = organizationKeys),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `unlockVault with initializeCrypto authentication failure for users should return AuthenticationError`() =
|
||||
runTest {
|
||||
val userId = "userId"
|
||||
val kdf = MOCK_PROFILE.toSdkParams()
|
||||
val email = MOCK_PROFILE.email
|
||||
val masterPassword = "drowssap"
|
||||
val userKey = "12345"
|
||||
val privateKey = "54321"
|
||||
val organizationKeys = mapOf("orgId1" to "orgKey1")
|
||||
coEvery {
|
||||
vaultSdkSource.initializeCrypto(
|
||||
userId = userId,
|
||||
request = InitUserCryptoRequest(
|
||||
kdfParams = kdf,
|
||||
email = email,
|
||||
privateKey = privateKey,
|
||||
method = InitUserCryptoMethod.Password(
|
||||
password = masterPassword,
|
||||
userKey = userKey,
|
||||
),
|
||||
),
|
||||
)
|
||||
} returns InitializeCryptoResult.AuthenticationError.asSuccess()
|
||||
|
||||
assertEquals(
|
||||
VaultState(
|
||||
unlockedVaultUserIds = emptySet(),
|
||||
unlockingVaultUserIds = emptySet(),
|
||||
),
|
||||
vaultLockManager.vaultStateFlow.value,
|
||||
)
|
||||
val result = vaultLockManager.unlockVault(
|
||||
userId = userId,
|
||||
kdf = kdf,
|
||||
email = email,
|
||||
initUserCryptoMethod = InitUserCryptoMethod.Password(
|
||||
password = masterPassword,
|
||||
userKey = userKey,
|
||||
),
|
||||
privateKey = privateKey,
|
||||
organizationKeys = organizationKeys,
|
||||
)
|
||||
|
||||
assertEquals(VaultUnlockResult.AuthenticationError, result)
|
||||
assertEquals(
|
||||
VaultState(
|
||||
unlockedVaultUserIds = emptySet(),
|
||||
unlockingVaultUserIds = emptySet(),
|
||||
),
|
||||
vaultLockManager.vaultStateFlow.value,
|
||||
)
|
||||
coVerify(exactly = 1) {
|
||||
vaultSdkSource.initializeCrypto(
|
||||
userId = userId,
|
||||
request = InitUserCryptoRequest(
|
||||
kdfParams = kdf,
|
||||
email = email,
|
||||
privateKey = privateKey,
|
||||
method = InitUserCryptoMethod.Password(
|
||||
password = masterPassword,
|
||||
userKey = userKey,
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `unlockVault with initializeCrypto authentication failure for orgs should return AuthenticationError`() =
|
||||
runTest {
|
||||
val userId = "userId"
|
||||
val kdf = MOCK_PROFILE.toSdkParams()
|
||||
val email = MOCK_PROFILE.email
|
||||
val masterPassword = "drowssap"
|
||||
val userKey = "12345"
|
||||
val privateKey = "54321"
|
||||
val organizationKeys = mapOf("orgId1" to "orgKey1")
|
||||
coEvery {
|
||||
vaultSdkSource.initializeCrypto(
|
||||
userId = userId,
|
||||
request = InitUserCryptoRequest(
|
||||
kdfParams = kdf,
|
||||
email = email,
|
||||
privateKey = privateKey,
|
||||
method = InitUserCryptoMethod.Password(
|
||||
password = masterPassword,
|
||||
userKey = userKey,
|
||||
),
|
||||
),
|
||||
)
|
||||
} returns InitializeCryptoResult.Success.asSuccess()
|
||||
coEvery {
|
||||
vaultSdkSource.initializeOrganizationCrypto(
|
||||
userId = userId,
|
||||
request = InitOrgCryptoRequest(organizationKeys = organizationKeys),
|
||||
)
|
||||
} returns InitializeCryptoResult.AuthenticationError.asSuccess()
|
||||
|
||||
assertEquals(
|
||||
VaultState(
|
||||
unlockedVaultUserIds = emptySet(),
|
||||
unlockingVaultUserIds = emptySet(),
|
||||
),
|
||||
vaultLockManager.vaultStateFlow.value,
|
||||
)
|
||||
|
||||
val result = vaultLockManager.unlockVault(
|
||||
userId = userId,
|
||||
kdf = kdf,
|
||||
email = email,
|
||||
initUserCryptoMethod = InitUserCryptoMethod.Password(
|
||||
password = masterPassword,
|
||||
userKey = userKey,
|
||||
),
|
||||
privateKey = privateKey,
|
||||
organizationKeys = organizationKeys,
|
||||
)
|
||||
|
||||
assertEquals(VaultUnlockResult.AuthenticationError, result)
|
||||
assertEquals(
|
||||
VaultState(
|
||||
unlockedVaultUserIds = emptySet(),
|
||||
unlockingVaultUserIds = emptySet(),
|
||||
),
|
||||
vaultLockManager.vaultStateFlow.value,
|
||||
)
|
||||
coVerify(exactly = 1) {
|
||||
vaultSdkSource.initializeCrypto(
|
||||
userId = userId,
|
||||
request = InitUserCryptoRequest(
|
||||
kdfParams = kdf,
|
||||
email = email,
|
||||
privateKey = privateKey,
|
||||
method = InitUserCryptoMethod.Password(
|
||||
password = masterPassword,
|
||||
userKey = userKey,
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
coVerify(exactly = 1) {
|
||||
vaultSdkSource.initializeOrganizationCrypto(
|
||||
userId = userId,
|
||||
request = InitOrgCryptoRequest(organizationKeys = organizationKeys),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `unlockVault with initializeCrypto failure for users should return GenericError`() =
|
||||
runTest {
|
||||
val userId = "userId"
|
||||
val kdf = MOCK_PROFILE.toSdkParams()
|
||||
val email = MOCK_PROFILE.email
|
||||
val masterPassword = "drowssap"
|
||||
val userKey = "12345"
|
||||
val privateKey = "54321"
|
||||
val organizationKeys = mapOf("orgId1" to "orgKey1")
|
||||
coEvery {
|
||||
vaultSdkSource.initializeCrypto(
|
||||
userId = userId,
|
||||
request = InitUserCryptoRequest(
|
||||
kdfParams = kdf,
|
||||
email = email,
|
||||
privateKey = privateKey,
|
||||
method = InitUserCryptoMethod.Password(
|
||||
password = masterPassword,
|
||||
userKey = userKey,
|
||||
),
|
||||
),
|
||||
)
|
||||
} returns Throwable("Fail").asFailure()
|
||||
assertEquals(
|
||||
VaultState(
|
||||
unlockedVaultUserIds = emptySet(),
|
||||
unlockingVaultUserIds = emptySet(),
|
||||
),
|
||||
vaultLockManager.vaultStateFlow.value,
|
||||
)
|
||||
|
||||
val result = vaultLockManager.unlockVault(
|
||||
userId = userId,
|
||||
kdf = kdf,
|
||||
email = email,
|
||||
initUserCryptoMethod = InitUserCryptoMethod.Password(
|
||||
password = masterPassword,
|
||||
userKey = userKey,
|
||||
),
|
||||
privateKey = privateKey,
|
||||
organizationKeys = organizationKeys,
|
||||
)
|
||||
|
||||
assertEquals(VaultUnlockResult.GenericError, result)
|
||||
assertEquals(
|
||||
VaultState(
|
||||
unlockedVaultUserIds = emptySet(),
|
||||
unlockingVaultUserIds = emptySet(),
|
||||
),
|
||||
vaultLockManager.vaultStateFlow.value,
|
||||
)
|
||||
coVerify(exactly = 1) {
|
||||
vaultSdkSource.initializeCrypto(
|
||||
userId = userId,
|
||||
request = InitUserCryptoRequest(
|
||||
kdfParams = kdf,
|
||||
email = email,
|
||||
privateKey = privateKey,
|
||||
method = InitUserCryptoMethod.Password(
|
||||
password = masterPassword,
|
||||
userKey = userKey,
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `unlockVault with initializeCrypto failure for orgs should return GenericError`() =
|
||||
runTest {
|
||||
val userId = "userId"
|
||||
val kdf = MOCK_PROFILE.toSdkParams()
|
||||
val email = MOCK_PROFILE.email
|
||||
val masterPassword = "drowssap"
|
||||
val userKey = "12345"
|
||||
val privateKey = "54321"
|
||||
val organizationKeys = mapOf("orgId1" to "orgKey1")
|
||||
coEvery {
|
||||
vaultSdkSource.initializeCrypto(
|
||||
userId = userId,
|
||||
request = InitUserCryptoRequest(
|
||||
kdfParams = kdf,
|
||||
email = email,
|
||||
privateKey = privateKey,
|
||||
method = InitUserCryptoMethod.Password(
|
||||
password = masterPassword,
|
||||
userKey = userKey,
|
||||
),
|
||||
),
|
||||
)
|
||||
} returns InitializeCryptoResult.Success.asSuccess()
|
||||
coEvery {
|
||||
vaultSdkSource.initializeOrganizationCrypto(
|
||||
userId = userId,
|
||||
request = InitOrgCryptoRequest(organizationKeys = organizationKeys),
|
||||
)
|
||||
} returns Throwable("Fail").asFailure()
|
||||
assertEquals(
|
||||
VaultState(
|
||||
unlockedVaultUserIds = emptySet(),
|
||||
unlockingVaultUserIds = emptySet(),
|
||||
),
|
||||
vaultLockManager.vaultStateFlow.value,
|
||||
)
|
||||
|
||||
val result = vaultLockManager.unlockVault(
|
||||
userId = userId,
|
||||
kdf = kdf,
|
||||
email = email,
|
||||
initUserCryptoMethod = InitUserCryptoMethod.Password(
|
||||
password = masterPassword,
|
||||
userKey = userKey,
|
||||
),
|
||||
privateKey = privateKey,
|
||||
organizationKeys = organizationKeys,
|
||||
)
|
||||
|
||||
assertEquals(VaultUnlockResult.GenericError, result)
|
||||
assertEquals(
|
||||
VaultState(
|
||||
unlockedVaultUserIds = emptySet(),
|
||||
unlockingVaultUserIds = emptySet(),
|
||||
),
|
||||
vaultLockManager.vaultStateFlow.value,
|
||||
)
|
||||
coVerify(exactly = 1) {
|
||||
vaultSdkSource.initializeCrypto(
|
||||
userId = userId,
|
||||
request = InitUserCryptoRequest(
|
||||
kdfParams = kdf,
|
||||
email = email,
|
||||
privateKey = privateKey,
|
||||
method = InitUserCryptoMethod.Password(
|
||||
password = masterPassword,
|
||||
userKey = userKey,
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
coVerify(exactly = 1) {
|
||||
vaultSdkSource.initializeOrganizationCrypto(
|
||||
userId = userId,
|
||||
request = InitOrgCryptoRequest(organizationKeys = organizationKeys),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to ensures that the vault for the user with the given [userId] is actively unlocking.
|
||||
* Note that this call will actively hang.
|
||||
*/
|
||||
private suspend fun verifyUnlockingVault(userId: String) {
|
||||
val kdf = MOCK_PROFILE.toSdkParams()
|
||||
val email = MOCK_PROFILE.email
|
||||
val masterPassword = "drowssap"
|
||||
val userKey = "12345"
|
||||
val privateKey = "54321"
|
||||
val organizationKeys = null
|
||||
coEvery {
|
||||
vaultSdkSource.initializeCrypto(
|
||||
userId = userId,
|
||||
request = InitUserCryptoRequest(
|
||||
kdfParams = kdf,
|
||||
email = email,
|
||||
privateKey = privateKey,
|
||||
method = InitUserCryptoMethod.Password(
|
||||
password = masterPassword,
|
||||
userKey = userKey,
|
||||
),
|
||||
),
|
||||
)
|
||||
} just awaits
|
||||
|
||||
vaultLockManager.unlockVault(
|
||||
userId = userId,
|
||||
kdf = kdf,
|
||||
email = email,
|
||||
privateKey = privateKey,
|
||||
initUserCryptoMethod = InitUserCryptoMethod.Password(
|
||||
password = masterPassword,
|
||||
userKey = userKey,
|
||||
),
|
||||
organizationKeys = organizationKeys,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to ensures that the vault for the user with the given [userId] is unlocked.
|
||||
*/
|
||||
private suspend fun verifyUnlockedVault(userId: String) {
|
||||
val kdf = MOCK_PROFILE.toSdkParams()
|
||||
val email = MOCK_PROFILE.email
|
||||
val masterPassword = "drowssap"
|
||||
val userKey = "12345"
|
||||
val privateKey = "54321"
|
||||
val organizationKeys = null
|
||||
coEvery {
|
||||
vaultSdkSource.initializeCrypto(
|
||||
userId = userId,
|
||||
request = InitUserCryptoRequest(
|
||||
kdfParams = kdf,
|
||||
email = email,
|
||||
privateKey = privateKey,
|
||||
method = InitUserCryptoMethod.Password(
|
||||
password = masterPassword,
|
||||
userKey = userKey,
|
||||
),
|
||||
),
|
||||
)
|
||||
} returns InitializeCryptoResult.Success.asSuccess()
|
||||
|
||||
val result = vaultLockManager.unlockVault(
|
||||
userId = userId,
|
||||
kdf = kdf,
|
||||
email = email,
|
||||
privateKey = privateKey,
|
||||
initUserCryptoMethod = InitUserCryptoMethod.Password(
|
||||
password = masterPassword,
|
||||
userKey = userKey,
|
||||
),
|
||||
organizationKeys = organizationKeys,
|
||||
)
|
||||
|
||||
assertEquals(VaultUnlockResult.Success, result)
|
||||
coVerify(exactly = 1) {
|
||||
vaultSdkSource.initializeCrypto(
|
||||
userId = userId,
|
||||
request = InitUserCryptoRequest(
|
||||
kdfParams = kdf,
|
||||
email = email,
|
||||
privateKey = privateKey,
|
||||
method = InitUserCryptoMethod.Password(
|
||||
password = masterPassword,
|
||||
userKey = userKey,
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val MOCK_PROFILE = AccountJson.Profile(
|
||||
userId = "mockId-1",
|
||||
email = "email",
|
||||
isEmailVerified = true,
|
||||
name = null,
|
||||
stamp = null,
|
||||
organizationId = null,
|
||||
avatarColorHex = null,
|
||||
hasPremium = false,
|
||||
forcePasswordResetReason = null,
|
||||
kdfType = null,
|
||||
kdfIterations = null,
|
||||
kdfMemory = null,
|
||||
kdfParallelism = null,
|
||||
userDecryptionOptions = null,
|
||||
)
|
||||
|
||||
private val MOCK_ACCOUNT = AccountJson(
|
||||
profile = MOCK_PROFILE,
|
||||
tokens = AccountJson.Tokens(
|
||||
accessToken = "accessToken",
|
||||
refreshToken = "refreshToken",
|
||||
),
|
||||
settings = AccountJson.Settings(
|
||||
environmentUrlData = null,
|
||||
),
|
||||
)
|
||||
|
||||
private val MOCK_USER_STATE = UserStateJson(
|
||||
activeUserId = "mockId-1",
|
||||
accounts = mapOf(
|
||||
"mockId-1" to MOCK_ACCOUNT,
|
||||
),
|
||||
)
|
File diff suppressed because it is too large
Load diff
Loading…
Add table
Reference in a new issue