This commit is contained in:
Matt Gibson 2024-10-24 12:55:13 -07:00
parent 425b085a96
commit 028d382a5b
No known key found for this signature in database
GPG key ID: 7CBCA182C13B0912
5 changed files with 110 additions and 18 deletions

View file

@ -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<DataState<List<CipherView>>>
val offlineCiphersStateFlow: StateFlow<DataState<List<OfflineCipherView>>>
/**
* Flow that represents all ciphers for the active user.
*

View file

@ -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<SendData>>(DataState.Loading)
private val mutableOfflineCiphersStateFlow =
MutableStateFlow<DataState<List<CipherView>>>(DataState.Loading)
MutableStateFlow<DataState<List<OfflineCipherView>>>(DataState.Loading)
private val mutableCiphersStateFlow =
MutableStateFlow<DataState<List<CipherView>>>(DataState.Loading)
@ -208,8 +211,8 @@ class VaultRepositoryImpl(
override val totpCodeFlow: Flow<TotpCodeResult>
get() = mutableTotpCodeResultFlow.asSharedFlow()
override val offlineCiphersStateFlow: StateFlow<DataState<List<CipherView>>>
get() = mutableOfflineCiphersStateFlow.asStateFlow();
override val offlineCiphersStateFlow: StateFlow<DataState<List<OfflineCipherView>>>
get() = mutableOfflineCiphersStateFlow.asStateFlow()
override val ciphersStateFlow: StateFlow<DataState<List<CipherView>>>
get() = mutableCiphersStateFlow.asStateFlow()
@ -945,18 +948,21 @@ class VaultRepositoryImpl(
private fun observeVaultDiskOfflineCiphers(
userId: String,
): Flow<DataState<List<CipherView>>> =
): Flow<DataState<List<OfflineCipherView>>> =
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) },
)
}

View file

@ -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<Uuid>,
/**
* 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<AttachmentView>?,
val fields: List<FieldView>?,
val passwordHistory: List<PasswordHistoryView>?,
val creationDate: DateTime,
val deletedDate: DateTime?,
val revisionDate: DateTime,
val mergeConflict: Boolean
)

View file

@ -17,7 +17,7 @@ import com.bitwarden.vault.FolderView
* @param fido2CredentialAutofillViewList List of decrypted fido 2 credentials.
*/
data class VaultData(
val offlineCipherViewList: List<CipherView>,
val offlineCipherViewList: List<OfflineCipherView>,
val cipherViewList: List<CipherView>,
val collectionViewList: List<CollectionView>,
val folderViewList: List<FolderView>,

View file

@ -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<SyncResponseJson.Cipher>.toEncryptedSdkCipherList(): List<Cipher> =
* Converts a list of [OfflineCipherJson] objects to a list of corresponding
* Bitwarden SDK [Cipher] objects.
*/
fun List<OfflineCipherJson>.toCipherList(): List<Cipher> =
fun List<OfflineCipherJson>.toCipherList(): List<Cipher> =
map { it.toOfflineCipher().toCipher() }
/**