aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorJesse Luehrs <doy@tozt.net>2020-07-25 03:08:53 -0400
committerJesse Luehrs <doy@tozt.net>2020-07-25 03:08:53 -0400
commitc5d01a97ae4fda0871c8fd49777be325376ba5df (patch)
treed6417b2f4fe7ec5c84a3ea7d31daee7e9b5060f5 /src
parentc08a44b7cb903a448ea7815496703f1a39fbbd1f (diff)
downloadrbw-c5d01a97ae4fda0871c8fd49777be325376ba5df.tar.gz
rbw-c5d01a97ae4fda0871c8fd49777be325376ba5df.zip
add totp, uris, and custom fields to --full output
Diffstat (limited to 'src')
-rw-r--r--src/api.rs31
-rw-r--r--src/bin/rbw/commands.rs90
-rw-r--r--src/db.rs10
3 files changed, 127 insertions, 4 deletions
diff --git a/src/api.rs b/src/api.rs
index 6ee77ca..aca87b0 100644
--- a/src/api.rs
+++ b/src/api.rs
@@ -139,6 +139,8 @@ struct SyncResCipher {
notes: Option<String>,
#[serde(rename = "PasswordHistory")]
password_history: Option<Vec<SyncResPasswordHistory>>,
+ #[serde(rename = "Fields")]
+ fields: Option<Vec<SyncResField>>,
}
impl SyncResCipher {
@@ -172,6 +174,7 @@ impl SyncResCipher {
crate::db::EntryData::Login {
username: login.username.clone(),
password: login.password.clone(),
+ totp: login.totp.clone(),
uris: login.uris.as_ref().map_or_else(
std::vec::Vec::new,
|uris| {
@@ -215,6 +218,17 @@ impl SyncResCipher {
} else {
return None;
};
+ let fields = if let Some(fields) = &self.fields {
+ fields
+ .iter()
+ .map(|field| crate::db::Field {
+ name: field.name.clone(),
+ value: field.value.clone(),
+ })
+ .collect()
+ } else {
+ vec![]
+ };
Some(crate::db::Entry {
id: self.id.clone(),
org_id: self.organization_id.clone(),
@@ -222,6 +236,7 @@ impl SyncResCipher {
folder_id: folder_id.map(std::string::ToString::to_string),
name: self.name.clone(),
data,
+ fields,
notes: self.notes.clone(),
history,
})
@@ -260,6 +275,8 @@ struct CipherLogin {
username: Option<String>,
#[serde(rename = "Password")]
password: Option<String>,
+ #[serde(rename = "Totp")]
+ totp: Option<String>,
#[serde(rename = "Uris")]
uris: Option<Vec<CipherLoginUri>>,
}
@@ -337,6 +354,16 @@ struct SyncResPasswordHistory {
password: String,
}
+#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)]
+struct SyncResField {
+ #[serde(rename = "Type")]
+ ty: u32,
+ #[serde(rename = "Name")]
+ name: Option<String>,
+ #[serde(rename = "Value")]
+ value: Option<String>,
+}
+
#[derive(serde::Serialize, Debug)]
struct CiphersPostReq {
#[serde(rename = "type")]
@@ -546,6 +573,7 @@ impl Client {
crate::db::EntryData::Login {
username,
password,
+ totp,
uris,
} => {
let uris = if uris.is_empty() {
@@ -562,6 +590,7 @@ impl Client {
req.login = Some(CipherLogin {
username: username.clone(),
password: password.clone(),
+ totp: totp.clone(),
uris,
})
}
@@ -676,6 +705,7 @@ impl Client {
crate::db::EntryData::Login {
username,
password,
+ totp,
uris,
} => {
let uris = if uris.is_empty() {
@@ -692,6 +722,7 @@ impl Client {
req.login = Some(CipherLogin {
username: username.clone(),
password: password.clone(),
+ totp: totp.clone(),
uris,
});
}
diff --git a/src/bin/rbw/commands.rs b/src/bin/rbw/commands.rs
index 9ee80fc..3180cc9 100644
--- a/src/bin/rbw/commands.rs
+++ b/src/bin/rbw/commands.rs
@@ -17,6 +17,7 @@ struct DecryptedCipher {
folder: Option<String>,
name: String,
data: DecryptedData,
+ fields: Vec<DecryptedField>,
notes: Option<String>,
history: Vec<DecryptedHistoryEntry>,
}
@@ -78,10 +79,30 @@ impl DecryptedCipher {
fn display_long(&self, desc: &str) {
match &self.data {
- DecryptedData::Login { username, .. } => {
+ DecryptedData::Login {
+ username,
+ totp,
+ uris,
+ ..
+ } => {
let mut displayed = self.display_short(desc);
displayed |=
self.display_field("Username", username.as_deref());
+ displayed |=
+ self.display_field("TOTP Secret", totp.as_deref());
+
+ if let Some(uris) = uris {
+ for uri in uris {
+ displayed |= self.display_field("URI", Some(uri));
+ }
+ }
+
+ for field in &self.fields {
+ displayed |= self.display_field(
+ field.name.as_deref().unwrap_or_else(|| "(null)"),
+ Some(field.value.as_deref().unwrap_or_else(|| "")),
+ );
+ }
if let Some(notes) = &self.notes {
if displayed {
@@ -292,6 +313,8 @@ enum DecryptedData {
Login {
username: Option<String>,
password: Option<String>,
+ totp: Option<String>,
+ uris: Option<Vec<String>>,
},
Card {
cardholder_name: Option<String>,
@@ -325,6 +348,13 @@ enum DecryptedData {
#[derive(Debug, Clone)]
#[cfg_attr(test, derive(Eq, PartialEq))]
+struct DecryptedField {
+ name: Option<String>,
+ value: Option<String>,
+}
+
+#[derive(Debug, Clone)]
+#[cfg_attr(test, derive(Eq, PartialEq))]
struct DecryptedHistoryEntry {
last_used_date: String,
password: String,
@@ -593,6 +623,7 @@ pub fn add(
username,
password,
uris,
+ totp: None,
},
notes.as_deref(),
folder_id.as_deref(),
@@ -682,6 +713,7 @@ pub fn generate(
username,
password: Some(password),
uris,
+ totp: None,
},
None,
folder_id.as_deref(),
@@ -743,13 +775,14 @@ pub fn edit(
})
.transpose()?;
let mut history = entry.history.clone();
- let (entry_username, entry_password, entry_uris) =
+ let (entry_username, entry_password, entry_uris, entry_totp) =
match &entry.data {
rbw::db::EntryData::Login {
username,
password,
uris,
- } => (username, password, uris),
+ totp,
+ } => (username, password, uris, totp),
_ => unreachable!(),
};
let new_history_entry = rbw::db::HistoryEntry {
@@ -764,6 +797,7 @@ pub fn edit(
username: entry_username.clone(),
password,
uris: entry_uris.to_vec(),
+ totp: entry_totp.clone(),
};
(data, notes, history)
}
@@ -1069,6 +1103,34 @@ fn decrypt_cipher(entry: &rbw::db::Entry) -> anyhow::Result<DecryptedCipher> {
None
}
};
+ let fields = entry
+ .fields
+ .iter()
+ .map(|field| {
+ Ok(DecryptedField {
+ name: field
+ .name
+ .as_ref()
+ .map(|name| {
+ crate::actions::decrypt(
+ &name,
+ entry.org_id.as_deref(),
+ )
+ })
+ .transpose()?,
+ value: field
+ .value
+ .as_ref()
+ .map(|value| {
+ crate::actions::decrypt(
+ &value,
+ entry.org_id.as_deref(),
+ )
+ })
+ .transpose()?,
+ })
+ })
+ .collect::<anyhow::Result<_>>()?;
let notes = entry
.notes
.as_ref()
@@ -1097,7 +1159,10 @@ fn decrypt_cipher(entry: &rbw::db::Entry) -> anyhow::Result<DecryptedCipher> {
let data = match &entry.data {
rbw::db::EntryData::Login {
- username, password, ..
+ username,
+ password,
+ totp,
+ uris,
} => DecryptedData::Login {
username: decrypt_field(
"username",
@@ -1109,6 +1174,17 @@ fn decrypt_cipher(entry: &rbw::db::Entry) -> anyhow::Result<DecryptedCipher> {
password.as_deref(),
entry.org_id.as_deref(),
),
+ totp: decrypt_field(
+ "totp",
+ totp.as_deref(),
+ entry.org_id.as_deref(),
+ ),
+ uris: uris
+ .iter()
+ .map(|s| {
+ decrypt_field("uri", Some(s), entry.org_id.as_deref())
+ })
+ .collect(),
},
rbw::db::EntryData::Card {
cardholder_name,
@@ -1262,6 +1338,7 @@ fn decrypt_cipher(entry: &rbw::db::Entry) -> anyhow::Result<DecryptedCipher> {
folder,
name: crate::actions::decrypt(&entry.name, entry.org_id.as_deref())?,
data,
+ fields,
notes,
history,
})
@@ -1455,7 +1532,9 @@ mod test {
}),
password: None,
uris: vec![],
+ totp: None,
},
+ fields: vec![],
notes: None,
history: vec![],
},
@@ -1466,7 +1545,10 @@ mod test {
data: DecryptedData::Login {
username: username.map(std::string::ToString::to_string),
password: None,
+ totp: None,
+ uris: None,
},
+ fields: vec![],
notes: None,
history: vec![],
},
diff --git a/src/db.rs b/src/db.rs
index c695577..d519f00 100644
--- a/src/db.rs
+++ b/src/db.rs
@@ -13,6 +13,7 @@ pub struct Entry {
pub folder_id: Option<String>,
pub name: String,
pub data: EntryData,
+ pub fields: Vec<Field>,
pub notes: Option<String>,
pub history: Vec<HistoryEntry>,
}
@@ -24,6 +25,7 @@ pub enum EntryData {
Login {
username: Option<String>,
password: Option<String>,
+ totp: Option<String>,
uris: Vec<String>,
},
Card {
@@ -59,6 +61,14 @@ pub enum EntryData {
#[derive(
serde::Serialize, serde::Deserialize, Debug, Clone, Eq, PartialEq,
)]
+pub struct Field {
+ pub name: Option<String>,
+ pub value: Option<String>,
+}
+
+#[derive(
+ serde::Serialize, serde::Deserialize, Debug, Clone, Eq, PartialEq,
+)]
pub struct HistoryEntry {
pub last_used_date: String,
pub password: String,