diff --git a/crates/bitwarden-exporters/src/lib.rs b/crates/bitwarden-exporters/src/lib.rs index 89e4094ab..b92f86b2f 100644 --- a/crates/bitwarden-exporters/src/lib.rs +++ b/crates/bitwarden-exporters/src/lib.rs @@ -3,7 +3,8 @@ use std::fmt; use bitwarden_vault::{ - CipherRepromptType, CipherView, Fido2CredentialFullView, FolderId, LoginUriView, UriMatchType, + CipherRepromptType, CipherView, Fido2CredentialFullView, FieldView, FolderId, LoginUriView, + UriMatchType, }; use chrono::{DateTime, Utc}; use uuid::Uuid; @@ -242,7 +243,14 @@ impl From for CipherView { view_password: true, local_data: None, attachments: None, - fields: None, + fields: { + let fields: Vec = value.fields.into_iter().map(Into::into).collect(); + if fields.is_empty() { + None + } else { + Some(fields) + } + }, password_history: None, creation_date: value.creation_date, deleted_date: None, @@ -422,7 +430,7 @@ pub struct SshKey { #[cfg(test)] mod tests { - use bitwarden_vault::CipherType as VaultCipherType; + use bitwarden_vault::{CipherType as VaultCipherType, FieldType}; use chrono::{DateTime, Utc}; use super::*; @@ -448,7 +456,12 @@ mod tests { })), favorite: true, reprompt: 1, - fields: vec![], + fields: vec![Field { + name: Some("CustomField".to_string()), + value: Some("CustomValue".to_string()), + r#type: 0, + linked_id: None, + }], revision_date: test_date, creation_date: test_date, deleted_date: None, @@ -469,6 +482,15 @@ mod tests { assert_eq!(cipher_view.creation_date, test_date); assert_eq!(cipher_view.revision_date, test_date); + let fields = cipher_view.fields.unwrap(); + assert_eq!(fields.len(), 1); + + let field = fields.first().unwrap(); + assert_eq!(field.name, Some("CustomField".to_string())); + assert_eq!(field.value, Some("CustomValue".to_string())); + assert_eq!(field.r#type, FieldType::Text); + assert_eq!(field.linked_id, None); + let login = cipher_view.login.expect("Login should be present"); assert_eq!(login.username, Some("test@example.com".to_string())); assert_eq!(login.password, Some("password123".to_string())); diff --git a/crates/bitwarden-exporters/src/models.rs b/crates/bitwarden-exporters/src/models.rs index feaf78ede..89d85efdc 100644 --- a/crates/bitwarden-exporters/src/models.rs +++ b/crates/bitwarden-exporters/src/models.rs @@ -1,8 +1,8 @@ use bitwarden_core::{key_management::KeyIds, require, MissingFieldError}; use bitwarden_crypto::KeyStore; use bitwarden_vault::{ - CardView, Cipher, CipherType, CipherView, Fido2CredentialFullView, FieldView, FolderView, - IdentityView, LoginUriView, SecureNoteType, SecureNoteView, SshKeyView, + CardView, Cipher, CipherType, CipherView, Fido2CredentialFullView, FieldType, FieldView, + FolderView, IdentityView, LoginUriView, SecureNoteType, SecureNoteView, SshKeyView, }; impl TryFrom for crate::Folder { @@ -188,6 +188,17 @@ impl From for crate::Field { } } +impl From for FieldView { + fn from(value: crate::Field) -> Self { + Self { + name: value.name, + value: value.value, + r#type: value.r#type.try_into().unwrap_or(FieldType::Text), + linked_id: value.linked_id.and_then(|id| id.try_into().ok()), + } + } +} + impl From for crate::SecureNoteType { fn from(value: SecureNoteType) -> Self { match value { diff --git a/crates/bitwarden-vault/src/cipher/field.rs b/crates/bitwarden-vault/src/cipher/field.rs index 732989485..9811e68b2 100644 --- a/crates/bitwarden-vault/src/cipher/field.rs +++ b/crates/bitwarden-vault/src/cipher/field.rs @@ -1,7 +1,7 @@ use bitwarden_api_api::models::CipherFieldModel; use bitwarden_core::{ key_management::{KeyIds, SymmetricKeyId}, - require, + require, MissingFieldError, }; use bitwarden_crypto::{ CompositeEncryptable, CryptoError, Decryptable, EncString, KeyStoreContext, @@ -18,7 +18,7 @@ use super::linked_id::LinkedIdType; use crate::VaultParseError; /// Represents the type of a [FieldView]. -#[derive(Clone, Copy, Serialize_repr, Deserialize_repr, Debug)] +#[derive(Clone, Copy, Serialize_repr, Deserialize_repr, Debug, PartialEq, Eq)] #[repr(u8)] #[cfg_attr(feature = "uniffi", derive(uniffi::Enum))] #[cfg_attr(feature = "wasm", wasm_bindgen)] @@ -33,6 +33,20 @@ pub enum FieldType { Linked = 3, } +impl TryFrom for FieldType { + type Error = MissingFieldError; + + fn try_from(value: u8) -> Result { + match value { + 0 => Ok(FieldType::Text), + 1 => Ok(FieldType::Hidden), + 2 => Ok(FieldType::Boolean), + 3 => Ok(FieldType::Linked), + _ => Err(MissingFieldError("FieldType")), + } + } +} + #[derive(Serialize, Deserialize, Debug, Clone)] #[serde(rename_all = "camelCase", deny_unknown_fields)] #[cfg_attr(feature = "uniffi", derive(uniffi::Record))] @@ -114,3 +128,22 @@ impl From for FieldType { } } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_field_type_try_from_u8_valid() { + assert_eq!(FieldType::try_from(0).unwrap(), FieldType::Text); + assert_eq!(FieldType::try_from(1).unwrap(), FieldType::Hidden); + assert_eq!(FieldType::try_from(2).unwrap(), FieldType::Boolean); + assert_eq!(FieldType::try_from(3).unwrap(), FieldType::Linked); + } + + #[test] + fn test_field_type_try_from_u8_invalid() { + assert!(FieldType::try_from(4).is_err()); + assert!(FieldType::try_from(255).is_err()); + } +}