refactor(cli): move cmd_generate + cmd_rate into commands/
This commit is contained in:
68
crates/relicario-cli/src/commands/generate.rs
Normal file
68
crates/relicario-cli/src/commands/generate.rs
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
//! `relicario generate` — emit a fresh password or BIP39 passphrase.
|
||||||
|
|
||||||
|
use anyhow::Result;
|
||||||
|
|
||||||
|
pub fn cmd_generate(
|
||||||
|
length: Option<u32>,
|
||||||
|
bip39: bool,
|
||||||
|
words: Option<u32>,
|
||||||
|
symbols: Option<String>,
|
||||||
|
separator: Option<String>,
|
||||||
|
) -> Result<()> {
|
||||||
|
use relicario_core::{
|
||||||
|
generate_passphrase, generate_password, Capitalization, CharClasses,
|
||||||
|
GeneratorRequest, SymbolCharset,
|
||||||
|
};
|
||||||
|
|
||||||
|
// If we're inside a vault, unlock and pull `generator_defaults`. Outside
|
||||||
|
// a vault, this stays a fast standalone CSPRNG tool (no unlock prompt).
|
||||||
|
let vault_defaults: Option<GeneratorRequest> = if crate::helpers::vault_dir().is_ok() {
|
||||||
|
let vault = crate::session::UnlockedVault::unlock_interactive()?;
|
||||||
|
Some(vault.load_settings()?.generator_defaults)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
// `--bip39` flag forces Bip39 mode; otherwise use whatever mode the
|
||||||
|
// vault default is in (Random when no vault).
|
||||||
|
let use_bip39 = bip39 || matches!(vault_defaults, Some(GeneratorRequest::Bip39 { .. }));
|
||||||
|
|
||||||
|
let output = if use_bip39 {
|
||||||
|
let (def_words, def_sep, def_cap) = match &vault_defaults {
|
||||||
|
Some(GeneratorRequest::Bip39 { word_count, separator, capitalization }) => {
|
||||||
|
(*word_count, separator.clone(), *capitalization)
|
||||||
|
}
|
||||||
|
_ => (5, " ".to_string(), Capitalization::Lower),
|
||||||
|
};
|
||||||
|
generate_passphrase(&GeneratorRequest::Bip39 {
|
||||||
|
word_count: words.unwrap_or(def_words),
|
||||||
|
separator: separator.unwrap_or(def_sep),
|
||||||
|
capitalization: def_cap,
|
||||||
|
})?
|
||||||
|
} else {
|
||||||
|
let (def_length, def_classes, def_charset) = match &vault_defaults {
|
||||||
|
Some(GeneratorRequest::Random { length, classes, symbol_charset }) => {
|
||||||
|
(*length, *classes, symbol_charset.clone())
|
||||||
|
}
|
||||||
|
_ => (
|
||||||
|
20,
|
||||||
|
CharClasses { lower: true, upper: true, digits: true, symbols: true },
|
||||||
|
SymbolCharset::SafeOnly,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
let symbol_charset = match symbols.as_deref() {
|
||||||
|
None => def_charset,
|
||||||
|
Some("safe") => SymbolCharset::SafeOnly,
|
||||||
|
Some("extended") => SymbolCharset::Extended,
|
||||||
|
Some(other) => SymbolCharset::Custom(other.to_string()),
|
||||||
|
};
|
||||||
|
generate_password(&GeneratorRequest::Random {
|
||||||
|
length: length.unwrap_or(def_length),
|
||||||
|
classes: def_classes,
|
||||||
|
symbol_charset,
|
||||||
|
})?
|
||||||
|
};
|
||||||
|
|
||||||
|
println!("{}", output.as_str());
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
@@ -5,3 +5,6 @@
|
|||||||
//! command modules (e.g. `commit_paths`, `resolve_query`) are defined in
|
//! command modules (e.g. `commit_paths`, `resolve_query`) are defined in
|
||||||
//! this file as `pub(crate)` so siblings can pull them in via
|
//! this file as `pub(crate)` so siblings can pull them in via
|
||||||
//! `use crate::commands::*`.
|
//! `use crate::commands::*`.
|
||||||
|
|
||||||
|
pub mod generate;
|
||||||
|
pub mod rate;
|
||||||
|
|||||||
28
crates/relicario-cli/src/commands/rate.rs
Normal file
28
crates/relicario-cli/src/commands/rate.rs
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
//! `relicario rate` — score a passphrase via zxcvbn.
|
||||||
|
|
||||||
|
use anyhow::Result;
|
||||||
|
|
||||||
|
pub fn cmd_rate(passphrase: String) -> Result<()> {
|
||||||
|
let pw: String = if passphrase == "-" {
|
||||||
|
use std::io::BufRead;
|
||||||
|
let stdin = std::io::stdin();
|
||||||
|
let mut line = String::new();
|
||||||
|
stdin.lock().read_line(&mut line)?;
|
||||||
|
line.trim_end_matches(&['\r', '\n'][..]).to_string()
|
||||||
|
} else {
|
||||||
|
passphrase
|
||||||
|
};
|
||||||
|
let est = relicario_core::generators::rate_passphrase(&pw);
|
||||||
|
let label = match est.score {
|
||||||
|
0 => "very weak",
|
||||||
|
1 => "weak",
|
||||||
|
2 => "fair",
|
||||||
|
3 => "good",
|
||||||
|
4 => "strong",
|
||||||
|
_ => "?",
|
||||||
|
};
|
||||||
|
println!("score: {}/4 ({})", est.score, label);
|
||||||
|
println!("guesses: ~10^{:.1}", est.guesses_log10);
|
||||||
|
println!("note: init requires score ≥ 3 (see `relicario init`)");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
@@ -443,7 +443,7 @@ fn main() -> Result<()> {
|
|||||||
Commands::Extract { query, aid, out } => cmd_extract(query, aid, out),
|
Commands::Extract { query, aid, out } => cmd_extract(query, aid, out),
|
||||||
Commands::Detach { query, aid } => cmd_detach(query, aid),
|
Commands::Detach { query, aid } => cmd_detach(query, aid),
|
||||||
Commands::Generate { length, bip39, words, symbols, separator } => {
|
Commands::Generate { length, bip39, words, symbols, separator } => {
|
||||||
cmd_generate(length, bip39, words, symbols, separator)
|
commands::generate::cmd_generate(length, bip39, words, symbols, separator)
|
||||||
}
|
}
|
||||||
Commands::Settings { action } => cmd_settings(action),
|
Commands::Settings { action } => cmd_settings(action),
|
||||||
Commands::Sync => cmd_sync(),
|
Commands::Sync => cmd_sync(),
|
||||||
@@ -454,7 +454,7 @@ fn main() -> Result<()> {
|
|||||||
generate(shell, &mut cmd, "relicario", &mut std::io::stdout());
|
generate(shell, &mut cmd, "relicario", &mut std::io::stdout());
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
Commands::Rate { passphrase } => cmd_rate(passphrase),
|
Commands::Rate { passphrase } => commands::rate::cmd_rate(passphrase),
|
||||||
Commands::Device { action } => cmd_device(action),
|
Commands::Device { action } => cmd_device(action),
|
||||||
Commands::RecoveryQr { cmd } => cmd_recovery_qr(cmd),
|
Commands::RecoveryQr { cmd } => cmd_recovery_qr(cmd),
|
||||||
}
|
}
|
||||||
@@ -1979,70 +1979,6 @@ fn cmd_detach(query: String, aid: String) -> Result<()> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cmd_generate(
|
|
||||||
length: Option<u32>,
|
|
||||||
bip39: bool,
|
|
||||||
words: Option<u32>,
|
|
||||||
symbols: Option<String>,
|
|
||||||
separator: Option<String>,
|
|
||||||
) -> Result<()> {
|
|
||||||
use relicario_core::{
|
|
||||||
generate_passphrase, generate_password, Capitalization, CharClasses,
|
|
||||||
GeneratorRequest, SymbolCharset,
|
|
||||||
};
|
|
||||||
|
|
||||||
// If we're inside a vault, unlock and pull `generator_defaults`. Outside
|
|
||||||
// a vault, this stays a fast standalone CSPRNG tool (no unlock prompt).
|
|
||||||
let vault_defaults: Option<GeneratorRequest> = if crate::helpers::vault_dir().is_ok() {
|
|
||||||
let vault = crate::session::UnlockedVault::unlock_interactive()?;
|
|
||||||
Some(vault.load_settings()?.generator_defaults)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
// `--bip39` flag forces Bip39 mode; otherwise use whatever mode the
|
|
||||||
// vault default is in (Random when no vault).
|
|
||||||
let use_bip39 = bip39 || matches!(vault_defaults, Some(GeneratorRequest::Bip39 { .. }));
|
|
||||||
|
|
||||||
let output = if use_bip39 {
|
|
||||||
let (def_words, def_sep, def_cap) = match &vault_defaults {
|
|
||||||
Some(GeneratorRequest::Bip39 { word_count, separator, capitalization }) => {
|
|
||||||
(*word_count, separator.clone(), *capitalization)
|
|
||||||
}
|
|
||||||
_ => (5, " ".to_string(), Capitalization::Lower),
|
|
||||||
};
|
|
||||||
generate_passphrase(&GeneratorRequest::Bip39 {
|
|
||||||
word_count: words.unwrap_or(def_words),
|
|
||||||
separator: separator.unwrap_or(def_sep),
|
|
||||||
capitalization: def_cap,
|
|
||||||
})?
|
|
||||||
} else {
|
|
||||||
let (def_length, def_classes, def_charset) = match &vault_defaults {
|
|
||||||
Some(GeneratorRequest::Random { length, classes, symbol_charset }) => {
|
|
||||||
(*length, *classes, symbol_charset.clone())
|
|
||||||
}
|
|
||||||
_ => (
|
|
||||||
20,
|
|
||||||
CharClasses { lower: true, upper: true, digits: true, symbols: true },
|
|
||||||
SymbolCharset::SafeOnly,
|
|
||||||
),
|
|
||||||
};
|
|
||||||
let symbol_charset = match symbols.as_deref() {
|
|
||||||
None => def_charset,
|
|
||||||
Some("safe") => SymbolCharset::SafeOnly,
|
|
||||||
Some("extended") => SymbolCharset::Extended,
|
|
||||||
Some(other) => SymbolCharset::Custom(other.to_string()),
|
|
||||||
};
|
|
||||||
generate_password(&GeneratorRequest::Random {
|
|
||||||
length: length.unwrap_or(def_length),
|
|
||||||
classes: def_classes,
|
|
||||||
symbol_charset,
|
|
||||||
})?
|
|
||||||
};
|
|
||||||
|
|
||||||
println!("{}", output.as_str());
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
fn cmd_settings(action: SettingsAction) -> Result<()> {
|
fn cmd_settings(action: SettingsAction) -> Result<()> {
|
||||||
use relicario_core::{
|
use relicario_core::{
|
||||||
Capitalization, CharClasses, GeneratorRequest, HistoryRetention,
|
Capitalization, CharClasses, GeneratorRequest, HistoryRetention,
|
||||||
@@ -2210,31 +2146,6 @@ struct ParamsKdf {
|
|||||||
argon2_p: u32,
|
argon2_p: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cmd_rate(passphrase: String) -> Result<()> {
|
|
||||||
let pw: String = if passphrase == "-" {
|
|
||||||
use std::io::BufRead;
|
|
||||||
let stdin = std::io::stdin();
|
|
||||||
let mut line = String::new();
|
|
||||||
stdin.lock().read_line(&mut line)?;
|
|
||||||
line.trim_end_matches(&['\r', '\n'][..]).to_string()
|
|
||||||
} else {
|
|
||||||
passphrase
|
|
||||||
};
|
|
||||||
let est = relicario_core::generators::rate_passphrase(&pw);
|
|
||||||
let label = match est.score {
|
|
||||||
0 => "very weak",
|
|
||||||
1 => "weak",
|
|
||||||
2 => "fair",
|
|
||||||
3 => "good",
|
|
||||||
4 => "strong",
|
|
||||||
_ => "?",
|
|
||||||
};
|
|
||||||
println!("score: {}/4 ({})", est.score, label);
|
|
||||||
println!("guesses: ~10^{:.1}", est.guesses_log10);
|
|
||||||
println!("note: init requires score ≥ 3 (see `relicario init`)");
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── Device management ─────────────────────────────────────────────────────────
|
// ── Device management ─────────────────────────────────────────────────────────
|
||||||
|
|
||||||
/// Build a `GiteaClient` from flags or environment variables.
|
/// Build a `GiteaClient` from flags or environment variables.
|
||||||
|
|||||||
Reference in New Issue
Block a user