cli: Give passphrase error on prompt
Example:
✗ Invalid passphrase, please try again
? Passphrase: ***
This commit is contained in:
parent
5040f33025
commit
579ddfe725
|
|
@ -177,10 +177,11 @@ pub fn authenticate(options: Options, profile: &Profile) -> anyhow::Result<()> {
|
|||
term::format::Identity::new(profile).styled()
|
||||
);
|
||||
|
||||
let validator = term::io::PassphraseValidator::new(profile.keystore.clone());
|
||||
let passphrase = if options.stdin {
|
||||
term::passphrase_stdin()?
|
||||
} else {
|
||||
term::passphrase(RAD_PASSPHRASE)?
|
||||
term::passphrase(RAD_PASSPHRASE, validator)?
|
||||
};
|
||||
register(&mut agent, profile, passphrase)?;
|
||||
|
||||
|
|
|
|||
|
|
@ -30,7 +30,8 @@ pub fn start(
|
|||
let envs = if profile.keystore.is_encrypted()? {
|
||||
// Ask passphrase here, otherwise it'll be a fatal error when running the daemon
|
||||
// without `RAD_PASSPHRASE`.
|
||||
let Ok(passphrase) = term::io::passphrase(profile::env::RAD_PASSPHRASE) else {
|
||||
let validator = term::io::PassphraseValidator::new(profile.keystore.clone());
|
||||
let Ok(passphrase) = term::io::passphrase(profile::env::RAD_PASSPHRASE, validator) else {
|
||||
anyhow::bail!("your radicle passphrase is required to start your node");
|
||||
};
|
||||
Some((profile::env::RAD_PASSPHRASE, passphrase))
|
||||
|
|
|
|||
|
|
@ -1,19 +1,51 @@
|
|||
use radicle::cob::issue::Issue;
|
||||
use radicle::cob::thread::{Comment, CommentId};
|
||||
use radicle::crypto::ssh::keystore::MemorySigner;
|
||||
use radicle::crypto::Signer;
|
||||
use radicle::crypto::{ssh::Keystore, Signer};
|
||||
use radicle::profile::env::RAD_PASSPHRASE;
|
||||
use radicle::profile::Profile;
|
||||
|
||||
pub use radicle_term::io::*;
|
||||
pub use radicle_term::spinner;
|
||||
|
||||
use inquire::validator;
|
||||
|
||||
/// Validates secret key passphrases.
|
||||
#[derive(Clone)]
|
||||
pub struct PassphraseValidator {
|
||||
keystore: Keystore,
|
||||
}
|
||||
|
||||
impl PassphraseValidator {
|
||||
/// Create a new validator.
|
||||
pub fn new(keystore: Keystore) -> Self {
|
||||
Self { keystore }
|
||||
}
|
||||
}
|
||||
|
||||
impl inquire::validator::StringValidator for PassphraseValidator {
|
||||
fn validate(
|
||||
&self,
|
||||
input: &str,
|
||||
) -> Result<validator::Validation, inquire::error::CustomUserError> {
|
||||
let passphrase = Passphrase::from(input.to_owned());
|
||||
if self.keystore.is_valid_passphrase(&passphrase)? {
|
||||
Ok(validator::Validation::Valid)
|
||||
} else {
|
||||
Ok(validator::Validation::Invalid(
|
||||
validator::ErrorMessage::from("Invalid passphrase, please try again"),
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the signer. First we try getting it from ssh-agent, otherwise we prompt the user.
|
||||
pub fn signer(profile: &Profile) -> anyhow::Result<Box<dyn Signer>> {
|
||||
if let Ok(signer) = profile.signer() {
|
||||
return Ok(signer);
|
||||
}
|
||||
let passphrase = passphrase(RAD_PASSPHRASE)?;
|
||||
let validator = PassphraseValidator::new(profile.keystore.clone());
|
||||
let passphrase = passphrase(RAD_PASSPHRASE, validator)?;
|
||||
let spinner = spinner("Unsealing key...");
|
||||
let signer = MemorySigner::load(&profile.keystore, Some(passphrase))?;
|
||||
|
||||
|
|
|
|||
|
|
@ -138,6 +138,19 @@ impl Keystore {
|
|||
}
|
||||
}
|
||||
|
||||
/// Check that the passphrase is valid.
|
||||
pub fn is_valid_passphrase(&self, passphrase: &Passphrase) -> Result<bool, Error> {
|
||||
let path = self.path.join("radicle");
|
||||
if !path.exists() {
|
||||
return Err(Error::Io(io::ErrorKind::NotFound.into()));
|
||||
}
|
||||
|
||||
let secret = ssh_key::PrivateKey::read_openssh_file(&path)?;
|
||||
let valid = secret.decrypt(passphrase).is_ok();
|
||||
|
||||
Ok(valid)
|
||||
}
|
||||
|
||||
/// Check whether the secret key is encrypted.
|
||||
pub fn is_encrypted(&self) -> Result<bool, Error> {
|
||||
let path = self.path.join("radicle");
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ use std::ffi::OsStr;
|
|||
use std::{env, fmt, io, process};
|
||||
|
||||
use inquire::ui::{ErrorMessageRenderConfig, StyleSheet, Styled};
|
||||
use inquire::validator;
|
||||
use inquire::InquireError;
|
||||
use inquire::{ui::Color, ui::RenderConfig, Confirm, CustomType, Password};
|
||||
use once_cell::sync::Lazy;
|
||||
|
|
@ -11,7 +12,7 @@ use crate::command;
|
|||
use crate::format;
|
||||
use crate::{style, Paint};
|
||||
|
||||
// TODO: Try not to export this.
|
||||
pub use inquire;
|
||||
pub use inquire::Select;
|
||||
|
||||
pub const ERROR_PREFIX: Paint<&str> = Paint::red("✗");
|
||||
|
|
@ -199,7 +200,10 @@ where
|
|||
Ok(value)
|
||||
}
|
||||
|
||||
pub fn passphrase<K: AsRef<OsStr>>(var: K) -> Result<Passphrase, inquire::InquireError> {
|
||||
pub fn passphrase<K: AsRef<OsStr>, V: validator::StringValidator + 'static>(
|
||||
var: K,
|
||||
validate: V,
|
||||
) -> Result<Passphrase, inquire::InquireError> {
|
||||
if let Ok(p) = env::var(var) {
|
||||
Ok(Passphrase::from(p))
|
||||
} else {
|
||||
|
|
@ -208,6 +212,7 @@ pub fn passphrase<K: AsRef<OsStr>>(var: K) -> Result<Passphrase, inquire::Inquir
|
|||
.with_render_config(*CONFIG)
|
||||
.with_display_mode(inquire::PasswordDisplayMode::Masked)
|
||||
.without_confirmation()
|
||||
.with_validator(validate)
|
||||
.prompt()?,
|
||||
))
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue