aboutsummaryrefslogtreecommitdiffstats
path: root/src/bin/rbw/commands.rs
diff options
context:
space:
mode:
authorJesse Luehrs <doy@tozt.net>2020-04-18 03:26:08 -0400
committerJesse Luehrs <doy@tozt.net>2020-04-18 03:26:38 -0400
commitf1d63c8bfb76b218822ee417e98a9015877ab528 (patch)
treed9a920a10382d3140e9014b21d57c95d9c37f9b7 /src/bin/rbw/commands.rs
parent0402e161b1a3b69af3cc8ede2fcaf1c4855b601b (diff)
downloadrbw-f1d63c8bfb76b218822ee417e98a9015877ab528.tar.gz
rbw-f1d63c8bfb76b218822ee417e98a9015877ab528.zip
refactor
Diffstat (limited to 'src/bin/rbw/commands.rs')
-rw-r--r--src/bin/rbw/commands.rs119
1 files changed, 85 insertions, 34 deletions
diff --git a/src/bin/rbw/commands.rs b/src/bin/rbw/commands.rs
index 3e2f241..acf3f32 100644
--- a/src/bin/rbw/commands.rs
+++ b/src/bin/rbw/commands.rs
@@ -1,5 +1,13 @@
use anyhow::Context as _;
+#[derive(Debug, Clone)]
+struct DecryptedCipher {
+ name: String,
+ username: Option<String>,
+ password: Option<String>,
+ notes: Option<String>,
+}
+
const HELP: &str = r#"
# The first line of this file will be the password, and the remainder of the
# file (after any blank lines after the password) will be stored as a note.
@@ -81,48 +89,22 @@ pub fn get(name: &str, user: Option<&str>) -> anyhow::Result<()> {
let email = config_email()?;
let db = rbw::db::Db::load(&email)
.context("failed to load password database")?;
+
let desc = format!(
"{}{}",
user.map(|s| format!("{}@", s))
.unwrap_or_else(|| "".to_string()),
name
);
- for cipher in db.ciphers {
- let cipher_name = crate::actions::decrypt(&cipher.name)
- .context("failed to decrypt entry name")?;
- if name == cipher_name {
- if let Some(user) = user {
- if let Some(encrypted_user) = &cipher.login.username {
- let cipher_user = crate::actions::decrypt(encrypted_user)
- .context("failed to decrypt entry username")?;
- if user == cipher_user {
- if let Some(encrypted_pass) = &cipher.login.password {
- let pass =
- crate::actions::decrypt(encrypted_pass)
- .context(
- "failed to decrypt entry password",
- )?;
- println!("{}", pass);
- } else {
- eprintln!("no password found for entry {}", desc);
- }
- return Ok(());
- }
- }
- } else {
- if let Some(encrypted_pass) = &cipher.login.password {
- let pass = crate::actions::decrypt(encrypted_pass)
- .context("failed to decrypt entry password")?;
- println!("{}", pass);
- } else {
- eprintln!("no password found for entry {}", desc);
- }
- return Ok(());
- }
- }
+
+ let entry = find_entry(&db, name, user)
+ .with_context(|| format!("couldn't find entry for '{}'", desc))?;
+ if let Some(password) = entry.password {
+ println!("{}", password);
+ } else {
+ eprintln!("entry for '{}' had no password", desc);
}
- eprintln!("no entry found for {}", desc);
Ok(())
}
@@ -306,6 +288,75 @@ fn ensure_agent() -> anyhow::Result<()> {
Ok(())
}
+fn find_entry(
+ db: &rbw::db::Db,
+ name: &str,
+ username: Option<&str>,
+) -> anyhow::Result<DecryptedCipher> {
+ let ciphers: anyhow::Result<Vec<DecryptedCipher>> = db
+ .ciphers
+ .iter()
+ .cloned()
+ .map(decrypt_cipher)
+ .filter(|res| {
+ if let Ok(decrypted_cipher) = res {
+ name == decrypted_cipher.name
+ && if let Some(username) = username {
+ decrypted_cipher.username.as_deref() == Some(username)
+ } else {
+ true
+ }
+ } else {
+ true
+ }
+ })
+ .collect();
+ let ciphers = ciphers?;
+
+ if ciphers.is_empty() {
+ Err(anyhow::anyhow!("no entry found"))
+ } else if ciphers.len() > 1 {
+ let users: Vec<String> = ciphers
+ .iter()
+ .map(|cipher| {
+ cipher
+ .username
+ .clone()
+ .unwrap_or_else(|| "(no login)".to_string())
+ })
+ .collect();
+ let users = users.join(", ");
+ Err(anyhow::anyhow!("multiple entries found: {}", users))
+ } else {
+ Ok(ciphers[0].clone())
+ }
+}
+
+fn decrypt_cipher(
+ cipher: rbw::api::Cipher,
+) -> anyhow::Result<DecryptedCipher> {
+ Ok(DecryptedCipher {
+ name: crate::actions::decrypt(&cipher.name)?,
+ username: cipher
+ .login
+ .username
+ .as_ref()
+ .map(|username| crate::actions::decrypt(username))
+ .transpose()?,
+ password: cipher
+ .login
+ .password
+ .as_ref()
+ .map(|password| crate::actions::decrypt(password))
+ .transpose()?,
+ notes: cipher
+ .notes
+ .as_ref()
+ .map(|notes| crate::actions::decrypt(notes))
+ .transpose()?,
+ })
+}
+
fn config_email() -> anyhow::Result<String> {
let config = rbw::config::Config::load()?;
if let Some(email) = config.email {