make --email optional, saved on enroll and reused on subsequent runs

This commit is contained in:
2026-03-20 03:02:09 +09:00
parent 38cff6ef55
commit 626a51d2b8

View File

@@ -6,6 +6,9 @@ mod ipc;
mod log;
mod storage;
use std::fs;
use std::path::PathBuf;
use clap::Parser;
use sha2::{Digest, Sha256};
use zeroize::Zeroize;
@@ -18,7 +21,7 @@ use storage::get_backend;
#[command(about = "Bitwarden desktop bridge agent")]
struct Args {
#[arg(long)]
email: String,
email: Option<String>,
#[arg(long)]
password: Option<String>,
@@ -39,6 +42,27 @@ struct Args {
remove: bool,
}
fn config_dir() -> PathBuf {
let home = std::env::var("HOME").unwrap_or_else(|_| "/tmp".into());
PathBuf::from(home).join(".cache").join("com.bitwarden.desktop")
}
fn config_path() -> PathBuf {
config_dir().join("agent.conf")
}
fn save_email(email: &str) {
fs::create_dir_all(config_dir()).ok();
fs::write(config_path(), email).ok();
}
fn load_email() -> Option<String> {
fs::read_to_string(config_path())
.ok()
.map(|s| s.trim().to_string())
.filter(|s| !s.is_empty())
}
fn user_hash(email: &str) -> String {
let mut hasher = Sha256::new();
hasher.update(email.to_lowercase().trim().as_bytes());
@@ -48,7 +72,14 @@ fn user_hash(email: &str) -> String {
fn main() {
let args = Args::parse();
let uid = user_hash(&args.email);
let email = args
.email
.clone()
.or_else(load_email)
.unwrap_or_else(|| log::fatal("no email provided (use --email on first run)"));
let uid = user_hash(&email);
let store = get_backend(args.backend.as_deref());
let prompt = get_prompter(args.askpass.as_deref());
log::info(&format!("backend: {}", store.name()));
@@ -56,9 +87,9 @@ fn main() {
if args.remove {
if store.has_key(&uid) {
store.remove(&uid);
log::info(&format!("key removed for {}", args.email));
log::info(&format!("key removed for {email}"));
} else {
log::info(&format!("no key found for {}", args.email));
log::info(&format!("no key found for {email}"));
}
return;
}
@@ -76,8 +107,8 @@ fn main() {
.or_else(|| prompt("master password:"))
.unwrap_or_else(|| log::fatal("no password provided"));
log::info(&format!("logging in as {}", args.email));
let (mut key_bytes, server_uid) = auth::login(&args.email, &pw, &args.server, &prompt);
log::info(&format!("logging in as {email}"));
let (mut key_bytes, server_uid) = auth::login(&email, &pw, &args.server, &prompt);
log::info(&format!("authenticated, uid={server_uid}"));
let auth = prompt(&format!("choose {} password:", store.name()))
@@ -92,10 +123,11 @@ fn main() {
.store(&uid, &key_bytes, &auth)
.unwrap_or_else(|e| log::fatal(&format!("store failed: {e}")));
key_bytes.zeroize();
save_email(&email);
log::info(&format!("key sealed via {}", store.name()));
log::info("wiped key from memory");
} else {
log::info(&format!("key ready for {}", args.email));
log::info(&format!("key ready for {email}"));
}
let mut bridge = BiometricBridge::new(store, uid, prompt);