use anyhow::Result;
use colored::Colorize;
use crate::cli::BiometricAction;
use tsafe_core::{errors::SafeError, keyring_store, profile};
use zeroize::Zeroizing;
use crate::helpers::*;
pub fn retrieve_biometric_password(
profile_name: &str,
) -> Result<Option<Zeroizing<String>>, SafeError> {
keyring_store::retrieve_password(profile_name).map(|opt| opt.map(Zeroizing::new))
}
pub fn classify_biometric_unlock_error(e: SafeError) -> SafeError {
let msg = e.to_string();
if keyring_store::looks_like_stale_credential(&msg) {
return SafeError::StaleBiometricCredential;
}
e
}
pub(crate) fn cmd_biometric(profile: &str, action: BiometricAction) -> Result<()> {
match action {
BiometricAction::Enable => biometric_enable(profile),
BiometricAction::Disable => biometric_disable(profile),
BiometricAction::Status => biometric_status(profile),
BiometricAction::ReEnroll => biometric_re_enroll(profile),
}
}
fn biometric_enable(profile: &str) -> Result<()> {
let password = prompt_password(&format!("Password for profile '{profile}': "))?;
let path = profile::vault_path(profile);
tsafe_core::vault::Vault::open(&path, password.as_bytes())
.map_err(|e| anyhow::anyhow!("{e}"))?;
keyring_store::store_password(profile, &password).map_err(|e| anyhow::anyhow!("{e}"))?;
println!(
"{} Biometric/keyring unlock enabled for '{}'",
"✓".green(),
profile
);
println!(" Your vault password is now stored in the OS credential store.");
Ok(())
}
fn biometric_disable(profile: &str) -> Result<()> {
keyring_store::remove_password(profile).map_err(|e| anyhow::anyhow!("{e}"))?;
println!(
"{} Biometric/keyring unlock disabled for '{}'",
"✓".green(),
profile
);
Ok(())
}
fn biometric_status(profile: &str) -> Result<()> {
if keyring_store::has_password(profile) {
println!(
"{} Biometric/keyring unlock is {} for '{}'",
"✓".green(),
"enabled".green(),
profile
);
if let Some(note) = keyring_store::quick_unlock_storage_note(profile) {
println!(" {note}");
}
} else {
println!(
" Biometric/keyring unlock is {} for '{}'",
"not configured".yellow(),
profile
);
println!(" Run {} to enable.", "tsafe biometric enable".cyan());
}
Ok(())
}
fn biometric_re_enroll(profile: &str) -> Result<()> {
let _ = keyring_store::remove_password(profile);
println!(
"{} Stale biometric credential removed for '{}'.",
"i".cyan(),
profile
);
println!(" Enter the current vault password to re-enroll:");
let password = prompt_password(&format!("Password for profile '{profile}': "))?;
let path = profile::vault_path(profile);
tsafe_core::vault::Vault::open(&path, password.as_bytes()).map_err(|e| {
anyhow::anyhow!(
"password verification failed — the vault could not be opened: {e}\n\
Hint: use the current master password, not the old one."
)
})?;
keyring_store::store_password(profile, &password).map_err(|e| anyhow::anyhow!("{e}"))?;
println!(
"{} Biometric/keyring unlock re-enrolled for '{}'. Password-free unlock is restored.",
"✓".green(),
profile
);
Ok(())
}