feat: Validate args before ask for the master password (#17)
Reviewed-on: #17 Fixes: #13 Co-authored-by: Awiteb <a@4rs.nl> Co-committed-by: Awiteb <a@4rs.nl>
This commit is contained in:
parent
4af6639e21
commit
b4bcaa92ca
11 changed files with 124 additions and 96 deletions
|
@ -18,7 +18,7 @@ use clap::Args;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
vault::{vault_state::*, Vault, Vaults},
|
vault::{vault_state::*, Vault, Vaults},
|
||||||
LprsError, LprsResult, RunCommand,
|
LprsCommand, LprsError, LprsResult,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Args)]
|
#[derive(Debug, Args)]
|
||||||
|
@ -31,16 +31,8 @@ pub struct Add {
|
||||||
password: Option<Option<String>>,
|
password: Option<Option<String>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RunCommand for Add {
|
impl LprsCommand for Add {
|
||||||
fn run(mut self, mut vault_manager: Vaults<Plain>) -> LprsResult<()> {
|
fn run(mut self, mut vault_manager: Vaults<Plain>) -> LprsResult<()> {
|
||||||
if self.vault_info.username.is_none()
|
|
||||||
&& self.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()));
|
|
||||||
}
|
|
||||||
|
|
||||||
match self.password {
|
match self.password {
|
||||||
Some(Some(password)) => self.vault_info.password = Some(password),
|
Some(Some(password)) => self.vault_info.password = Some(password),
|
||||||
Some(None) => {
|
Some(None) => {
|
||||||
|
@ -58,4 +50,15 @@ impl RunCommand for Add {
|
||||||
vault_manager.add_vault(self.vault_info);
|
vault_manager.add_vault(self.vault_info);
|
||||||
vault_manager.try_export()
|
vault_manager.try_export()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn validate_args(&self) -> LprsResult<()> {
|
||||||
|
if self.vault_info.username.is_none()
|
||||||
|
&& self.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()));
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,14 +20,14 @@ use clap::Args;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
vault::{vault_state::*, Vaults},
|
vault::{vault_state::*, Vaults},
|
||||||
LprsError, LprsResult, RunCommand,
|
LprsCommand, LprsError, LprsResult,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Args)]
|
#[derive(Debug, Args)]
|
||||||
#[command(author, version, about, long_about = None)]
|
#[command(author, version, about, long_about = None)]
|
||||||
pub struct Clean {}
|
pub struct Clean {}
|
||||||
|
|
||||||
impl RunCommand for Clean {
|
impl LprsCommand for Clean {
|
||||||
fn run(self, vault_manager: Vaults<Plain>) -> LprsResult<()> {
|
fn run(self, vault_manager: Vaults<Plain>) -> LprsResult<()> {
|
||||||
fs::write(vault_manager.vaults_file, "[]").map_err(LprsError::Io)
|
fs::write(vault_manager.vaults_file, "[]").map_err(LprsError::Io)
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,7 @@ use clap::Args;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
vault::{vault_state::*, Vault, Vaults},
|
vault::{vault_state::*, Vault, Vaults},
|
||||||
LprsError, LprsResult, RunCommand,
|
LprsCommand, LprsError, LprsResult,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Args)]
|
#[derive(Debug, Args)]
|
||||||
|
@ -46,7 +46,7 @@ pub struct Edit {
|
||||||
note: Option<String>,
|
note: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RunCommand for Edit {
|
impl LprsCommand for Edit {
|
||||||
fn run(self, mut vault_manager: Vaults<Plain>) -> LprsResult<()> {
|
fn run(self, mut vault_manager: Vaults<Plain>) -> LprsResult<()> {
|
||||||
let index = self.index.get() as usize;
|
let index = self.index.get() as usize;
|
||||||
|
|
||||||
|
@ -58,17 +58,6 @@ impl RunCommand for Edit {
|
||||||
)));
|
)));
|
||||||
};
|
};
|
||||||
|
|
||||||
if self.name.is_none()
|
|
||||||
&& self.username.is_none()
|
|
||||||
&& self.password.is_none()
|
|
||||||
&& self.service.is_none()
|
|
||||||
&& self.note.is_none()
|
|
||||||
{
|
|
||||||
return Err(LprsError::Other(
|
|
||||||
"You must edit one option at least".to_owned(),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the password from stdin or from its value if provided
|
// Get the password from stdin or from its value if provided
|
||||||
let password = match self.password {
|
let password = match self.password {
|
||||||
Some(Some(password)) => Some(password),
|
Some(Some(password)) => Some(password),
|
||||||
|
@ -91,4 +80,18 @@ impl RunCommand for Edit {
|
||||||
);
|
);
|
||||||
vault_manager.try_export()
|
vault_manager.try_export()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn validate_args(&self) -> LprsResult<()> {
|
||||||
|
if self.name.is_none()
|
||||||
|
&& self.username.is_none()
|
||||||
|
&& self.password.is_none()
|
||||||
|
&& self.service.is_none()
|
||||||
|
&& self.note.is_none()
|
||||||
|
{
|
||||||
|
return Err(LprsError::Other(
|
||||||
|
"You must edit one option at least".to_owned(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,7 @@ use clap::Args;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
vault::{vault_state::*, BitWardenPasswords, Format, Vault, Vaults},
|
vault::{vault_state::*, BitWardenPasswords, Format, Vault, Vaults},
|
||||||
LprsError, LprsResult, RunCommand,
|
LprsCommand, LprsError, LprsResult,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Args)]
|
#[derive(Debug, Args)]
|
||||||
|
@ -33,24 +33,26 @@ pub struct Export {
|
||||||
format: Format,
|
format: Format,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RunCommand for Export {
|
impl LprsCommand for Export {
|
||||||
fn run(self, vault_manager: Vaults<Plain>) -> LprsResult<()> {
|
fn run(self, vault_manager: Vaults<Plain>) -> LprsResult<()> {
|
||||||
|
let exported_data = match self.format {
|
||||||
|
Format::Lprs => {
|
||||||
|
serde_json::to_string::<Vec<Vault<Encrypted>>>(&vault_manager.encrypt_vaults()?)
|
||||||
|
}
|
||||||
|
Format::BitWarden => serde_json::to_string(&BitWardenPasswords::from(vault_manager)),
|
||||||
|
}?;
|
||||||
|
|
||||||
|
fs::write(&self.path, exported_data).map_err(LprsError::from)
|
||||||
|
}
|
||||||
|
|
||||||
|
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"))
|
||||||
{
|
{
|
||||||
if !self.path.exists() {
|
if !self.path.exists() {
|
||||||
let exported_data = match self.format {
|
Ok(())
|
||||||
Format::Lprs => serde_json::to_string::<Vec<Vault<Encrypted>>>(
|
|
||||||
&vault_manager.encrypt_vaults()?,
|
|
||||||
),
|
|
||||||
Format::BitWarden => {
|
|
||||||
serde_json::to_string(&BitWardenPasswords::from(vault_manager))
|
|
||||||
}
|
|
||||||
}?;
|
|
||||||
|
|
||||||
fs::write(&self.path, exported_data).map_err(LprsError::from)
|
|
||||||
} else {
|
} else {
|
||||||
Err(LprsError::Io(IoError::new(
|
Err(LprsError::Io(IoError::new(
|
||||||
IoErrorKind::AlreadyExists,
|
IoErrorKind::AlreadyExists,
|
||||||
|
|
|
@ -20,7 +20,7 @@ use clap::Args;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
vault::{vault_state::*, Vaults},
|
vault::{vault_state::*, Vaults},
|
||||||
LprsError, LprsResult, RunCommand,
|
LprsCommand, LprsError, LprsResult,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Args)]
|
#[derive(Debug, Args)]
|
||||||
|
@ -44,26 +44,29 @@ pub struct Gen {
|
||||||
symbols: bool,
|
symbols: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RunCommand for Gen {
|
impl LprsCommand for Gen {
|
||||||
fn run(self, _vault_manager: Vaults<Plain>) -> LprsResult<()> {
|
fn run(self, _vault_manager: Vaults<Plain>) -> LprsResult<()> {
|
||||||
if self.uppercase || self.lowercase || self.numbers || self.symbols {
|
println!(
|
||||||
println!(
|
"{}",
|
||||||
"{}",
|
passwords::PasswordGenerator::new()
|
||||||
passwords::PasswordGenerator::new()
|
.length(self.length.get() as usize)
|
||||||
.length(self.length.get() as usize)
|
.uppercase_letters(self.uppercase)
|
||||||
.uppercase_letters(self.uppercase)
|
.lowercase_letters(self.lowercase)
|
||||||
.lowercase_letters(self.lowercase)
|
.numbers(self.numbers)
|
||||||
.numbers(self.numbers)
|
.symbols(self.symbols)
|
||||||
.symbols(self.symbols)
|
.strict(true)
|
||||||
.strict(true)
|
.generate_one()
|
||||||
.generate_one()
|
.expect("The length cannot be zero")
|
||||||
.expect("The length cannot be zero")
|
);
|
||||||
);
|
Ok(())
|
||||||
Ok(())
|
}
|
||||||
} else {
|
|
||||||
Err(LprsError::Other(
|
fn validate_args(&self) -> LprsResult<()> {
|
||||||
|
if !(self.uppercase || self.lowercase || self.numbers || self.symbols) {
|
||||||
|
return Err(LprsError::Other(
|
||||||
"You need to enable at least one kind of characters".to_owned(),
|
"You need to enable at least one kind of characters".to_owned(),
|
||||||
))
|
));
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,7 @@ use clap::Args;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
vault::{vault_state::*, BitWardenPasswords, Format, Vault, Vaults},
|
vault::{vault_state::*, BitWardenPasswords, Format, Vault, Vaults},
|
||||||
LprsError, LprsResult, RunCommand,
|
LprsCommand, LprsError, LprsResult,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Args)]
|
#[derive(Debug, Args)]
|
||||||
|
@ -34,41 +34,42 @@ pub struct Import {
|
||||||
format: Format,
|
format: Format,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RunCommand for Import {
|
impl LprsCommand for Import {
|
||||||
fn run(self, mut vault_manager: Vaults<Plain>) -> LprsResult<()> {
|
fn run(self, mut vault_manager: Vaults<Plain>) -> LprsResult<()> {
|
||||||
|
let imported_passwords_len = match self.format {
|
||||||
|
Format::Lprs => {
|
||||||
|
let vaults = Vaults::try_reload(self.path, vault_manager.master_password.to_vec())?;
|
||||||
|
let vaults_len = vaults.vaults.len();
|
||||||
|
|
||||||
|
vault_manager.vaults.extend(vaults.vaults);
|
||||||
|
vault_manager.try_export()?;
|
||||||
|
vaults_len
|
||||||
|
}
|
||||||
|
Format::BitWarden => {
|
||||||
|
let vaults: BitWardenPasswords = serde_json::from_reader(File::open(&self.path)?)?;
|
||||||
|
let vaults_len = vaults.items.len();
|
||||||
|
|
||||||
|
vault_manager
|
||||||
|
.vaults
|
||||||
|
.extend(vaults.items.into_iter().map(Vault::from));
|
||||||
|
vault_manager.try_export()?;
|
||||||
|
vaults_len
|
||||||
|
}
|
||||||
|
};
|
||||||
|
println!(
|
||||||
|
"{imported_passwords_len} vault{s} were imported successfully",
|
||||||
|
s = if imported_passwords_len >= 2 { "s" } else { "" }
|
||||||
|
);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn validate_args(&self) -> LprsResult<()> {
|
||||||
if self.path.exists() {
|
if self.path.exists() {
|
||||||
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"))
|
||||||
{
|
{
|
||||||
let imported_passwords_len = match self.format {
|
|
||||||
Format::Lprs => {
|
|
||||||
let vaults =
|
|
||||||
Vaults::try_reload(self.path, vault_manager.master_password.to_vec())?;
|
|
||||||
let vaults_len = vaults.vaults.len();
|
|
||||||
|
|
||||||
vault_manager.vaults.extend(vaults.vaults);
|
|
||||||
vault_manager.try_export()?;
|
|
||||||
vaults_len
|
|
||||||
}
|
|
||||||
Format::BitWarden => {
|
|
||||||
let vaults: BitWardenPasswords =
|
|
||||||
serde_json::from_reader(File::open(&self.path)?)?;
|
|
||||||
let vaults_len = vaults.items.len();
|
|
||||||
|
|
||||||
vault_manager
|
|
||||||
.vaults
|
|
||||||
.extend(vaults.items.into_iter().map(Vault::from));
|
|
||||||
vault_manager.try_export()?;
|
|
||||||
vaults_len
|
|
||||||
}
|
|
||||||
};
|
|
||||||
println!(
|
|
||||||
"{imported_passwords_len} vault{s} were imported successfully",
|
|
||||||
s = if imported_passwords_len >= 2 { "s" } else { "" }
|
|
||||||
);
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err(LprsError::Io(IoError::new(
|
Err(LprsError::Io(IoError::new(
|
||||||
|
|
|
@ -20,7 +20,7 @@ use clap::Args;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
vault::{vault_state::*, Vaults},
|
vault::{vault_state::*, Vaults},
|
||||||
LprsError, LprsResult, RunCommand,
|
LprsCommand, LprsError, LprsResult,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Args)]
|
#[derive(Debug, Args)]
|
||||||
|
@ -49,7 +49,7 @@ pub struct List {
|
||||||
regex: bool,
|
regex: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RunCommand for List {
|
impl LprsCommand for List {
|
||||||
fn run(self, vault_manager: Vaults<Plain>) -> LprsResult<()> {
|
fn run(self, vault_manager: Vaults<Plain>) -> LprsResult<()> {
|
||||||
if vault_manager.vaults.is_empty() {
|
if vault_manager.vaults.is_empty() {
|
||||||
return Err(LprsError::Other(
|
return Err(LprsError::Other(
|
||||||
|
|
|
@ -18,7 +18,7 @@ use std::path::PathBuf;
|
||||||
|
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
|
|
||||||
use crate::{utils, vault::Vaults, LprsResult, RunCommand};
|
use crate::{utils, vault::Vaults, LprsCommand, LprsResult};
|
||||||
|
|
||||||
pub mod add_command;
|
pub mod add_command;
|
||||||
pub mod clean_command;
|
pub mod clean_command;
|
||||||
|
@ -63,6 +63,8 @@ impl Cli {
|
||||||
};
|
};
|
||||||
log::debug!("Getting the vaults file: {}", vaults_file.to_string_lossy());
|
log::debug!("Getting the vaults file: {}", vaults_file.to_string_lossy());
|
||||||
|
|
||||||
|
self.command.validate_args()?;
|
||||||
|
|
||||||
let vault_manager = if matches!(self.command, Commands::Clean(..) | Commands::Gen(..)) {
|
let vault_manager = if matches!(self.command, Commands::Clean(..) | Commands::Gen(..)) {
|
||||||
// Returns empty vault manager for those commands don't need it
|
// Returns empty vault manager for those commands don't need it
|
||||||
Vaults {
|
Vaults {
|
||||||
|
|
|
@ -20,7 +20,7 @@ use clap::Args;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
vault::{vault_state::*, Vaults},
|
vault::{vault_state::*, Vaults},
|
||||||
LprsError, LprsResult, RunCommand,
|
LprsCommand, LprsError, LprsResult,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Args)]
|
#[derive(Debug, Args)]
|
||||||
|
@ -34,7 +34,7 @@ pub struct Remove {
|
||||||
force: bool,
|
force: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RunCommand for Remove {
|
impl LprsCommand for Remove {
|
||||||
fn run(self, mut vault_manager: Vaults<Plain>) -> LprsResult<()> {
|
fn run(self, mut vault_manager: Vaults<Plain>) -> LprsResult<()> {
|
||||||
let index = (self.index.get() - 1) as usize;
|
let index = (self.index.get() - 1) as usize;
|
||||||
if index > vault_manager.vaults.len() {
|
if index > vault_manager.vaults.len() {
|
||||||
|
|
|
@ -14,10 +14,10 @@
|
||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/gpl-3.0.html>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/gpl-3.0.html>.
|
||||||
|
|
||||||
/// Creates commands macro, to create the `Commands` enum and impl `RunCommand` to it.
|
/// Creates commands macro, to create the `Commands` enum and impl `LprsCommand` to it.
|
||||||
///
|
///
|
||||||
/// ### Notes:
|
/// ### Notes:
|
||||||
/// The `$command` must impl `RunCommand` trait
|
/// The `$command` must impl `LprsCommand` trait
|
||||||
///
|
///
|
||||||
/// ### Example:
|
/// ### Example:
|
||||||
/// ```rust
|
/// ```rust
|
||||||
|
@ -37,7 +37,7 @@
|
||||||
/// Some(SomeArgs),
|
/// Some(SomeArgs),
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// impl crate::RunCommand for TestCommands {
|
/// impl crate::LprsCommand for TestCommands {
|
||||||
/// fn run(
|
/// fn run(
|
||||||
/// &self,
|
/// &self,
|
||||||
/// vault_manager: crate::vault::Vaults,
|
/// vault_manager: crate::vault::Vaults,
|
||||||
|
@ -62,7 +62,7 @@ macro_rules! create_commands {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[automatically_derived]
|
#[automatically_derived]
|
||||||
impl $crate::RunCommand for $enum_name{
|
impl $crate::LprsCommand for $enum_name{
|
||||||
fn run(self, vault_manager: $crate::vault::Vaults<$crate::vault::vault_state::Plain>) -> $crate::LprsResult<()> {
|
fn run(self, vault_manager: $crate::vault::Vaults<$crate::vault::vault_state::Plain>) -> $crate::LprsResult<()> {
|
||||||
match self {
|
match self {
|
||||||
$(
|
$(
|
||||||
|
@ -70,6 +70,14 @@ macro_rules! create_commands {
|
||||||
)+
|
)+
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn validate_args(&self) -> LprsResult<()> {
|
||||||
|
match self {
|
||||||
|
$(
|
||||||
|
Self::$varint(command) => command.validate_args(),
|
||||||
|
)+
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,13 @@ use crate::{
|
||||||
LprsResult,
|
LprsResult,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Trait to run the command
|
/// Trait to work with the commands
|
||||||
pub trait RunCommand {
|
pub trait LprsCommand {
|
||||||
|
/// Run the command, should do all the logic, even the export
|
||||||
fn run(self, vault_manager: Vaults<Plain>) -> LprsResult<()>;
|
fn run(self, vault_manager: Vaults<Plain>) -> LprsResult<()>;
|
||||||
|
|
||||||
|
/// Validate the gaiven args from the user.
|
||||||
|
fn validate_args(&self) -> LprsResult<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue