From 869d36481c398ad65595e01b32714720dd6cfb1e Mon Sep 17 00:00:00 2001 From: Jesse Luehrs Date: Sat, 20 Apr 2024 17:11:11 -0400 Subject: don't delete custom fields when editing passwords --- src/actions.rs | 4 +++ src/api.rs | 94 +++++++++++++++++++++++++++++++++++++++++++------ src/bin/rbw/commands.rs | 7 ++-- src/db.rs | 2 ++ 4 files changed, 93 insertions(+), 14 deletions(-) diff --git a/src/actions.rs b/src/actions.rs index b07cf44..7ee1fa4 100644 --- a/src/actions.rs +++ b/src/actions.rs @@ -185,6 +185,7 @@ pub fn edit( org_id: Option<&str>, name: &str, data: &crate::db::EntryData, + fields: &[crate::db::Field], notes: Option<&str>, folder_uuid: Option<&str>, history: &[crate::db::HistoryEntry], @@ -196,6 +197,7 @@ pub fn edit( org_id, name, data, + fields, notes, folder_uuid, history, @@ -209,6 +211,7 @@ fn edit_once( org_id: Option<&str>, name: &str, data: &crate::db::EntryData, + fields: &[crate::db::Field], notes: Option<&str>, folder_uuid: Option<&str>, history: &[crate::db::HistoryEntry], @@ -220,6 +223,7 @@ fn edit_once( org_id, name, data, + fields, notes, folder_uuid, history, diff --git a/src/api.rs b/src/api.rs index 85d2265..9a58f9e 100644 --- a/src/api.rs +++ b/src/api.rs @@ -362,7 +362,7 @@ struct SyncResCipher { #[serde(rename = "PasswordHistory", alias = "passwordHistory")] password_history: Option>, #[serde(rename = "Fields", alias = "fields")] - fields: Option>, + fields: Option>, #[serde(rename = "DeletedDate", alias = "deletedDate")] deleted_date: Option, } @@ -463,8 +463,10 @@ impl SyncResCipher { fields .iter() .map(|field| crate::db::Field { + ty: field.ty, name: field.name.clone(), value: field.value.clone(), + linked_id: field.linked_id, }) .collect() }); @@ -582,6 +584,75 @@ struct CipherIdentity { username: Option, } +#[derive( + serde_repr::Serialize_repr, + serde_repr::Deserialize_repr, + Debug, + Clone, + Copy, + PartialEq, + Eq, +)] +#[repr(u16)] +pub enum FieldType { + Text = 0, + Hidden = 1, + Boolean = 2, + Linked = 3, +} + +#[derive( + serde_repr::Serialize_repr, + serde_repr::Deserialize_repr, + Debug, + Clone, + Copy, + PartialEq, + Eq, +)] +#[repr(u16)] +pub enum LinkedIdType { + LoginUsername = 100, + LoginPassword = 101, + CardCardholderName = 300, + CardExpMonth = 301, + CardExpYear = 302, + CardCode = 303, + CardBrand = 304, + CardNumber = 305, + IdentityTitle = 400, + IdentityMiddleName = 401, + IdentityAddress1 = 402, + IdentityAddress2 = 403, + IdentityAddress3 = 404, + IdentityCity = 405, + IdentityState = 406, + IdentityPostalCode = 407, + IdentityCountry = 408, + IdentityCompany = 409, + IdentityEmail = 410, + IdentityPhone = 411, + IdentitySsn = 412, + IdentityUsername = 413, + IdentityPassportNumber = 414, + IdentityLicenseNumber = 415, + IdentityFirstName = 416, + IdentityLastName = 417, + IdentityFullName = 418, +} + +#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)] +struct CipherField { + #[serde(rename = "Type", alias = "type")] + ty: FieldType, + #[serde(rename = "Name", alias = "name")] + name: Option, + #[serde(rename = "Value", alias = "value")] + value: Option, + #[serde(rename = "LinkedId", alias = "linkedId")] + linked_id: Option, +} + // this is just a name and some notes, both of which are already on the cipher // object #[derive(serde::Serialize, serde::Deserialize, Debug, Clone)] @@ -595,16 +666,6 @@ struct SyncResPasswordHistory { password: Option, } -#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)] -struct SyncResField { - #[serde(rename = "Type", alias = "type")] - ty: u32, - #[serde(rename = "Name", alias = "name")] - name: Option, - #[serde(rename = "Value", alias = "value")] - value: Option, -} - #[derive(serde::Serialize, Debug)] struct CiphersPostReq { #[serde(rename = "type")] @@ -633,6 +694,7 @@ struct CiphersPutReq { login: Option, card: Option, identity: Option, + fields: Vec, #[serde(rename = "secureNote")] secure_note: Option, #[serde(rename = "passwordHistory")] @@ -1042,6 +1104,7 @@ impl Client { org_id: Option<&str>, name: &str, data: &crate::db::EntryData, + fields: &[crate::db::Field], notes: Option<&str>, folder_uuid: Option<&str>, history: &[crate::db::HistoryEntry], @@ -1061,6 +1124,15 @@ impl Client { card: None, identity: None, secure_note: None, + fields: fields + .iter() + .map(|field| CipherField { + ty: field.ty, + name: field.name.clone(), + value: field.value.clone(), + linked_id: field.linked_id, + }) + .collect(), password_history: history .iter() .map(|entry| CiphersPutReqHistory { diff --git a/src/bin/rbw/commands.rs b/src/bin/rbw/commands.rs index a4a513b..3329f76 100644 --- a/src/bin/rbw/commands.rs +++ b/src/bin/rbw/commands.rs @@ -1266,7 +1266,7 @@ pub fn edit( find_entry(&db, &Needle::Name(name.to_string()), username, folder) .with_context(|| format!("couldn't find entry for '{desc}'"))?; - let (data, notes, history) = match &decrypted.data { + let (data, fields, notes, history) = match &decrypted.data { DecryptedData::Login { password, .. } => { let mut contents = format!("{}\n", password.as_deref().unwrap_or("")); @@ -1320,7 +1320,7 @@ pub fn edit( uris: entry_uris.clone(), totp: entry_totp.clone(), }; - (data, notes, history) + (data, entry.fields, notes, history) } DecryptedData::SecureNote {} => { let data = rbw::db::EntryData::SecureNote {}; @@ -1340,7 +1340,7 @@ pub fn edit( }) .transpose()?; - (data, notes, entry.history) + (data, entry.fields, notes, entry.history) } _ => { return Err(anyhow::anyhow!( @@ -1356,6 +1356,7 @@ pub fn edit( entry.org_id.as_deref(), &entry.name, &data, + &fields, notes.as_deref(), entry.folder_id.as_deref(), &history, diff --git a/src/db.rs b/src/db.rs index 73a69d1..ab742ea 100644 --- a/src/db.rs +++ b/src/db.rs @@ -147,8 +147,10 @@ pub enum EntryData { serde::Serialize, serde::Deserialize, Debug, Clone, Eq, PartialEq, )] pub struct Field { + pub ty: crate::api::FieldType, pub name: Option, pub value: Option, + pub linked_id: Option, } #[derive( -- cgit v1.2.3-54-g00ecf