feat: Make the username & password optional in the vault (#12)
Reviewed-on: #12 Co-authored-by: Awiteb <a@4rs.nl> Co-committed-by: Awiteb <a@4rs.nl>
This commit is contained in:
parent
fbf00ebacd
commit
af6664da5c
6 changed files with 57 additions and 129 deletions
|
@ -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<Plain>) -> 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()
|
||||
}
|
||||
|
|
|
@ -63,8 +63,8 @@ impl RunCommand for Edit {
|
|||
} else {
|
||||
*vault = Vault::<Plain>::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()),
|
||||
);
|
||||
|
|
|
@ -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<Plain>) -> 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")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,17 +43,13 @@ impl From<BitWardenPassword> for Vault<Plain> {
|
|||
fn from(value: BitWardenPassword) -> Self {
|
||||
Self::new(
|
||||
value.name,
|
||||
value
|
||||
.login
|
||||
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()
|
||||
.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()))),
|
||||
.and_then(|p| p.first().map(|u| u.uri.clone()))
|
||||
}),
|
||||
value.notes,
|
||||
)
|
||||
}
|
||||
|
@ -65,8 +61,8 @@ impl From<Vault<Plain>> 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 }]),
|
||||
|
|
|
@ -46,3 +46,21 @@ pub fn decrypt(master_password: &[u8], data: &str) -> LprsResult<String> {
|
|||
})
|
||||
.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<impl AsRef<str>>,
|
||||
) -> LprsResult<Option<String>> {
|
||||
data.map(|d| encrypt(master_password, d.as_ref()))
|
||||
.transpose()
|
||||
}
|
||||
|
||||
/// Decrypt if the `Option` are `Some`
|
||||
pub fn decrypt_some(
|
||||
master_password: &[u8],
|
||||
data: Option<impl AsRef<str>>,
|
||||
) -> LprsResult<Option<String>> {
|
||||
data.map(|d| decrypt(master_password, d.as_ref()))
|
||||
.transpose()
|
||||
}
|
||||
|
|
|
@ -58,10 +58,10 @@ where
|
|||
pub name: String,
|
||||
/// The username
|
||||
#[arg(short, long)]
|
||||
pub username: String,
|
||||
pub username: Option<String>,
|
||||
/// The password
|
||||
#[arg(short, long)]
|
||||
pub password: String,
|
||||
pub password: Option<String>,
|
||||
/// The service name. e.g the website url
|
||||
#[arg(short, long)]
|
||||
pub service: Option<String>,
|
||||
|
@ -96,17 +96,17 @@ where
|
|||
/// Create new [`Vault`] instance
|
||||
pub fn new(
|
||||
name: impl Into<String>,
|
||||
username: impl Into<String>,
|
||||
password: impl Into<String>,
|
||||
username: Option<impl Into<String>>,
|
||||
password: Option<impl Into<String>>,
|
||||
service: Option<impl Into<String>>,
|
||||
note: Option<impl Into<String>>,
|
||||
) -> Self {
|
||||
Self {
|
||||
name: name.into(),
|
||||
username: username.into(),
|
||||
password: password.into(),
|
||||
service: service.map(|s| s.into()),
|
||||
note: note.map(|s| s.into()),
|
||||
username: username.map(Into::into),
|
||||
password: password.map(Into::into),
|
||||
service: service.map(Into::into),
|
||||
note: note.map(Into::into),
|
||||
phantom: std::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
|
@ -117,16 +117,10 @@ impl Vault<Encrypted> {
|
|||
pub fn decrypt(&self, master_password: &[u8]) -> LprsResult<Vault<Plain>> {
|
||||
Ok(Vault::<Plain>::new(
|
||||
cipher::decrypt(master_password, &self.name)?,
|
||||
cipher::decrypt(master_password, &self.username)?,
|
||||
cipher::decrypt(master_password, &self.password)?,
|
||||
self.service
|
||||
.as_ref()
|
||||
.map(|url| cipher::decrypt(master_password, url))
|
||||
.transpose()?,
|
||||
self.note
|
||||
.as_ref()
|
||||
.map(|note| cipher::decrypt(master_password, note))
|
||||
.transpose()?,
|
||||
cipher::decrypt_some(master_password, self.username.as_ref())?,
|
||||
cipher::decrypt_some(master_password, self.password.as_ref())?,
|
||||
cipher::decrypt_some(master_password, self.service.as_ref())?,
|
||||
cipher::decrypt_some(master_password, self.note.as_ref())?,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
@ -136,16 +130,10 @@ impl Vault<Plain> {
|
|||
pub fn encrypt(&self, master_password: &[u8]) -> LprsResult<Vault<Encrypted>> {
|
||||
Ok(Vault::<Encrypted>::new(
|
||||
cipher::encrypt(master_password, &self.name)?,
|
||||
cipher::encrypt(master_password, &self.username)?,
|
||||
cipher::encrypt(master_password, &self.password)?,
|
||||
self.service
|
||||
.as_ref()
|
||||
.map(|url| cipher::encrypt(master_password, url))
|
||||
.transpose()?,
|
||||
self.note
|
||||
.as_ref()
|
||||
.map(|note| cipher::encrypt(master_password, note))
|
||||
.transpose()?,
|
||||
cipher::encrypt_some(master_password, self.username.as_ref())?,
|
||||
cipher::encrypt_some(master_password, self.password.as_ref())?,
|
||||
cipher::encrypt_some(master_password, self.service.as_ref())?,
|
||||
cipher::encrypt_some(master_password, self.note.as_ref())?,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue