From 028d382a5b660ba88e6e29be0a1e91d0454755e1 Mon Sep 17 00:00:00 2001 From: Matt Gibson Date: Thu, 24 Oct 2024 12:55:13 -0700 Subject: [PATCH] wip --- .../data/vault/repository/VaultRepository.kt | 3 +- .../vault/repository/VaultRepositoryImpl.kt | 20 ++++--- .../repository/model/OfflineCipherView.kt | 46 +++++++++++++++ .../data/vault/repository/model/VaultData.kt | 2 +- .../util/VaultSdkCipherExtensions.kt | 57 ++++++++++++++++--- 5 files changed, 110 insertions(+), 18 deletions(-) create mode 100644 app/src/main/java/com/x8bit/bitwarden/data/vault/repository/model/OfflineCipherView.kt diff --git a/app/src/main/java/com/x8bit/bitwarden/data/vault/repository/VaultRepository.kt b/app/src/main/java/com/x8bit/bitwarden/data/vault/repository/VaultRepository.kt index a51f0959f..20f18bd36 100644 --- a/app/src/main/java/com/x8bit/bitwarden/data/vault/repository/VaultRepository.kt +++ b/app/src/main/java/com/x8bit/bitwarden/data/vault/repository/VaultRepository.kt @@ -22,6 +22,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.OfflineCipherView 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.SyncVaultDataResult @@ -62,7 +63,7 @@ interface VaultRepository : CipherManager, VaultLockManager { * Note that the [StateFlow.value] will return the last known value but the [StateFlow] itself * must be collected in order to trigger state changes. */ - val offlineCiphersStateFlow: StateFlow>> + val offlineCiphersStateFlow: StateFlow>> /** * Flow that represents all ciphers for the active user. * diff --git a/app/src/main/java/com/x8bit/bitwarden/data/vault/repository/VaultRepositoryImpl.kt b/app/src/main/java/com/x8bit/bitwarden/data/vault/repository/VaultRepositoryImpl.kt index ab417c238..73a523f9c 100644 --- a/app/src/main/java/com/x8bit/bitwarden/data/vault/repository/VaultRepositoryImpl.kt +++ b/app/src/main/java/com/x8bit/bitwarden/data/vault/repository/VaultRepositoryImpl.kt @@ -63,6 +63,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.OfflineCipherView 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.SyncVaultDataResult @@ -83,6 +84,8 @@ import com.x8bit.bitwarden.data.vault.repository.util.toEncryptedSdkFolder 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.toOfflineCipher +import com.x8bit.bitwarden.data.vault.repository.util.toOfflineCipherView import com.x8bit.bitwarden.ui.vault.feature.vault.model.VaultFilterType import com.x8bit.bitwarden.ui.vault.feature.vault.util.toFilteredList import kotlinx.coroutines.CancellationException @@ -158,7 +161,7 @@ class VaultRepositoryImpl( private val mutableSendDataStateFlow = MutableStateFlow>(DataState.Loading) private val mutableOfflineCiphersStateFlow = - MutableStateFlow>>(DataState.Loading) + MutableStateFlow>>(DataState.Loading) private val mutableCiphersStateFlow = MutableStateFlow>>(DataState.Loading) @@ -208,8 +211,8 @@ class VaultRepositoryImpl( override val totpCodeFlow: Flow get() = mutableTotpCodeResultFlow.asSharedFlow() - override val offlineCiphersStateFlow: StateFlow>> - get() = mutableOfflineCiphersStateFlow.asStateFlow(); + override val offlineCiphersStateFlow: StateFlow>> + get() = mutableOfflineCiphersStateFlow.asStateFlow() override val ciphersStateFlow: StateFlow>> get() = mutableCiphersStateFlow.asStateFlow() @@ -945,18 +948,21 @@ class VaultRepositoryImpl( private fun observeVaultDiskOfflineCiphers( userId: String, - ): Flow>> = + ): Flow>> = vaultDiskSource.getOfflineCiphers(userId = userId) .onStart { mutableOfflineCiphersStateFlow.updateToPendingOrLoading() } - .map { + .map { ciphers -> waitUntilUnlocked(userId = userId) vaultSdkSource .decryptCipherList( userId = userId, - cipherList = it.toCipherList(), + cipherList = ciphers.toCipherList(), ) + .map { + it.zip(ciphers).map { (cipher, offlineJson) -> cipher.toOfflineCipherView(offlineJson.toOfflineCipher()) } + } .fold( - onSuccess = { ciphers -> DataState.Loaded(ciphers.sortAlphabetically()) }, + onSuccess = { views -> DataState.Loaded(views) }, onFailure = { throwable -> DataState.Error(throwable) }, ) } diff --git a/app/src/main/java/com/x8bit/bitwarden/data/vault/repository/model/OfflineCipherView.kt b/app/src/main/java/com/x8bit/bitwarden/data/vault/repository/model/OfflineCipherView.kt new file mode 100644 index 000000000..601b0bde5 --- /dev/null +++ b/app/src/main/java/com/x8bit/bitwarden/data/vault/repository/model/OfflineCipherView.kt @@ -0,0 +1,46 @@ +package com.x8bit.bitwarden.data.vault.repository.model + +import com.bitwarden.core.DateTime +import com.bitwarden.core.Uuid +import com.bitwarden.crypto.EncString +import com.bitwarden.vault.AttachmentView +import com.bitwarden.vault.CardView +import com.bitwarden.vault.CipherRepromptType +import com.bitwarden.vault.CipherType +import com.bitwarden.vault.FieldView +import com.bitwarden.vault.IdentityView +import com.bitwarden.vault.LocalDataView +import com.bitwarden.vault.LoginView +import com.bitwarden.vault.PasswordHistoryView +import com.bitwarden.vault.SecureNoteView + +data class OfflineCipherView ( + val id: Uuid?, + val organizationId: Uuid?, + val folderId: Uuid?, + val collectionIds: List, + /** + * Temporary, required to support re-encrypting existing items. + */ + val key: EncString?, + val name: String, + val notes: String?, + val type: CipherType, + val login: LoginView?, + val identity: IdentityView?, + val card: CardView?, + val secureNote: SecureNoteView?, + val favorite: Boolean, + val reprompt: CipherRepromptType, + val organizationUseTotp: Boolean, + val edit: Boolean, + val viewPassword: Boolean, + val localData: LocalDataView?, + val attachments: List?, + val fields: List?, + val passwordHistory: List?, + val creationDate: DateTime, + val deletedDate: DateTime?, + val revisionDate: DateTime, + val mergeConflict: Boolean +) \ No newline at end of file diff --git a/app/src/main/java/com/x8bit/bitwarden/data/vault/repository/model/VaultData.kt b/app/src/main/java/com/x8bit/bitwarden/data/vault/repository/model/VaultData.kt index 4f736505e..2d068b950 100644 --- a/app/src/main/java/com/x8bit/bitwarden/data/vault/repository/model/VaultData.kt +++ b/app/src/main/java/com/x8bit/bitwarden/data/vault/repository/model/VaultData.kt @@ -17,7 +17,7 @@ import com.bitwarden.vault.FolderView * @param fido2CredentialAutofillViewList List of decrypted fido 2 credentials. */ data class VaultData( - val offlineCipherViewList: List, + val offlineCipherViewList: List, val cipherViewList: List, val collectionViewList: List, val folderViewList: List, diff --git a/app/src/main/java/com/x8bit/bitwarden/data/vault/repository/util/VaultSdkCipherExtensions.kt b/app/src/main/java/com/x8bit/bitwarden/data/vault/repository/util/VaultSdkCipherExtensions.kt index cba86c45d..897abbf55 100644 --- a/app/src/main/java/com/x8bit/bitwarden/data/vault/repository/util/VaultSdkCipherExtensions.kt +++ b/app/src/main/java/com/x8bit/bitwarden/data/vault/repository/util/VaultSdkCipherExtensions.kt @@ -4,7 +4,9 @@ package com.x8bit.bitwarden.data.vault.repository.util import com.bitwarden.core.DateTime import com.bitwarden.vault.Attachment +import com.bitwarden.vault.AttachmentView import com.bitwarden.vault.Card +import com.bitwarden.vault.CardView import com.bitwarden.vault.Cipher import com.bitwarden.vault.CipherRepromptType import com.bitwarden.vault.CipherType @@ -12,12 +14,18 @@ import com.bitwarden.vault.CipherView import com.bitwarden.vault.Fido2Credential import com.bitwarden.vault.Field import com.bitwarden.vault.FieldType +import com.bitwarden.vault.FieldView import com.bitwarden.vault.Identity +import com.bitwarden.vault.IdentityView +import com.bitwarden.vault.LocalDataView import com.bitwarden.vault.Login import com.bitwarden.vault.LoginUri +import com.bitwarden.vault.LoginView import com.bitwarden.vault.PasswordHistory +import com.bitwarden.vault.PasswordHistoryView import com.bitwarden.vault.SecureNote import com.bitwarden.vault.SecureNoteType +import com.bitwarden.vault.SecureNoteView import com.bitwarden.vault.UriMatchType import com.x8bit.bitwarden.data.platform.util.SpecialCharWithPrecedenceComparator import com.x8bit.bitwarden.data.vault.datasource.network.model.AttachmentJsonRequest @@ -31,6 +39,7 @@ import com.x8bit.bitwarden.data.vault.datasource.network.model.SecureNoteTypeJso import com.x8bit.bitwarden.data.vault.datasource.network.model.SyncResponseJson import com.x8bit.bitwarden.data.vault.datasource.network.model.UriMatchTypeJson import com.x8bit.bitwarden.data.vault.datasource.sdk.model.OfflineCipher +import com.x8bit.bitwarden.data.vault.repository.model.OfflineCipherView import com.x8bit.bitwarden.ui.vault.feature.vault.model.NotificationSummary import java.time.ZoneOffset import java.time.ZonedDateTime @@ -63,7 +72,7 @@ fun Cipher.toEncryptedNetworkCipher(): CipherJsonRequest = ) fun Cipher.toOfflineCipher(): OfflineCipher = - OfflineCipher ( + OfflineCipher( id = id, organizationId = organizationId, folderId = folderId, @@ -108,19 +117,51 @@ fun OfflineCipher.toOfflineCipherJson(): OfflineCipherJson = passwordHistory = passwordHistory?.toEncryptedNetworkPasswordHistoryList(), creationDate = ZonedDateTime.ofInstant(creationDate, ZoneOffset.UTC), deletedDate = deletedDate?.let { ZonedDateTime.ofInstant(deletedDate, ZoneOffset.UTC) }, - revisionDate = ZonedDateTime.ofInstant(creationDate, ZoneOffset.UTC), + revisionDate = ZonedDateTime.ofInstant(creationDate, ZoneOffset.UTC), mergeConflict = false, // TODO: Copy from the new OfflineCipher type - ) + ) -fun CipherView.toNotificationSummary(): NotificationSummary = +fun OfflineCipherView.toNotificationSummary(): NotificationSummary = NotificationSummary( title = name, - subtitle = "edited on ${revisionDate.toString()}", + subtitle = "edited on $revisionDate. Has Merge Conflict: $mergeConflict", + ) + +fun CipherView.toOfflineCipherView(offlineCipher: OfflineCipher) = + OfflineCipherView( + id = id, + organizationId = organizationId, + folderId = folderId, + collectionIds = collectionIds, + /** + * Temporary, required to support re-encrypting existing items. + */ + key = key, + name = name, + notes = notes, + type = type, + login = login, + identity = identity, + card = card, + secureNote = secureNote, + favorite = favorite, + reprompt = reprompt, + organizationUseTotp = organizationUseTotp, + edit = edit, + viewPassword = viewPassword, + localData = localData, + attachments = attachments, + fields = fields, + passwordHistory = passwordHistory, + creationDate = creationDate, + deletedDate = deletedDate, + revisionDate = revisionDate, + mergeConflict = offlineCipher.mergeConflict ) fun OfflineCipherJson.toOfflineCipher(): OfflineCipher = OfflineCipher( - id = if(id.startsWith("create")) null else id, + id = if (id.startsWith("create")) null else id, organizationId = organizationId, folderId = folderId, collectionIds = collectionIds.orEmpty(), @@ -143,8 +184,6 @@ fun OfflineCipherJson.toOfflineCipher(): OfflineCipher = mergeConflict = mergeConflict ) - - fun OfflineCipher.toCipher(): Cipher = Cipher( id = id, @@ -440,7 +479,7 @@ fun List.toEncryptedSdkCipherList(): List = * Converts a list of [OfflineCipherJson] objects to a list of corresponding * Bitwarden SDK [Cipher] objects. */ -fun List.toCipherList(): List = +fun List.toCipherList(): List = map { it.toOfflineCipher().toCipher() } /**