Skip to content

Commit 786639a

Browse files
feat: generator command
1 parent e1d1141 commit 786639a

File tree

4 files changed

+115
-6
lines changed

4 files changed

+115
-6
lines changed

crates/bws/src/cli.rs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,49 @@ pub(crate) enum Commands {
8181
#[command(long_about = "Generate shell completion files")]
8282
Completions { shell: Option<Shell> },
8383

84+
/// Generate secrets
85+
Generate {
86+
/// Include lowercase characters
87+
#[arg(long, short = 'l', default_value_t = true, num_args=0..=1)]
88+
include_lowercase: bool,
89+
90+
/// Include uppercase characters
91+
#[arg(long, short = 'U', default_value_t = true, num_args=0..=1)]
92+
include_uppercase: bool,
93+
94+
/// Include numeric characters
95+
#[arg(long, short = 'n', default_value_t = true, num_args=0..=1)]
96+
include_numbers: bool,
97+
98+
/// Length of the secret, up to 255 characters
99+
#[arg(default_value_t = 24, value_parser = clap::value_parser!(u8).range(1..=255))]
100+
length: u8,
101+
102+
/// Include special characters
103+
#[arg(long, short = 's', default_value_t = true, num_args=0..=1)]
104+
include_special: bool,
105+
106+
/// Include ambiguous characters (I, O, l, 0, 1)
107+
#[arg(long, default_value_t = false, num_args=0..=1)]
108+
include_ambiguous: bool,
109+
110+
/// Minimum lowercase characters
111+
#[arg(long)]
112+
min_lowercase: Option<u8>,
113+
114+
/// Minimum uppercase characters
115+
#[arg(long)]
116+
min_uppercase: Option<u8>,
117+
118+
/// Minimum numeric characters
119+
#[arg(long)]
120+
min_number: Option<u8>,
121+
122+
/// Minimum special characters
123+
#[arg(long)]
124+
min_special: Option<u8>,
125+
},
126+
84127
#[command(long_about = "Commands available on Projects")]
85128
Project {
86129
#[command(subcommand)]

crates/bws/src/command/generate.rs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
use bitwarden::{
2+
Client,
3+
generators::{GeneratorClientsExt, PasswordGeneratorRequest},
4+
};
5+
use color_eyre::eyre::Result;
6+
7+
#[allow(clippy::too_many_arguments)]
8+
pub(crate) fn generate_secret(
9+
include_lowercase: bool,
10+
include_uppercase: bool,
11+
include_numbers: bool,
12+
length: u8,
13+
include_special: bool,
14+
include_ambiguous: bool,
15+
min_lowercase: Option<u8>,
16+
min_uppercase: Option<u8>,
17+
min_number: Option<u8>,
18+
min_special: Option<u8>,
19+
) -> Result<()> {
20+
let input = PasswordGeneratorRequest {
21+
lowercase: include_lowercase,
22+
uppercase: include_uppercase,
23+
numbers: include_numbers,
24+
length,
25+
special: include_special,
26+
avoid_ambiguous: !include_ambiguous,
27+
min_lowercase,
28+
min_uppercase,
29+
min_number,
30+
min_special,
31+
};
32+
33+
let generated_secret = Client::new(None).generator().password(input)?;
34+
print!("{generated_secret}");
35+
36+
Ok(())
37+
}

crates/bws/src/command/mod.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
pub(crate) mod generate;
12
pub(crate) mod project;
23
pub(crate) mod run;
34
pub(crate) mod secret;
@@ -7,9 +8,9 @@ use std::{path::PathBuf, str::FromStr};
78
use bitwarden::auth::AccessToken;
89
use clap::CommandFactory;
910
use clap_complete::Shell;
10-
use color_eyre::eyre::{bail, Result};
11+
use color_eyre::eyre::{Result, bail};
1112

12-
use crate::{config, util, Cli, ProfileKey};
13+
use crate::{Cli, ProfileKey, config, util};
1314

1415
pub(crate) fn completions(shell: Option<Shell>) -> Result<()> {
1516
let Some(shell) = shell.or_else(Shell::from_env) else {

crates/bws/src/main.rs

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
use std::{path::PathBuf, str::FromStr};
22

33
use bitwarden::{
4-
auth::{login::AccessTokenLoginRequest, AccessToken},
54
ClientSettings,
5+
auth::{AccessToken, login::AccessTokenLoginRequest},
66
};
77
use bitwarden_cli::install_color_eyre;
88
use clap::{CommandFactory, Parser};
9-
use color_eyre::eyre::{bail, Result};
9+
use color_eyre::eyre::{Result, bail};
1010
use config::Profile;
1111
use log::error;
1212
use render::OutputSettings;
@@ -59,6 +59,31 @@ async fn process_commands() -> Result<()> {
5959
cli.config_file,
6060
);
6161
}
62+
Commands::Generate {
63+
include_lowercase,
64+
include_uppercase,
65+
include_numbers,
66+
length,
67+
include_special,
68+
include_ambiguous,
69+
min_lowercase,
70+
min_uppercase,
71+
min_number,
72+
min_special,
73+
} => {
74+
return command::generate::generate_secret(
75+
include_lowercase,
76+
include_uppercase,
77+
include_numbers,
78+
length,
79+
include_special,
80+
include_ambiguous,
81+
min_lowercase,
82+
min_uppercase,
83+
min_number,
84+
min_special,
85+
);
86+
}
6287
_ => (),
6388
}
6489

@@ -94,7 +119,10 @@ async fn process_commands() -> Result<()> {
94119
) {
95120
Ok(state_file) => Some(state_file),
96121
Err(e) => {
97-
eprintln!("Warning: {}\nRetrieving the state file failed. Attempting to continue without using state. Please set \"state_dir\" in your config file to avoid authentication limits.", e);
122+
eprintln!(
123+
"Warning: {}\nRetrieving the state file failed. Attempting to continue without using state. Please set \"state_dir\" in your config file to avoid authentication limits.",
124+
e
125+
);
98126
None
99127
}
100128
},
@@ -153,7 +181,7 @@ async fn process_commands() -> Result<()> {
153181
std::process::exit(exit_code);
154182
}
155183

156-
Commands::Config { .. } | Commands::Completions { .. } => {
184+
Commands::Config { .. } | Commands::Completions { .. } | Commands::Generate { .. } => {
157185
unreachable!()
158186
}
159187
}

0 commit comments

Comments
 (0)