cli: Improve passphrase handling

Reduce redundancy in passphrase functions and give better error message
when not using a TTY.
This commit is contained in:
cloudhead 2023-12-11 14:30:51 +01:00
parent ebee41fbf1
commit 87debf380e
No known key found for this signature in database
4 changed files with 30 additions and 22 deletions

View File

@ -171,12 +171,12 @@ pub fn authenticate(options: Options, profile: &Profile) -> anyhow::Result<()> {
term::success!("Radicle key already in ssh-agent");
return Ok(());
}
let validator = term::io::PassphraseValidator::new(profile.keystore.clone());
let passphrase = if options.stdin {
let passphrase = if let Some(phrase) = profile::env::passphrase() {
phrase
} else if options.stdin {
term::passphrase_stdin()?
} else {
term::passphrase(RAD_PASSPHRASE, validator)?
term::io::passphrase(term::io::PassphraseValidator::new(profile.keystore.clone()))?
};
register(&mut agent, profile, passphrase)?;
@ -191,7 +191,7 @@ pub fn authenticate(options: Options, profile: &Profile) -> anyhow::Result<()> {
// Try RAD_PASSPHRASE fallback.
if let Some(passphrase) = profile::env::passphrase() {
ssh::keystore::MemorySigner::load(&profile.keystore, Some(passphrase))
.map_err(|_| anyhow!("RAD_PASSPHRASE failed"))?;
.map_err(|_| anyhow!("`{RAD_PASSPHRASE}` is invalid"))?;
return Ok(());
};

View File

@ -31,7 +31,11 @@ pub fn start(
// Ask passphrase here, otherwise it'll be a fatal error when running the daemon
// without `RAD_PASSPHRASE`.
let validator = term::io::PassphraseValidator::new(profile.keystore.clone());
let Ok(passphrase) = term::io::passphrase(profile::env::RAD_PASSPHRASE, validator) else {
let passphrase = if let Some(phrase) = profile::env::passphrase() {
phrase
} else if let Ok(phrase) = term::io::passphrase(validator) {
phrase
} else {
anyhow::bail!("your radicle passphrase is required to start your node");
};
Some((profile::env::RAD_PASSPHRASE, passphrase))

View File

@ -39,13 +39,22 @@ impl inquire::validator::StringValidator for PassphraseValidator {
}
}
/// Get the signer. First we try getting it from ssh-agent, otherwise we prompt the user.
/// Get the signer. First we try getting it from ssh-agent, otherwise we prompt the user,
/// if we're connected to a TTY.
pub fn signer(profile: &Profile) -> anyhow::Result<Box<dyn Signer>> {
if let Ok(signer) = profile.signer() {
return Ok(signer);
}
let validator = PassphraseValidator::new(profile.keystore.clone());
let passphrase = passphrase(RAD_PASSPHRASE, validator)?;
let passphrase = match passphrase(validator) {
Ok(p) => p,
Err(inquire::InquireError::NotTTY) => {
return Err(anyhow::anyhow!(
"running in non-interactive mode, please set `{RAD_PASSPHRASE}` to unseal your key",
));
}
Err(e) => return Err(e.into()),
};
let spinner = spinner("Unsealing key...");
let signer = MemorySigner::load(&profile.keystore, Some(passphrase))?;

View File

@ -214,22 +214,17 @@ where
Ok(value)
}
pub fn passphrase<K: AsRef<OsStr>, V: validator::StringValidator + 'static>(
var: K,
pub fn passphrase<V: validator::StringValidator + 'static>(
validate: V,
) -> Result<Passphrase, inquire::InquireError> {
if let Ok(p) = env::var(var) {
Ok(Passphrase::from(p))
} else {
Ok(Passphrase::from(
Password::new("Passphrase:")
.with_render_config(*CONFIG)
.with_display_mode(inquire::PasswordDisplayMode::Masked)
.without_confirmation()
.with_validator(validate)
.prompt()?,
))
}
Ok(Passphrase::from(
Password::new("Passphrase:")
.with_render_config(*CONFIG)
.with_display_mode(inquire::PasswordDisplayMode::Masked)
.without_confirmation()
.with_validator(validate)
.prompt()?,
))
}
pub fn passphrase_confirm<K: AsRef<OsStr>>(