From 03bc9d84d692f2957eddf3baaf83ef328b83d597 Mon Sep 17 00:00:00 2001 From: Slack Coder Date: Tue, 3 Jan 2023 15:27:50 -0500 Subject: [PATCH] rad-remote-helper: honor RAD_PASSPHRASE Make testing more convenient by reducing rad-remote-helper's dependency on SSH Agent. It may be opted out of by setting the `RAD_PASSPHRASE` environment variable. Signed-off-by: Slack Coder --- radicle-cli/src/terminal/io.rs | 35 +++++++++++++--------------------- radicle/src/profile.rs | 25 ++++++++++++++++++++---- 2 files changed, 34 insertions(+), 26 deletions(-) diff --git a/radicle-cli/src/terminal/io.rs b/radicle-cli/src/terminal/io.rs index 9802cc78..f8ea60ff 100644 --- a/radicle-cli/src/terminal/io.rs +++ b/radicle-cli/src/terminal/io.rs @@ -7,7 +7,7 @@ use radicle::cob::issue::Issue; use radicle::cob::thread::CommentId; use radicle::crypto::ssh::keystore::Passphrase; use radicle::crypto::Signer; -use radicle::profile::env::RAD_PASSPHRASE; +use radicle::profile; use radicle::profile::Profile; use radicle_crypto::ssh::keystore::MemorySigner; @@ -177,19 +177,16 @@ pub fn abort(prompt: D) -> bool { /// Get the signer. First we try getting it from ssh-agent, otherwise we prompt the user. pub fn signer(profile: &Profile) -> anyhow::Result> { - let signer = if let Ok(passphrase) = read_passphrase_from_env_var() { - MemorySigner::load(&profile.keystore, passphrase)?.boxed() - } else if let Ok(signer) = profile.signer() { - signer.boxed() - } else { - let passphrase = secret_input(); - let spinner = spinner("Unsealing key..."); - let signer = MemorySigner::load(&profile.keystore, passphrase)?; + if let Ok(signer) = profile.signer() { + return Ok(signer); + } - spinner.finish(); - signer.boxed() - }; - Ok(signer) + let passphrase = secret_input(); + let spinner = spinner("Unsealing key..."); + let signer = MemorySigner::load(&profile.keystore, passphrase)?; + + spinner.finish(); + Ok(signer.boxed()) } pub fn theme() -> ColorfulTheme { @@ -313,9 +310,9 @@ pub fn secret_stdin() -> Result { } pub fn read_passphrase(stdin: bool, confirm: bool) -> Result { - let passphrase = match read_passphrase_from_env_var() { - Ok(input) => input, - _ => { + let passphrase = match profile::env::read_passphrase() { + Some(input) => input, + None => { if stdin { secret_stdin()? } else if confirm { @@ -329,12 +326,6 @@ pub fn read_passphrase(stdin: bool, confirm: bool) -> Result Result { - let passphrase = std::env::var(RAD_PASSPHRASE)?; - - Ok(Passphrase::from(passphrase.trim_end().to_owned())) -} - pub fn select<'a, T>(options: &'a [T], active: &'a T) -> Option<&'a T> where T: fmt::Display + Eq + PartialEq, diff --git a/radicle/src/profile.rs b/radicle/src/profile.rs index afa07abf..2b61c387 100644 --- a/radicle/src/profile.rs +++ b/radicle/src/profile.rs @@ -15,13 +15,15 @@ use std::path::{Path, PathBuf}; use thiserror::Error; -use crate::crypto::ssh::agent::{Agent, AgentSigner}; +use crate::crypto::ssh::agent::Agent; use crate::crypto::ssh::{Keystore, Passphrase}; -use crate::crypto::PublicKey; +use crate::crypto::{PublicKey, Signer}; use crate::node; use crate::storage::git::transport; use crate::storage::git::Storage; +use radicle_crypto::ssh::keystore; + /// Environment variables used by radicle. pub mod env { pub use std::env::*; @@ -32,6 +34,14 @@ pub mod env { pub const RAD_SOCKET: &str = "RAD_SOCKET"; /// Passphrase for the encrypted radicle secret key. pub const RAD_PASSPHRASE: &str = "RAD_PASSPHRASE"; + + pub fn read_passphrase() -> Option { + let Ok(passphrase) = std::env::var(RAD_PASSPHRASE) else { + return None; + }; + + Some(super::Passphrase::from(passphrase.trim_end().to_owned())) + } } #[derive(Debug, Error)] @@ -40,6 +50,8 @@ pub enum Error { Io(#[from] io::Error), #[error(transparent)] Keystore(#[from] crate::crypto::ssh::keystore::Error), + #[error(transparent)] + MemorySigner(#[from] keystore::MemorySignerError), #[error("no profile found at the filepath '{0}'")] NotFound(PathBuf), #[error("error connecting to ssh-agent: {0}")] @@ -95,12 +107,17 @@ impl Profile { &self.public_key } - pub fn signer(&self) -> Result { + pub fn signer(&self) -> Result, Error> { + if let Some(passphrase) = env::read_passphrase() { + let signer = keystore::MemorySigner::load(&self.keystore, passphrase)?; + return Ok(signer.boxed()); + } + match Agent::connect() { Ok(agent) => { let signer = agent.signer(self.public_key); if signer.is_ready()? { - Ok(signer) + Ok(signer.boxed()) } else { Err(Error::KeyNotRegistered(self.public_key)) }