Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions crates/bws/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,49 @@ pub(crate) enum Commands {
#[command(long_about = "Generate shell completion files")]
Completions { shell: Option<Shell> },

/// Generate secrets
Generate {
/// Include lowercase characters
#[arg(long, short = 'l', default_value_t = true, num_args=0..=1)]
include_lowercase: bool,

/// Include uppercase characters
#[arg(long, short = 'U', default_value_t = true, num_args=0..=1)]
include_uppercase: bool,

/// Include numeric characters
#[arg(long, short = 'n', default_value_t = true, num_args=0..=1)]
include_numbers: bool,

/// Length of the secret, up to 255 characters
#[arg(default_value_t = 24, value_parser = clap::value_parser!(u8).range(1..=255))]
length: u8,

/// Include special characters
#[arg(long, short = 's', default_value_t = true, num_args=0..=1)]
include_special: bool,

/// Include ambiguous characters (I, O, l, 0, 1)
#[arg(long, default_value_t = false, num_args=0..=1)]
include_ambiguous: bool,

/// Minimum lowercase characters
#[arg(long)]
min_lowercase: Option<u8>,

/// Minimum uppercase characters
#[arg(long)]
min_uppercase: Option<u8>,

/// Minimum numeric characters
#[arg(long)]
min_number: Option<u8>,

/// Minimum special characters
#[arg(long)]
min_special: Option<u8>,
},

#[command(long_about = "Commands available on Projects")]
Project {
#[command(subcommand)]
Expand Down
37 changes: 37 additions & 0 deletions crates/bws/src/command/generate.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
use bitwarden::{
Client,
generators::{GeneratorClientsExt, PasswordGeneratorRequest},
};
use color_eyre::eyre::Result;

#[allow(clippy::too_many_arguments)]
pub(crate) fn generate_secret(
include_lowercase: bool,
include_uppercase: bool,
include_numbers: bool,
length: u8,
include_special: bool,
include_ambiguous: bool,
min_lowercase: Option<u8>,
min_uppercase: Option<u8>,
min_number: Option<u8>,
min_special: Option<u8>,
) -> Result<()> {
let input = PasswordGeneratorRequest {
lowercase: include_lowercase,
uppercase: include_uppercase,
numbers: include_numbers,
length,
special: include_special,
avoid_ambiguous: !include_ambiguous,
min_lowercase,
min_uppercase,
min_number,
min_special,
};

let generated_secret = Client::new(None).generator().password(input)?;
print!("{generated_secret}");

Ok(())
}
5 changes: 3 additions & 2 deletions crates/bws/src/command/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
pub(crate) mod generate;
pub(crate) mod project;
pub(crate) mod run;
pub(crate) mod secret;
Expand All @@ -7,9 +8,9 @@ use std::{path::PathBuf, str::FromStr};
use bitwarden::auth::AccessToken;
use clap::CommandFactory;
use clap_complete::Shell;
use color_eyre::eyre::{bail, Result};
use color_eyre::eyre::{Result, bail};

use crate::{config, util, Cli, ProfileKey};
use crate::{Cli, ProfileKey, config, util};

pub(crate) fn completions(shell: Option<Shell>) -> Result<()> {
let Some(shell) = shell.or_else(Shell::from_env) else {
Expand Down
36 changes: 32 additions & 4 deletions crates/bws/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
use std::{path::PathBuf, str::FromStr};

use bitwarden::{
auth::{login::AccessTokenLoginRequest, AccessToken},
ClientSettings,
auth::{AccessToken, login::AccessTokenLoginRequest},
};
use bitwarden_cli::install_color_eyre;
use clap::{CommandFactory, Parser};
use color_eyre::eyre::{bail, Result};
use color_eyre::eyre::{Result, bail};
use config::Profile;
use log::error;
use render::OutputSettings;
Expand Down Expand Up @@ -59,6 +59,31 @@ async fn process_commands() -> Result<()> {
cli.config_file,
);
}
Commands::Generate {
include_lowercase,
include_uppercase,
include_numbers,
length,
include_special,
include_ambiguous,
min_lowercase,
min_uppercase,
min_number,
min_special,
} => {
return command::generate::generate_secret(
include_lowercase,
include_uppercase,
include_numbers,
length,
include_special,
include_ambiguous,
min_lowercase,
min_uppercase,
min_number,
min_special,
);
}
_ => (),
}

Expand Down Expand Up @@ -94,7 +119,10 @@ async fn process_commands() -> Result<()> {
) {
Ok(state_file) => Some(state_file),
Err(e) => {
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);
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
);
None
}
},
Expand Down Expand Up @@ -153,7 +181,7 @@ async fn process_commands() -> Result<()> {
std::process::exit(exit_code);
}

Commands::Config { .. } | Commands::Completions { .. } => {
Commands::Config { .. } | Commands::Completions { .. } | Commands::Generate { .. } => {
unreachable!()
}
}
Expand Down
Loading