From af6664da5c08cc39cf732d64ba74de1731095723 Mon Sep 17 00:00:00 2001
From: Awiteb
Date: Sat, 20 Apr 2024 16:38:50 +0200
Subject: [PATCH] feat: Make the username & password optional in the vault
(#12)
Reviewed-on: https://git.4rs.nl/awiteb/lprs/pulls/12
Co-authored-by: Awiteb
Co-committed-by: Awiteb
---
src/cli/add_command.rs | 9 ++++-
src/cli/edit_command.rs | 4 +-
src/cli/list_command.rs | 89 ++---------------------------------------
src/vault/bitwarden.rs | 22 +++++-----
src/vault/cipher.rs | 18 +++++++++
src/vault/mod.rs | 44 ++++++++------------
6 files changed, 57 insertions(+), 129 deletions(-)
diff --git a/src/cli/add_command.rs b/src/cli/add_command.rs
index 7b5a5c9..25009c6 100644
--- a/src/cli/add_command.rs
+++ b/src/cli/add_command.rs
@@ -18,7 +18,7 @@ use clap::Args;
use crate::{
vault::{vault_state::*, Vault, Vaults},
- LprsResult, RunCommand,
+ LprsError, LprsResult, RunCommand,
};
#[derive(Debug, Args)]
@@ -30,6 +30,13 @@ pub struct Add {
impl RunCommand for Add {
fn run(&self, mut vault_manager: Vaults) -> LprsResult<()> {
+ if self.vault_info.username.is_none()
+ && self.vault_info.password.is_none()
+ && self.vault_info.service.is_none()
+ && self.vault_info.note.is_none()
+ {
+ return Err(LprsError::Other("You can't add empty vault".to_owned()));
+ }
vault_manager.add_vault(self.vault_info.clone());
vault_manager.try_export()
}
diff --git a/src/cli/edit_command.rs b/src/cli/edit_command.rs
index d8cdd88..a7d393d 100644
--- a/src/cli/edit_command.rs
+++ b/src/cli/edit_command.rs
@@ -63,8 +63,8 @@ impl RunCommand for Edit {
} else {
*vault = Vault::::new(
self.name.as_ref().unwrap_or(&vault.name),
- self.username.as_ref().unwrap_or(&vault.username),
- self.password.as_ref().unwrap_or(&vault.password),
+ self.username.as_ref().or(vault.username.as_ref()),
+ self.password.as_ref().or(vault.password.as_ref()),
self.service.as_ref().or(vault.service.as_ref()),
self.note.as_ref().or(vault.note.as_ref()),
);
diff --git a/src/cli/list_command.rs b/src/cli/list_command.rs
index a7550c9..c26483c 100644
--- a/src/cli/list_command.rs
+++ b/src/cli/list_command.rs
@@ -17,8 +17,6 @@
use std::num::NonZeroU64;
use clap::Args;
-use comfy_table::Table;
-use regex::Regex;
use crate::{
vault::{vault_state::*, Vaults},
@@ -54,90 +52,11 @@ pub struct List {
impl RunCommand for List {
fn run(&self, vault_manager: Vaults) -> LprsResult<()> {
if vault_manager.vaults.is_empty() {
- Err(LprsError::Other(
+ return Err(LprsError::Other(
"Looks like there is no passwords to list".to_owned(),
- ))
- } else {
- if self.get.is_some() && self.search.is_some() {
- return Err(LprsError::ArgsConflict(
- "You cannot use `--get` arg with `--search` arg".to_owned(),
- ));
- }
- if self.regex && self.search.is_none() {
- return Err(LprsError::ArgsConflict(
- "You cannot use `--regex` without `--search` arg".to_owned(),
- ));
- }
-
- let mut table = Table::new();
- let mut header = vec!["Index", "Name", "Username", "Password"];
- if self.with_service {
- header.push("Service");
- }
- if self.with_note {
- header.push("Note");
- }
- let re = Regex::new(self.search.as_deref().unwrap_or("."))?;
-
- table.set_header(header);
- let vaults = vault_manager
- .vaults
- .iter()
- .enumerate()
- .filter(|(idx, pass)| {
- if let Some(index) = self.get {
- return (idx + 1) == index.get() as usize;
- }
- if let Some(ref pattern) = self.search {
- if self.regex {
- return re.is_match(&pass.name)
- || re.is_match(&pass.username)
- || (self.with_service
- && pass.service.as_ref().is_some_and(|s| re.is_match(s)))
- || (self.with_note
- && pass.note.as_ref().is_some_and(|n| re.is_match(n)));
- } else {
- let pattern = pattern.to_lowercase();
- return pass.name.to_lowercase().contains(&pattern)
- || pass.username.to_lowercase().contains(&pattern)
- || (self.with_service
- && pass
- .service
- .as_ref()
- .is_some_and(|s| s.to_lowercase().contains(&pattern)))
- || (self.with_note
- && pass
- .note
- .as_ref()
- .is_some_and(|n| n.to_lowercase().contains(&pattern)));
- }
- }
-
- true
- });
- for (idx, vault) in vaults {
- let hide_password = "*".repeat(vault.password.chars().count());
- let idx = (idx + 1).to_string();
- let mut row = vec![
- idx.as_str(),
- vault.name.as_str(),
- vault.username.as_str(),
- if self.unhide_password {
- vault.password.as_str()
- } else {
- hide_password.as_str()
- },
- ];
- if self.with_service {
- row.push(vault.service.as_deref().unwrap_or("Not Set"))
- }
- if self.with_note {
- row.push(vault.note.as_deref().unwrap_or("Not Set"))
- }
- table.add_row(row);
- }
- println!("{table}");
- Ok(())
+ ));
}
+
+ todo!("https://git.4rs.nl/awiteb/lprs/issues/8")
}
}
diff --git a/src/vault/bitwarden.rs b/src/vault/bitwarden.rs
index 7c4670c..f287464 100644
--- a/src/vault/bitwarden.rs
+++ b/src/vault/bitwarden.rs
@@ -43,17 +43,13 @@ impl From for Vault {
fn from(value: BitWardenPassword) -> Self {
Self::new(
value.name,
- value
- .login
- .as_ref()
- .map_or_else(String::new, |l| l.username.to_owned().unwrap_or_default()),
- value
- .login
- .as_ref()
- .map_or_else(String::new, |l| l.password.to_owned().unwrap_or_default()),
- value
- .login
- .and_then(|l| l.uris.and_then(|p| p.first().map(|u| u.uri.clone()))),
+ value.login.as_ref().and_then(|l| l.username.as_ref()),
+ value.login.as_ref().and_then(|l| l.password.as_ref()),
+ value.login.as_ref().and_then(|l| {
+ l.uris
+ .as_ref()
+ .and_then(|p| p.first().map(|u| u.uri.clone()))
+ }),
value.notes,
)
}
@@ -65,8 +61,8 @@ impl From> for BitWardenPassword {
ty: 1,
name: value.name,
login: Some(BitWardenLoginData {
- username: Some(value.username),
- password: Some(value.password),
+ username: value.username,
+ password: value.password,
uris: value
.service
.map(|s| vec![BitWardenUri { mt: None, uri: s }]),
diff --git a/src/vault/cipher.rs b/src/vault/cipher.rs
index 8e66c71..39a5c21 100644
--- a/src/vault/cipher.rs
+++ b/src/vault/cipher.rs
@@ -46,3 +46,21 @@ pub fn decrypt(master_password: &[u8], data: &str) -> LprsResult {
})
.map(|d| String::from_utf8(d).map_err(LprsError::Utf8))?
}
+
+/// Encrypt if the `Option` are `Some`
+pub fn encrypt_some(
+ master_password: &[u8],
+ data: Option>,
+) -> LprsResult