commit
be1ab5d527
5 changed files with 117 additions and 21 deletions
19
README.md
19
README.md
|
@ -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
66
src/cli/gen_command.rs
Normal 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(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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(())
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,6 +65,12 @@ impl Cli {
|
||||||
"Getting password file: {}",
|
"Getting password file: {}",
|
||||||
passwords_file.to_string_lossy()
|
passwords_file.to_string_lossy()
|
||||||
);
|
);
|
||||||
|
let password_manager = if matches!(self.command, Commands::Clean(..) | Commands::Gen(..)) {
|
||||||
|
Passwords {
|
||||||
|
passwords_file,
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
let password = scanpw::scanpw!("Master Password: ");
|
let password = scanpw::scanpw!("Master Password: ");
|
||||||
|
|
||||||
if password::is_new_password_file(&passwords_file)? {
|
if password::is_new_password_file(&passwords_file)? {
|
||||||
|
@ -79,10 +87,11 @@ impl Cli {
|
||||||
}
|
}
|
||||||
|
|
||||||
let master_password = sha256::digest(password);
|
let master_password = sha256::digest(password);
|
||||||
let password_manager = Passwords::try_reload(
|
Passwords::try_reload(
|
||||||
passwords_file,
|
passwords_file,
|
||||||
master_password.into_bytes().into_iter().take(32).collect(),
|
master_password.into_bytes().into_iter().take(32).collect(),
|
||||||
)?;
|
)?
|
||||||
|
};
|
||||||
self.command.run(password_manager)?;
|
self.command.run(password_manager)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -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)]
|
||||||
|
|
Loading…
Reference in a new issue