mirror of
https://github.com/bitwarden/android.git
synced 2025-12-10 00:06:22 -06:00
[PM-25664] Add CredentialExchangeImportManager for CXF payload import (#5872)
This commit is contained in:
parent
766e6b1bb9
commit
f22f4399be
@ -224,6 +224,7 @@ dependencies {
|
||||
|
||||
implementation(project(":annotation"))
|
||||
implementation(project(":core"))
|
||||
implementation(project(":cxf"))
|
||||
implementation(project(":data"))
|
||||
implementation(project(":network"))
|
||||
implementation(project(":ui"))
|
||||
@ -245,6 +246,8 @@ dependencies {
|
||||
implementation(libs.androidx.compose.ui.graphics)
|
||||
implementation(libs.androidx.compose.ui.tooling.preview)
|
||||
implementation(libs.androidx.core.ktx)
|
||||
implementation(libs.androidx.credentials)
|
||||
implementation(libs.androidx.credentials.providerevents)
|
||||
implementation(libs.androidx.hilt.navigation.compose)
|
||||
implementation(libs.androidx.lifecycle.process)
|
||||
implementation(libs.androidx.lifecycle.runtime.compose)
|
||||
@ -258,7 +261,6 @@ dependencies {
|
||||
implementation(libs.androidx.work.runtime.ktx)
|
||||
implementation(libs.bitwarden.sdk)
|
||||
implementation(libs.bumptech.glide)
|
||||
implementation(libs.androidx.credentials)
|
||||
implementation(libs.google.hilt.android)
|
||||
ksp(libs.google.hilt.compiler)
|
||||
implementation(libs.kotlinx.collections.immutable)
|
||||
|
||||
@ -0,0 +1,20 @@
|
||||
package com.x8bit.bitwarden.data.vault.manager
|
||||
|
||||
import com.x8bit.bitwarden.data.vault.manager.model.ImportCxfPayloadResult
|
||||
|
||||
/**
|
||||
* Manages the import process for Credential Exchange Format (CXF) payloads.
|
||||
*
|
||||
* This interface provides a contract for importing credential data from a standardized
|
||||
* CXF string, associating it with a specific user. It handles the parsing, decryption,
|
||||
* and storage of the credentials contained within the payload.
|
||||
*/
|
||||
interface CredentialExchangeImportManager {
|
||||
|
||||
/**
|
||||
* Attempt to import a CXF payload.
|
||||
*
|
||||
* @param payload The CXF payload to import.
|
||||
*/
|
||||
suspend fun importCxfPayload(userId: String, payload: String): ImportCxfPayloadResult
|
||||
}
|
||||
@ -0,0 +1,64 @@
|
||||
package com.x8bit.bitwarden.data.vault.manager
|
||||
|
||||
import androidx.credentials.providerevents.exception.ImportCredentialsUnknownErrorException
|
||||
import com.bitwarden.core.data.util.asFailure
|
||||
import com.bitwarden.core.data.util.asSuccess
|
||||
import com.bitwarden.core.data.util.flatMap
|
||||
import com.bitwarden.network.model.ImportCiphersJsonRequest
|
||||
import com.bitwarden.network.model.ImportCiphersResponseJson
|
||||
import com.bitwarden.network.service.CiphersService
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.VaultSdkSource
|
||||
import com.x8bit.bitwarden.data.vault.manager.model.ImportCxfPayloadResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.util.toEncryptedNetworkCipher
|
||||
|
||||
/**
|
||||
* Default implementation of [CredentialExchangeImportManager].
|
||||
*/
|
||||
class CredentialExchangeImportManagerImpl(
|
||||
private val vaultSdkSource: VaultSdkSource,
|
||||
private val ciphersService: CiphersService,
|
||||
) : CredentialExchangeImportManager {
|
||||
|
||||
override suspend fun importCxfPayload(
|
||||
userId: String,
|
||||
payload: String,
|
||||
): ImportCxfPayloadResult = vaultSdkSource
|
||||
.importCxf(
|
||||
userId = userId,
|
||||
payload = payload,
|
||||
)
|
||||
.flatMap { cipherList ->
|
||||
if (cipherList.isEmpty()) {
|
||||
// If no ciphers were returned, we can skip the remaining steps and return the
|
||||
// appropriate result.
|
||||
return ImportCxfPayloadResult.NoItems
|
||||
}
|
||||
ciphersService.importCiphers(
|
||||
request = ImportCiphersJsonRequest(
|
||||
ciphers = cipherList.map {
|
||||
it.toEncryptedNetworkCipher(
|
||||
encryptedFor = userId,
|
||||
)
|
||||
},
|
||||
folders = emptyList(),
|
||||
folderRelationships = emptyMap(),
|
||||
),
|
||||
)
|
||||
}
|
||||
.flatMap { importCiphersResponseJson ->
|
||||
when (importCiphersResponseJson) {
|
||||
is ImportCiphersResponseJson.Invalid -> {
|
||||
ImportCredentialsUnknownErrorException().asFailure()
|
||||
}
|
||||
|
||||
ImportCiphersResponseJson.Success -> {
|
||||
ImportCxfPayloadResult.Success
|
||||
.asSuccess()
|
||||
}
|
||||
}
|
||||
}
|
||||
.fold(
|
||||
onSuccess = { it },
|
||||
onFailure = { ImportCxfPayloadResult.Error(error = it) },
|
||||
)
|
||||
}
|
||||
@ -18,6 +18,8 @@ import com.x8bit.bitwarden.data.vault.datasource.disk.VaultDiskSource
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.VaultSdkSource
|
||||
import com.x8bit.bitwarden.data.vault.manager.CipherManager
|
||||
import com.x8bit.bitwarden.data.vault.manager.CipherManagerImpl
|
||||
import com.x8bit.bitwarden.data.vault.manager.CredentialExchangeImportManager
|
||||
import com.x8bit.bitwarden.data.vault.manager.CredentialExchangeImportManagerImpl
|
||||
import com.x8bit.bitwarden.data.vault.manager.FileManager
|
||||
import com.x8bit.bitwarden.data.vault.manager.FileManagerImpl
|
||||
import com.x8bit.bitwarden.data.vault.manager.TotpCodeManager
|
||||
@ -134,4 +136,14 @@ object VaultManagerModule {
|
||||
userLogoutManager = userLogoutManager,
|
||||
clock = clock,
|
||||
)
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideCredentialExchangeImportManager(
|
||||
vaultSdkSource: VaultSdkSource,
|
||||
ciphersService: CiphersService,
|
||||
): CredentialExchangeImportManager = CredentialExchangeImportManagerImpl(
|
||||
vaultSdkSource = vaultSdkSource,
|
||||
ciphersService = ciphersService,
|
||||
)
|
||||
}
|
||||
|
||||
@ -1,6 +1,4 @@
|
||||
package com.x8bit.bitwarden.data.vault.repository.model
|
||||
|
||||
import com.bitwarden.vault.Cipher
|
||||
package com.x8bit.bitwarden.data.vault.manager.model
|
||||
|
||||
/**
|
||||
* Models result of the vault data being imported from a CXF payload.
|
||||
@ -10,7 +8,12 @@ sealed class ImportCxfPayloadResult {
|
||||
/**
|
||||
* The vault data has been successfully imported.
|
||||
*/
|
||||
data class Success(val ciphers: List<Cipher>) : ImportCxfPayloadResult()
|
||||
data object Success : ImportCxfPayloadResult()
|
||||
|
||||
/**
|
||||
* There are no items to import.
|
||||
*/
|
||||
data object NoItems : ImportCxfPayloadResult()
|
||||
|
||||
/**
|
||||
* There was an error importing the vault data.
|
||||
@ -25,7 +25,7 @@ import com.x8bit.bitwarden.data.vault.repository.model.DeleteSendResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.DomainsData
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.ExportVaultDataResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.GenerateTotpResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.ImportCxfPayloadResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.ImportCredentialsResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.RemovePasswordSendResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.SendData
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.TotpCodeResult
|
||||
@ -264,7 +264,7 @@ interface VaultRepository : CipherManager, VaultLockManager {
|
||||
*
|
||||
* @param payload The CXF payload to import.
|
||||
*/
|
||||
suspend fun importCxfPayload(payload: String): ImportCxfPayloadResult
|
||||
suspend fun importCxfPayload(payload: String): ImportCredentialsResult
|
||||
|
||||
/**
|
||||
* Attempt to export the vault data to a CXF file.
|
||||
|
||||
@ -55,11 +55,13 @@ import com.x8bit.bitwarden.data.platform.repository.util.observeWhenSubscribedAn
|
||||
import com.x8bit.bitwarden.data.vault.datasource.disk.VaultDiskSource
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.VaultSdkSource
|
||||
import com.x8bit.bitwarden.data.vault.manager.CipherManager
|
||||
import com.x8bit.bitwarden.data.vault.manager.CredentialExchangeImportManager
|
||||
import com.x8bit.bitwarden.data.vault.manager.FileManager
|
||||
import com.x8bit.bitwarden.data.vault.manager.TotpCodeManager
|
||||
import com.x8bit.bitwarden.data.vault.manager.VaultLockManager
|
||||
import com.x8bit.bitwarden.data.vault.manager.VaultSyncManager
|
||||
import com.x8bit.bitwarden.data.vault.manager.model.GetCipherResult
|
||||
import com.x8bit.bitwarden.data.vault.manager.model.ImportCxfPayloadResult
|
||||
import com.x8bit.bitwarden.data.vault.manager.model.SyncVaultDataResult
|
||||
import com.x8bit.bitwarden.data.vault.manager.model.VerificationCodeItem
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.CreateFolderResult
|
||||
@ -69,7 +71,7 @@ import com.x8bit.bitwarden.data.vault.repository.model.DeleteSendResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.DomainsData
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.ExportVaultDataResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.GenerateTotpResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.ImportCxfPayloadResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.ImportCredentialsResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.RemovePasswordSendResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.SendData
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.TotpCodeResult
|
||||
@ -152,6 +154,7 @@ class VaultRepositoryImpl(
|
||||
dispatcherManager: DispatcherManager,
|
||||
private val reviewPromptManager: ReviewPromptManager,
|
||||
private val vaultSyncManager: VaultSyncManager,
|
||||
private val credentialExchangeImportManager: CredentialExchangeImportManager,
|
||||
) : VaultRepository,
|
||||
CipherManager by cipherManager,
|
||||
VaultLockManager by vaultLockManager {
|
||||
@ -968,18 +971,37 @@ class VaultRepositoryImpl(
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun importCxfPayload(payload: String): ImportCxfPayloadResult {
|
||||
override suspend fun importCxfPayload(
|
||||
payload: String,
|
||||
): ImportCredentialsResult {
|
||||
val userId = activeUserId
|
||||
?: return ImportCxfPayloadResult.Error(error = NoActiveUserException())
|
||||
return vaultSdkSource
|
||||
.importCxf(
|
||||
?: return ImportCredentialsResult.Error(error = NoActiveUserException())
|
||||
val importResult = credentialExchangeImportManager
|
||||
.importCxfPayload(
|
||||
userId = userId,
|
||||
payload = payload,
|
||||
)
|
||||
.fold(
|
||||
onSuccess = { ImportCxfPayloadResult.Success(it) },
|
||||
onFailure = { ImportCxfPayloadResult.Error(error = it) },
|
||||
)
|
||||
return when (importResult) {
|
||||
is ImportCxfPayloadResult.Error -> {
|
||||
ImportCredentialsResult.Error(error = importResult.error)
|
||||
}
|
||||
|
||||
ImportCxfPayloadResult.NoItems -> {
|
||||
ImportCredentialsResult.NoItems
|
||||
}
|
||||
|
||||
ImportCxfPayloadResult.Success -> {
|
||||
when (val syncResult = syncInternal(userId = userId, forced = true)) {
|
||||
is SyncVaultDataResult.Error -> {
|
||||
ImportCredentialsResult.SyncFailed(error = syncResult.throwable)
|
||||
}
|
||||
|
||||
is SyncVaultDataResult.Success -> {
|
||||
ImportCredentialsResult.Success
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun exportVaultDataToCxf(
|
||||
|
||||
@ -12,6 +12,7 @@ import com.x8bit.bitwarden.data.platform.manager.ReviewPromptManager
|
||||
import com.x8bit.bitwarden.data.vault.datasource.disk.VaultDiskSource
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.VaultSdkSource
|
||||
import com.x8bit.bitwarden.data.vault.manager.CipherManager
|
||||
import com.x8bit.bitwarden.data.vault.manager.CredentialExchangeImportManager
|
||||
import com.x8bit.bitwarden.data.vault.manager.FileManager
|
||||
import com.x8bit.bitwarden.data.vault.manager.TotpCodeManager
|
||||
import com.x8bit.bitwarden.data.vault.manager.VaultLockManager
|
||||
@ -52,6 +53,7 @@ object VaultRepositoryModule {
|
||||
clock: Clock,
|
||||
reviewPromptManager: ReviewPromptManager,
|
||||
vaultSyncManager: VaultSyncManager,
|
||||
credentialExchangeImportManager: CredentialExchangeImportManager,
|
||||
): VaultRepository = VaultRepositoryImpl(
|
||||
sendsService = sendsService,
|
||||
ciphersService = ciphersService,
|
||||
@ -70,5 +72,6 @@ object VaultRepositoryModule {
|
||||
clock = clock,
|
||||
reviewPromptManager = reviewPromptManager,
|
||||
vaultSyncManager = vaultSyncManager,
|
||||
credentialExchangeImportManager = credentialExchangeImportManager,
|
||||
)
|
||||
}
|
||||
|
||||
@ -0,0 +1,30 @@
|
||||
package com.x8bit.bitwarden.data.vault.repository.model
|
||||
|
||||
/**
|
||||
* Represents the result of importing credentials from a credential manager.
|
||||
*/
|
||||
sealed class ImportCredentialsResult {
|
||||
|
||||
/**
|
||||
* Indicates the vault data has been successfully imported.
|
||||
*/
|
||||
data object Success : ImportCredentialsResult()
|
||||
|
||||
/**
|
||||
* Indicates there are no items to import.
|
||||
*/
|
||||
data object NoItems : ImportCredentialsResult()
|
||||
|
||||
/**
|
||||
* Indicates the vault data has been successfully uploaded, but there was an error syncing the
|
||||
* vault data.
|
||||
*/
|
||||
data class SyncFailed(val error: Throwable) : ImportCredentialsResult()
|
||||
|
||||
/**
|
||||
* Indicates there was an error importing the vault data.
|
||||
*
|
||||
* @param error The error that occurred during import.
|
||||
*/
|
||||
data class Error(val error: Throwable) : ImportCredentialsResult()
|
||||
}
|
||||
@ -0,0 +1,186 @@
|
||||
package com.x8bit.bitwarden.data.vault.manager
|
||||
|
||||
import androidx.credentials.providerevents.exception.ImportCredentialsUnknownErrorException
|
||||
import com.bitwarden.core.data.util.asFailure
|
||||
import com.bitwarden.core.data.util.asSuccess
|
||||
import com.bitwarden.network.model.ImportCiphersJsonRequest
|
||||
import com.bitwarden.network.model.ImportCiphersResponseJson
|
||||
import com.bitwarden.network.service.CiphersService
|
||||
import com.bitwarden.vault.Cipher
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.VaultSdkSource
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockSdkCipher
|
||||
import com.x8bit.bitwarden.data.vault.manager.model.ImportCxfPayloadResult
|
||||
import io.mockk.awaits
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.coVerify
|
||||
import io.mockk.just
|
||||
import io.mockk.mockk
|
||||
import io.mockk.slot
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Assertions.assertTrue
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.assertNotNull
|
||||
|
||||
class CredentialExchangeImportManagerTest {
|
||||
|
||||
private val vaultSdkSource: VaultSdkSource = mockk()
|
||||
private val ciphersService: CiphersService = mockk(relaxed = true)
|
||||
|
||||
private val importManager = CredentialExchangeImportManagerImpl(
|
||||
vaultSdkSource = vaultSdkSource,
|
||||
ciphersService = ciphersService,
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `when vaultSdkSource importCxf fails, should return Error`() = runTest {
|
||||
val exception = RuntimeException("SDK import failed")
|
||||
coEvery {
|
||||
vaultSdkSource.importCxf(
|
||||
userId = DEFAULT_USER_ID,
|
||||
payload = DEFAULT_PAYLOAD,
|
||||
)
|
||||
} returns exception.asFailure()
|
||||
|
||||
coEvery {
|
||||
ciphersService.importCiphers(any())
|
||||
} just awaits
|
||||
|
||||
val result = importManager.importCxfPayload(DEFAULT_USER_ID, DEFAULT_PAYLOAD)
|
||||
|
||||
assertEquals(ImportCxfPayloadResult.Error(exception), result)
|
||||
coVerify(exactly = 1) {
|
||||
vaultSdkSource.importCxf(DEFAULT_USER_ID, DEFAULT_PAYLOAD)
|
||||
}
|
||||
coVerify(exactly = 0) {
|
||||
ciphersService.importCiphers(any())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `when ciphersService importCiphers fails, should return Error`() = runTest {
|
||||
coEvery {
|
||||
vaultSdkSource.importCxf(
|
||||
userId = DEFAULT_USER_ID,
|
||||
payload = DEFAULT_PAYLOAD,
|
||||
)
|
||||
} returns DEFAULT_CIPHER_LIST.asSuccess()
|
||||
|
||||
val exception = RuntimeException("Network import failed")
|
||||
val capturedRequest = slot<ImportCiphersJsonRequest>()
|
||||
coEvery {
|
||||
ciphersService.importCiphers(capture(capturedRequest))
|
||||
} returns exception.asFailure()
|
||||
|
||||
val result = importManager.importCxfPayload(DEFAULT_USER_ID, DEFAULT_PAYLOAD)
|
||||
|
||||
assertEquals(ImportCxfPayloadResult.Error(exception), result)
|
||||
assertEquals(1, capturedRequest.captured.ciphers.size)
|
||||
coVerify(exactly = 1) {
|
||||
vaultSdkSource.importCxf(DEFAULT_USER_ID, DEFAULT_PAYLOAD)
|
||||
ciphersService.importCiphers(any())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `when ciphersService importCiphers returns Invalid, should return Error`() = runTest {
|
||||
coEvery {
|
||||
vaultSdkSource.importCxf(
|
||||
userId = DEFAULT_USER_ID,
|
||||
payload = DEFAULT_PAYLOAD,
|
||||
)
|
||||
} returns DEFAULT_CIPHER_LIST.asSuccess()
|
||||
|
||||
coEvery {
|
||||
ciphersService.importCiphers(any())
|
||||
} returns ImportCiphersResponseJson
|
||||
.Invalid(validationErrors = emptyMap())
|
||||
.asSuccess()
|
||||
|
||||
val result = importManager.importCxfPayload(DEFAULT_USER_ID, DEFAULT_PAYLOAD)
|
||||
|
||||
val error = (result as? ImportCxfPayloadResult.Error)?.error
|
||||
assertNotNull(error)
|
||||
assertTrue(error is ImportCredentialsUnknownErrorException)
|
||||
coVerify(exactly = 1) {
|
||||
vaultSdkSource.importCxf(DEFAULT_USER_ID, DEFAULT_PAYLOAD)
|
||||
ciphersService.importCiphers(any())
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `when ciphersService importCiphers is Success should return Success`() =
|
||||
runTest {
|
||||
coEvery {
|
||||
vaultSdkSource.importCxf(
|
||||
userId = DEFAULT_USER_ID,
|
||||
payload = DEFAULT_PAYLOAD,
|
||||
)
|
||||
} returns DEFAULT_CIPHER_LIST.asSuccess()
|
||||
|
||||
coEvery {
|
||||
ciphersService.importCiphers(any())
|
||||
} returns ImportCiphersResponseJson.Success.asSuccess()
|
||||
|
||||
val result = importManager.importCxfPayload(DEFAULT_USER_ID, DEFAULT_PAYLOAD)
|
||||
|
||||
assertTrue(result is ImportCxfPayloadResult.Success)
|
||||
coVerify(exactly = 1) {
|
||||
vaultSdkSource.importCxf(DEFAULT_USER_ID, DEFAULT_PAYLOAD)
|
||||
ciphersService.importCiphers(any())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `when all steps succeed, should return Success`() = runTest {
|
||||
coEvery {
|
||||
vaultSdkSource.importCxf(
|
||||
userId = DEFAULT_USER_ID,
|
||||
payload = DEFAULT_PAYLOAD,
|
||||
)
|
||||
} returns DEFAULT_CIPHER_LIST.asSuccess()
|
||||
|
||||
coEvery {
|
||||
ciphersService.importCiphers(any())
|
||||
} returns ImportCiphersResponseJson.Success.asSuccess()
|
||||
|
||||
val result = importManager.importCxfPayload(DEFAULT_USER_ID, DEFAULT_PAYLOAD)
|
||||
|
||||
assertEquals(ImportCxfPayloadResult.Success, result)
|
||||
coVerify(exactly = 1) {
|
||||
vaultSdkSource.importCxf(DEFAULT_USER_ID, DEFAULT_PAYLOAD)
|
||||
ciphersService.importCiphers(any())
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `when importCxf returns empty cipher list, should skip importCiphers and sync and return NoItems`() =
|
||||
runTest {
|
||||
coEvery {
|
||||
vaultSdkSource.importCxf(
|
||||
userId = DEFAULT_USER_ID,
|
||||
payload = DEFAULT_PAYLOAD,
|
||||
)
|
||||
} returns emptyList<Cipher>().asSuccess()
|
||||
coEvery {
|
||||
ciphersService.importCiphers(any())
|
||||
} just awaits
|
||||
|
||||
val result = importManager.importCxfPayload(DEFAULT_USER_ID, DEFAULT_PAYLOAD)
|
||||
|
||||
assertEquals(ImportCxfPayloadResult.NoItems, result)
|
||||
coVerify(exactly = 1) {
|
||||
vaultSdkSource.importCxf(DEFAULT_USER_ID, DEFAULT_PAYLOAD)
|
||||
}
|
||||
coVerify(exactly = 0) {
|
||||
ciphersService.importCiphers(any())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private const val DEFAULT_USER_ID = "mockId-1"
|
||||
private const val DEFAULT_PAYLOAD = "mockPayload-1"
|
||||
private val DEFAULT_CIPHER: Cipher = createMockSdkCipher(number = 1)
|
||||
private val DEFAULT_CIPHER_LIST: List<Cipher> = listOf(DEFAULT_CIPHER)
|
||||
@ -74,10 +74,12 @@ import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockSdkFolder
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockSdkSend
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockSendView
|
||||
import com.x8bit.bitwarden.data.vault.manager.CipherManager
|
||||
import com.x8bit.bitwarden.data.vault.manager.CredentialExchangeImportManager
|
||||
import com.x8bit.bitwarden.data.vault.manager.FileManager
|
||||
import com.x8bit.bitwarden.data.vault.manager.TotpCodeManager
|
||||
import com.x8bit.bitwarden.data.vault.manager.VaultLockManager
|
||||
import com.x8bit.bitwarden.data.vault.manager.VaultSyncManager
|
||||
import com.x8bit.bitwarden.data.vault.manager.model.ImportCxfPayloadResult
|
||||
import com.x8bit.bitwarden.data.vault.manager.model.SyncVaultDataResult
|
||||
import com.x8bit.bitwarden.data.vault.manager.model.VerificationCodeItem
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.CreateFolderResult
|
||||
@ -87,7 +89,7 @@ import com.x8bit.bitwarden.data.vault.repository.model.DeleteSendResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.DomainsData
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.ExportVaultDataResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.GenerateTotpResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.ImportCxfPayloadResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.ImportCredentialsResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.RemovePasswordSendResult
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.SendData
|
||||
import com.x8bit.bitwarden.data.vault.repository.model.UpdateFolderResult
|
||||
@ -216,6 +218,7 @@ class VaultRepositoryTest {
|
||||
every { syncFolderUpsertFlow } returns mutableSyncFolderUpsertFlow
|
||||
}
|
||||
private val vaultSyncManager: VaultSyncManager = mockk()
|
||||
private val credentialExchangeImportManager: CredentialExchangeImportManager = mockk()
|
||||
|
||||
private val vaultRepository = VaultRepositoryImpl(
|
||||
sendsService = sendsService,
|
||||
@ -235,6 +238,7 @@ class VaultRepositoryTest {
|
||||
databaseSchemeManager = databaseSchemeManager,
|
||||
reviewPromptManager = reviewPromptManager,
|
||||
vaultSyncManager = vaultSyncManager,
|
||||
credentialExchangeImportManager = credentialExchangeImportManager,
|
||||
)
|
||||
|
||||
@BeforeEach
|
||||
@ -4080,44 +4084,113 @@ class VaultRepositoryTest {
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `importCxfPayload should return success result`() = runTest {
|
||||
val userId = "mockId-1"
|
||||
val payload = "payload"
|
||||
val ciphers = listOf(createMockSdkCipher(number = 1))
|
||||
fakeAuthDiskSource.userState = MOCK_USER_STATE
|
||||
fun `importCxfPayload should return success result when payload is successfully imported and vault sync is successful`() =
|
||||
runTest {
|
||||
val userId = "mockId-1"
|
||||
val payload = "payload"
|
||||
fakeAuthDiskSource.userState = MOCK_USER_STATE
|
||||
|
||||
coEvery {
|
||||
vaultSdkSource.importCxf(
|
||||
userId = userId,
|
||||
payload = payload,
|
||||
coEvery {
|
||||
credentialExchangeImportManager.importCxfPayload(
|
||||
userId = userId,
|
||||
payload = payload,
|
||||
)
|
||||
} returns ImportCxfPayloadResult.Success
|
||||
coEvery {
|
||||
vaultSyncManager.sync(userId = userId, forced = true)
|
||||
} returns SyncVaultDataResult.Success(itemsAvailable = true)
|
||||
val result = vaultRepository.importCxfPayload(payload)
|
||||
|
||||
assertEquals(
|
||||
ImportCredentialsResult.Success,
|
||||
result,
|
||||
)
|
||||
} returns ciphers.asSuccess()
|
||||
val result = vaultRepository.importCxfPayload(payload)
|
||||
coVerify(exactly = 1) {
|
||||
vaultSyncManager.sync(userId = userId, forced = true)
|
||||
}
|
||||
}
|
||||
|
||||
assertEquals(
|
||||
ImportCxfPayloadResult.Success(ciphers),
|
||||
result,
|
||||
@Test
|
||||
fun `importCxfPayload should return error result when activeUserId is null`() = runTest {
|
||||
val result = vaultRepository.importCxfPayload("")
|
||||
assertTrue(
|
||||
(result as? ImportCredentialsResult.Error)?.error is NoActiveUserException,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `importCxfPayload should return error result`() = runTest {
|
||||
fun `importCxfPayload should return error result when payload import fails`() = runTest {
|
||||
val userId = "mockId-1"
|
||||
val payload = "payload"
|
||||
val expected = Throwable()
|
||||
fakeAuthDiskSource.userState = MOCK_USER_STATE
|
||||
|
||||
coEvery {
|
||||
vaultSdkSource.importCxf(
|
||||
credentialExchangeImportManager.importCxfPayload(
|
||||
userId = userId,
|
||||
payload = payload,
|
||||
)
|
||||
} returns expected.asFailure()
|
||||
} returns ImportCxfPayloadResult.Error(expected)
|
||||
|
||||
val result = vaultRepository.importCxfPayload(payload)
|
||||
|
||||
assertEquals(
|
||||
ImportCxfPayloadResult.Error(expected),
|
||||
ImportCredentialsResult.Error(expected),
|
||||
result,
|
||||
)
|
||||
coVerify(exactly = 0) {
|
||||
vaultSyncManager.sync(userId = userId, forced = true)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `importCxfPayload should return NoItems when payload contains no credentials`() = runTest {
|
||||
val userId = "mockId-1"
|
||||
val payload = "payload"
|
||||
fakeAuthDiskSource.userState = MOCK_USER_STATE
|
||||
|
||||
coEvery {
|
||||
credentialExchangeImportManager.importCxfPayload(
|
||||
userId = userId,
|
||||
payload = payload,
|
||||
)
|
||||
} returns ImportCxfPayloadResult.NoItems
|
||||
|
||||
val result = vaultRepository.importCxfPayload(payload)
|
||||
|
||||
assertEquals(
|
||||
ImportCredentialsResult.NoItems,
|
||||
result,
|
||||
)
|
||||
coVerify(exactly = 0) {
|
||||
vaultSyncManager.sync(userId = userId, forced = true)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `importCxfPayload should return SyncFailed when sync fails`() = runTest {
|
||||
val userId = "mockId-1"
|
||||
val payload = "payload"
|
||||
val throwable = Throwable()
|
||||
fakeAuthDiskSource.userState = MOCK_USER_STATE
|
||||
|
||||
coEvery {
|
||||
credentialExchangeImportManager.importCxfPayload(
|
||||
userId = userId,
|
||||
payload = payload,
|
||||
)
|
||||
} returns ImportCxfPayloadResult.Success
|
||||
|
||||
coEvery {
|
||||
vaultSyncManager.sync(userId = userId, forced = true)
|
||||
} returns SyncVaultDataResult.Error(throwable)
|
||||
|
||||
val result = vaultRepository.importCxfPayload(payload)
|
||||
|
||||
assertEquals(
|
||||
ImportCredentialsResult.SyncFailed(throwable),
|
||||
result,
|
||||
)
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user