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"); term::success!("Radicle key already in ssh-agent");
return Ok(()); return Ok(());
} }
let passphrase = if let Some(phrase) = profile::env::passphrase() {
let validator = term::io::PassphraseValidator::new(profile.keystore.clone()); phrase
let passphrase = if options.stdin { } else if options.stdin {
term::passphrase_stdin()? term::passphrase_stdin()?
} else { } else {
term::passphrase(RAD_PASSPHRASE, validator)? term::io::passphrase(term::io::PassphraseValidator::new(profile.keystore.clone()))?
}; };
register(&mut agent, profile, passphrase)?; register(&mut agent, profile, passphrase)?;
@ -191,7 +191,7 @@ pub fn authenticate(options: Options, profile: &Profile) -> anyhow::Result<()> {
// Try RAD_PASSPHRASE fallback. // Try RAD_PASSPHRASE fallback.
if let Some(passphrase) = profile::env::passphrase() { if let Some(passphrase) = profile::env::passphrase() {
ssh::keystore::MemorySigner::load(&profile.keystore, Some(passphrase)) ssh::keystore::MemorySigner::load(&profile.keystore, Some(passphrase))
.map_err(|_| anyhow!("RAD_PASSPHRASE failed"))?; .map_err(|_| anyhow!("`{RAD_PASSPHRASE}` is invalid"))?;
return Ok(()); 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 // Ask passphrase here, otherwise it'll be a fatal error when running the daemon
// without `RAD_PASSPHRASE`. // without `RAD_PASSPHRASE`.
let validator = term::io::PassphraseValidator::new(profile.keystore.clone()); 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"); anyhow::bail!("your radicle passphrase is required to start your node");
}; };
Some((profile::env::RAD_PASSPHRASE, passphrase)) 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>> { pub fn signer(profile: &Profile) -> anyhow::Result<Box<dyn Signer>> {
if let Ok(signer) = profile.signer() { if let Ok(signer) = profile.signer() {
return Ok(signer); return Ok(signer);
} }
let validator = PassphraseValidator::new(profile.keystore.clone()); 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 spinner = spinner("Unsealing key...");
let signer = MemorySigner::load(&profile.keystore, Some(passphrase))?; let signer = MemorySigner::load(&profile.keystore, Some(passphrase))?;

View File

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