feat: Support export and import with different password #30

Merged
awiteb merged 5 commits from awiteb/fix-25 into master 2024-05-08 05:06:28 +02:00 AGit
2 changed files with 52 additions and 8 deletions
Showing only changes of commit a6483cf333 - Show all commits

View file

@ -17,8 +17,10 @@
use std::{fs, io::Error as IoError, io::ErrorKind as IoErrorKind, path::PathBuf}; use std::{fs, io::Error as IoError, io::ErrorKind as IoErrorKind, path::PathBuf};
use clap::Args; use clap::Args;
use sha2::Digest;
use crate::{ use crate::{
utils,
vault::{BitWardenPasswords, Format, Vaults}, vault::{BitWardenPasswords, Format, Vaults},
LprsCommand, LprsCommand,
LprsError, LprsError,
@ -30,12 +32,17 @@ use crate::{
/// Export command, used to export the vaults in `lprs` format or `BitWarden` /// Export command, used to export the vaults in `lprs` format or `BitWarden`
/// format. The exported file will be a json file. /// format. The exported file will be a json file.
pub struct Export { pub struct Export {
// TODO: `force` flag to write on existing file
/// The path to export to /// The path to export to
path: PathBuf, path: PathBuf,
/// Format to export vaults in /// Format to export vaults in
#[arg(short, long, value_name = "FORMAT", default_value_t= Format::Lprs)] #[arg(short, long, value_name = "FORMAT", default_value_t= Format::Lprs)]
format: Format, format: Format,
// TODO: `force` flag to write on existing file /// Encryption password of the exported vaults (in `lprs` format)
/// if there is not, will use the master password
#[arg(short = 'p', long)]
#[allow(clippy::option_option)]
encryption_password: Option<Option<String>>,
} }
impl LprsCommand for Export { impl LprsCommand for Export {
@ -46,8 +53,19 @@ impl LprsCommand for Export {
self.path.display(), self.path.display(),
self.format self.format
); );
let encryption_key: Option<[u8; 32]> =
utils::user_password(self.encryption_password, "Encryption Password:")?
.map(|p| sha2::Sha256::digest(p).into());
let exported_data = match self.format { let exported_data = match self.format {
Format::Lprs => vault_manager.json_export()?, Format::Lprs => {
vault_manager.json_export(
encryption_key
.as_ref()
.unwrap_or(&vault_manager.master_password),
)?
}
Format::BitWarden => serde_json::to_string(&BitWardenPasswords::from(vault_manager))?, Format::BitWarden => serde_json::to_string(&BitWardenPasswords::from(vault_manager))?,
}; };
@ -77,6 +95,11 @@ impl LprsCommand for Export {
format!("file `{}` is a directory", self.path.display()), format!("file `{}` is a directory", self.path.display()),
))); )));
} }
if self.encryption_password.is_some() && self.format != Format::Lprs {
return Err(LprsError::Other(
"You only can to use the encryption password with `lprs` format".to_owned(),
));
}
Ok(()) Ok(())
} }

View file

@ -22,8 +22,10 @@ use std::{
}; };
use clap::Args; use clap::Args;
use sha2::Digest;
use crate::{ use crate::{
utils,
vault::{BitWardenPasswords, Format, Vault, Vaults}, vault::{BitWardenPasswords, Format, Vault, Vaults},
LprsCommand, LprsCommand,
LprsError, LprsError,
@ -40,7 +42,12 @@ pub struct Import {
/// The format to import from /// The format to import from
#[arg(short, long, default_value_t = Format::Lprs)] #[arg(short, long, default_value_t = Format::Lprs)]
format: Format, format: Format,
/// Decryption password of the imported vaults (in `lprs` format)
/// if there is not, will use the master password
#[arg(short = 'p', long)]
#[allow(clippy::option_option)]
decryption_password: Option<Option<String>>,
} }
impl LprsCommand for Import { impl LprsCommand for Import {
@ -52,10 +59,18 @@ impl LprsCommand for Import {
vault_manager.vaults_file.display() vault_manager.vaults_file.display()
); );
let decryption_key: Option<[u8; 32]> =
utils::user_password(self.decryption_password, "Decryption password:")?
.map(|p| sha2::Sha256::digest(p).into());
let imported_passwords_len = match self.format { let imported_passwords_len = match self.format {
Format::Lprs => { Format::Lprs => {
let vaults = let vaults = Vaults::json_reload(
Vaults::json_reload(&vault_manager.master_password, &fs::read(self.path)?)?; decryption_key
.as_ref()
.unwrap_or(&vault_manager.master_password),
&fs::read(self.path)?,
)?;
let vaults_len = vaults.len(); let vaults_len = vaults.len();
vault_manager.vaults.extend(vaults); vault_manager.vaults.extend(vaults);
@ -81,7 +96,7 @@ impl LprsCommand for Import {
} }
fn validate_args(&self) -> LprsResult<()> { fn validate_args(&self) -> LprsResult<()> {
if self if !self
.path .path
.extension() .extension()
.is_some_and(|e| e.to_string_lossy().eq_ignore_ascii_case("json")) .is_some_and(|e| e.to_string_lossy().eq_ignore_ascii_case("json"))
@ -103,6 +118,12 @@ impl LprsCommand for Import {
format!("file `{}` is a directory", self.path.display()), format!("file `{}` is a directory", self.path.display()),
))); )));
} }
if self.decryption_password.is_some() && self.format != Format::Lprs {
return Err(LprsError::Other(
"You only can to use the decryption password with `lprs` format".to_owned(),
));
}
Ok(()) Ok(())
} }