Add migration logic for cipher attachments (#1446)

This commit is contained in:
David Perez 2024-06-11 17:07:09 -05:00 committed by Álison Fernandes
parent 67d2ac6191
commit 914db8d8e5

View file

@ -1,6 +1,7 @@
package com.x8bit.bitwarden.data.vault.manager package com.x8bit.bitwarden.data.vault.manager
import android.net.Uri import android.net.Uri
import androidx.core.net.toUri
import com.bitwarden.core.AttachmentView import com.bitwarden.core.AttachmentView
import com.bitwarden.core.Cipher import com.bitwarden.core.Cipher
import com.bitwarden.core.CipherView import com.bitwarden.core.CipherView
@ -28,6 +29,9 @@ import com.x8bit.bitwarden.data.vault.repository.util.toEncryptedNetworkCipher
import com.x8bit.bitwarden.data.vault.repository.util.toEncryptedNetworkCipherResponse import com.x8bit.bitwarden.data.vault.repository.util.toEncryptedNetworkCipherResponse
import com.x8bit.bitwarden.data.vault.repository.util.toEncryptedSdkCipher import com.x8bit.bitwarden.data.vault.repository.util.toEncryptedSdkCipher
import com.x8bit.bitwarden.data.vault.repository.util.toNetworkAttachmentRequest import com.x8bit.bitwarden.data.vault.repository.util.toNetworkAttachmentRequest
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.coroutineScope
import java.io.File import java.io.File
import java.time.Clock import java.time.Clock
@ -237,12 +241,14 @@ class CipherManagerImpl(
collectionIds: List<String>, collectionIds: List<String>,
): ShareCipherResult { ): ShareCipherResult {
val userId = activeUserId ?: return ShareCipherResult.Error val userId = activeUserId ?: return ShareCipherResult.Error
return vaultSdkSource return migrateAttachments(cipherView = cipherView)
.moveToOrganization( .flatMap {
userId = userId, vaultSdkSource.moveToOrganization(
organizationId = organizationId, userId = userId,
cipherView = cipherView, organizationId = organizationId,
) cipherView = cipherView,
)
}
.flatMap { vaultSdkSource.encryptCipher(userId = userId, cipherView = it) } .flatMap { vaultSdkSource.encryptCipher(userId = userId, cipherView = it) }
.flatMap { cipher -> .flatMap { cipher ->
ciphersService.shareCipher( ciphersService.shareCipher(
@ -317,8 +323,8 @@ class CipherManagerImpl(
private suspend fun createAttachmentForResult( private suspend fun createAttachmentForResult(
cipherId: String, cipherId: String,
cipherView: CipherView, cipherView: CipherView,
fileSizeBytes: String, fileSizeBytes: String?,
fileName: String, fileName: String?,
fileUri: Uri, fileUri: Uri,
): Result<CipherView> { ): Result<CipherView> {
val userId = activeUserId ?: return IllegalStateException("No active user").asFailure() val userId = activeUserId ?: return IllegalStateException("No active user").asFailure()
@ -478,4 +484,51 @@ class CipherManagerImpl(
} else { } else {
vaultSdkSource.encryptCipher(userId = userId, cipherView = this) vaultSdkSource.encryptCipher(userId = userId, cipherView = this)
} }
@Suppress("ReturnCount")
private suspend fun migrateAttachments(cipherView: CipherView): Result<CipherView> {
// Only run the migrations if we have attachments that do not have their own 'key'
val attachmentsToMigrate = cipherView.attachments.orEmpty().filter { it.key == null }
if (attachmentsToMigrate.none()) return cipherView.asSuccess()
val cipherViewId = cipherView.id
?: return IllegalStateException("CipherView must have an ID").asFailure()
val migrations = coroutineScope {
attachmentsToMigrate.map { attachmentView ->
async {
attachmentView
.id
?.let { attachmentId ->
this@CipherManagerImpl
.downloadAttachmentForResult(
cipherView = cipherView,
attachmentId = attachmentId,
)
.flatMap {
createAttachmentForResult(
cipherId = cipherViewId,
cipherView = cipherView,
fileSizeBytes = attachmentView.size,
fileName = attachmentView.fileName,
fileUri = it.toUri(),
)
}
.flatMap {
deleteCipherAttachmentForResult(
cipherId = cipherViewId,
attachmentId = attachmentId,
cipherView = cipherView,
)
}
.map { cipherView }
}
?: IllegalStateException("AttachmentView must have an ID").asFailure()
}
}
}
return awaitAll(*migrations.toTypedArray())
.firstOrNull { it.isFailure }
?: cipherView.asSuccess()
}
} }