Merge pull request #3 from TheAwiteb/gen-command

Gen command
This commit is contained in:
Mohammed Alotaibi 2023-12-24 20:29:49 +03:00 committed by GitHub
commit be1ab5d527
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 117 additions and 21 deletions

View file

@ -35,6 +35,7 @@ Commands:
list List your password and search list List your password and search
clean Clean the password file clean Clean the password file
edit Edit the password content edit Edit the password content
gen Generate password
help Print this message or the help of the given subcommand(s) help Print this message or the help of the given subcommand(s)
Options: Options:
@ -46,6 +47,24 @@ Options:
Print version Print version
``` ```
### Example
```bash
passrs add -n "Gmail" -u "some@gmail.com" -p $(passrs gen 19 -u -l -s) -s "https://mail.google.com"
```
#### Result
This is the result when search for it
```
$ passrs list -e "mail" -p -s
Master Password: ***************
+-------+-------+----------------+---------------------+-------------------------+
| Index | Name | Username | Password | Service |
+================================================================================+
| 31 | Gmail | some@gmail.com | >NC`q$%+Nno<y&<y]VB | https://mail.google.com |
+-------+-------+----------------+---------------------+-------------------------+
```
<!-- <!--
### Backup ### Backup

66
src/cli/gen_command.rs Normal file
View file

@ -0,0 +1,66 @@
// Local CLI password manager
// Copyright (C) 2024 Awiteb
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// 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>.
use std::num::NonZeroU64;
use clap::Args;
use crate::{password::Passwords, PassrsError, PassrsResult, RunCommand};
#[derive(Debug, Args)]
#[command(author, version, about, long_about = None)]
pub struct Gen {
/// The password length
#[arg(default_value_t = NonZeroU64::new(18).unwrap())]
length: NonZeroU64,
/// With uppercase letters (A-Z)
#[arg(short, long)]
uppercase: bool,
/// With lowercase letters (a-z)
#[arg(short, long)]
lowercase: bool,
/// With numbers (0-9)
#[arg(short, long)]
numbers: bool,
/// With symbols (!,# ...)
#[arg(short, long)]
symbols: bool,
}
impl RunCommand for Gen {
fn run(&self, _password_manager: Passwords) -> PassrsResult<()> {
if self.uppercase || self.lowercase || self.numbers || self.symbols {
println!(
"{}",
passwords::PasswordGenerator::new()
.length(self.length.get() as usize)
.uppercase_letters(self.uppercase)
.lowercase_letters(self.lowercase)
.numbers(self.numbers)
.symbols(self.symbols)
.strict(true)
.generate_one()
.expect("The length cannot be zero")
);
Ok(())
} else {
Err(PassrsError::Other(
"You need to enable at least one kind of characters".to_owned(),
))
}
}
}

View file

@ -51,7 +51,9 @@ pub struct List {
impl RunCommand for List { impl RunCommand for List {
fn run(&self, password_manager: Passwords) -> PassrsResult<()> { fn run(&self, password_manager: Passwords) -> PassrsResult<()> {
if password_manager.passwords.is_empty() { if password_manager.passwords.is_empty() {
println!("Looks like there is no passwords to list") Err(PassrsError::Other(
"Looks like there is no passwords to list".to_owned(),
))
} else { } else {
if self.get.is_some() && self.search.is_some() { if self.get.is_some() && self.search.is_some() {
return Err(PassrsError::ArgsConflict( return Err(PassrsError::ArgsConflict(
@ -132,7 +134,7 @@ impl RunCommand for List {
table.add_row(row); table.add_row(row);
} }
println!("{table}"); println!("{table}");
Ok(())
} }
Ok(())
} }
} }

View file

@ -26,6 +26,7 @@ use crate::{
pub mod add_command; pub mod add_command;
pub mod clean_command; pub mod clean_command;
pub mod edit_command; pub mod edit_command;
pub mod gen_command;
pub mod list_command; pub mod list_command;
crate::create_commands!( crate::create_commands!(
@ -34,6 +35,7 @@ crate::create_commands!(
"List your password and search", List => list_command::List "List your password and search", List => list_command::List
"Clean the password file", Clean => clean_command::Clean "Clean the password file", Clean => clean_command::Clean
"Edit the password content", Edit => edit_command::Edit "Edit the password content", Edit => edit_command::Edit
"Generate password", Gen => gen_command::Gen
// TODO: Remove command // TODO: Remove command
// TODO: Export command // TODO: Export command
// TODO: Import command // TODO: Import command
@ -63,26 +65,33 @@ impl Cli {
"Getting password file: {}", "Getting password file: {}",
passwords_file.to_string_lossy() passwords_file.to_string_lossy()
); );
let password = scanpw::scanpw!("Master Password: "); let password_manager = if matches!(self.command, Commands::Clean(..) | Commands::Gen(..)) {
Passwords {
if password::is_new_password_file(&passwords_file)? { passwords_file,
let analyzed = passwords::analyzer::analyze(&password); ..Default::default()
if analyzed.length() < 15 {
return Err(PassrsError::WeakPassword(
"The password length must be beggier then 15".to_owned(),
));
} else if passwords::scorer::score(&analyzed) < 80.0 {
return Err(PassrsError::WeakPassword(
"Your password is not stronge enough".to_owned(),
));
} }
} } else {
let password = scanpw::scanpw!("Master Password: ");
let master_password = sha256::digest(password); if password::is_new_password_file(&passwords_file)? {
let password_manager = Passwords::try_reload( let analyzed = passwords::analyzer::analyze(&password);
passwords_file, if analyzed.length() < 15 {
master_password.into_bytes().into_iter().take(32).collect(), return Err(PassrsError::WeakPassword(
)?; "The password length must be beggier then 15".to_owned(),
));
} else if passwords::scorer::score(&analyzed) < 80.0 {
return Err(PassrsError::WeakPassword(
"Your password is not stronge enough".to_owned(),
));
}
}
let master_password = sha256::digest(password);
Passwords::try_reload(
passwords_file,
master_password.into_bytes().into_iter().take(32).collect(),
)?
};
self.command.run(password_manager)?; self.command.run(password_manager)?;
Ok(()) Ok(())

View file

@ -27,7 +27,7 @@ mod validator;
pub use validator::*; pub use validator::*;
/// The passwords manager /// The passwords manager
#[derive(Deserialize, Serialize)] #[derive(Default, Deserialize, Serialize)]
pub struct Passwords { pub struct Passwords {
/// Hash of the master password /// Hash of the master password
#[serde(skip)] #[serde(skip)]