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 <slackcoder@server.ky>
This commit is contained in:
parent
3682ef195b
commit
03bc9d84d6
|
|
@ -7,7 +7,7 @@ use radicle::cob::issue::Issue;
|
||||||
use radicle::cob::thread::CommentId;
|
use radicle::cob::thread::CommentId;
|
||||||
use radicle::crypto::ssh::keystore::Passphrase;
|
use radicle::crypto::ssh::keystore::Passphrase;
|
||||||
use radicle::crypto::Signer;
|
use radicle::crypto::Signer;
|
||||||
use radicle::profile::env::RAD_PASSPHRASE;
|
use radicle::profile;
|
||||||
use radicle::profile::Profile;
|
use radicle::profile::Profile;
|
||||||
|
|
||||||
use radicle_crypto::ssh::keystore::MemorySigner;
|
use radicle_crypto::ssh::keystore::MemorySigner;
|
||||||
|
|
@ -177,19 +177,16 @@ pub fn abort<D: fmt::Display>(prompt: D) -> bool {
|
||||||
|
|
||||||
/// 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.
|
||||||
pub fn signer(profile: &Profile) -> anyhow::Result<Box<dyn Signer>> {
|
pub fn signer(profile: &Profile) -> anyhow::Result<Box<dyn Signer>> {
|
||||||
let signer = if let Ok(passphrase) = read_passphrase_from_env_var() {
|
if let Ok(signer) = profile.signer() {
|
||||||
MemorySigner::load(&profile.keystore, passphrase)?.boxed()
|
return Ok(signer);
|
||||||
} 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)?;
|
|
||||||
|
|
||||||
spinner.finish();
|
let passphrase = secret_input();
|
||||||
signer.boxed()
|
let spinner = spinner("Unsealing key...");
|
||||||
};
|
let signer = MemorySigner::load(&profile.keystore, passphrase)?;
|
||||||
Ok(signer)
|
|
||||||
|
spinner.finish();
|
||||||
|
Ok(signer.boxed())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn theme() -> ColorfulTheme {
|
pub fn theme() -> ColorfulTheme {
|
||||||
|
|
@ -313,9 +310,9 @@ pub fn secret_stdin() -> Result<Passphrase, anyhow::Error> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read_passphrase(stdin: bool, confirm: bool) -> Result<Passphrase, anyhow::Error> {
|
pub fn read_passphrase(stdin: bool, confirm: bool) -> Result<Passphrase, anyhow::Error> {
|
||||||
let passphrase = match read_passphrase_from_env_var() {
|
let passphrase = match profile::env::read_passphrase() {
|
||||||
Ok(input) => input,
|
Some(input) => input,
|
||||||
_ => {
|
None => {
|
||||||
if stdin {
|
if stdin {
|
||||||
secret_stdin()?
|
secret_stdin()?
|
||||||
} else if confirm {
|
} else if confirm {
|
||||||
|
|
@ -329,12 +326,6 @@ pub fn read_passphrase(stdin: bool, confirm: bool) -> Result<Passphrase, anyhow:
|
||||||
Ok(passphrase)
|
Ok(passphrase)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read_passphrase_from_env_var() -> Result<Passphrase, anyhow::Error> {
|
|
||||||
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>
|
pub fn select<'a, T>(options: &'a [T], active: &'a T) -> Option<&'a T>
|
||||||
where
|
where
|
||||||
T: fmt::Display + Eq + PartialEq,
|
T: fmt::Display + Eq + PartialEq,
|
||||||
|
|
|
||||||
|
|
@ -15,13 +15,15 @@ use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
use thiserror::Error;
|
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::ssh::{Keystore, Passphrase};
|
||||||
use crate::crypto::PublicKey;
|
use crate::crypto::{PublicKey, Signer};
|
||||||
use crate::node;
|
use crate::node;
|
||||||
use crate::storage::git::transport;
|
use crate::storage::git::transport;
|
||||||
use crate::storage::git::Storage;
|
use crate::storage::git::Storage;
|
||||||
|
|
||||||
|
use radicle_crypto::ssh::keystore;
|
||||||
|
|
||||||
/// Environment variables used by radicle.
|
/// Environment variables used by radicle.
|
||||||
pub mod env {
|
pub mod env {
|
||||||
pub use std::env::*;
|
pub use std::env::*;
|
||||||
|
|
@ -32,6 +34,14 @@ pub mod env {
|
||||||
pub const RAD_SOCKET: &str = "RAD_SOCKET";
|
pub const RAD_SOCKET: &str = "RAD_SOCKET";
|
||||||
/// Passphrase for the encrypted radicle secret key.
|
/// Passphrase for the encrypted radicle secret key.
|
||||||
pub const RAD_PASSPHRASE: &str = "RAD_PASSPHRASE";
|
pub const RAD_PASSPHRASE: &str = "RAD_PASSPHRASE";
|
||||||
|
|
||||||
|
pub fn read_passphrase() -> Option<super::Passphrase> {
|
||||||
|
let Ok(passphrase) = std::env::var(RAD_PASSPHRASE) else {
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
|
||||||
|
Some(super::Passphrase::from(passphrase.trim_end().to_owned()))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
|
|
@ -40,6 +50,8 @@ pub enum Error {
|
||||||
Io(#[from] io::Error),
|
Io(#[from] io::Error),
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
Keystore(#[from] crate::crypto::ssh::keystore::Error),
|
Keystore(#[from] crate::crypto::ssh::keystore::Error),
|
||||||
|
#[error(transparent)]
|
||||||
|
MemorySigner(#[from] keystore::MemorySignerError),
|
||||||
#[error("no profile found at the filepath '{0}'")]
|
#[error("no profile found at the filepath '{0}'")]
|
||||||
NotFound(PathBuf),
|
NotFound(PathBuf),
|
||||||
#[error("error connecting to ssh-agent: {0}")]
|
#[error("error connecting to ssh-agent: {0}")]
|
||||||
|
|
@ -95,12 +107,17 @@ impl Profile {
|
||||||
&self.public_key
|
&self.public_key
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn signer(&self) -> Result<AgentSigner, Error> {
|
pub fn signer(&self) -> Result<Box<dyn Signer>, Error> {
|
||||||
|
if let Some(passphrase) = env::read_passphrase() {
|
||||||
|
let signer = keystore::MemorySigner::load(&self.keystore, passphrase)?;
|
||||||
|
return Ok(signer.boxed());
|
||||||
|
}
|
||||||
|
|
||||||
match Agent::connect() {
|
match Agent::connect() {
|
||||||
Ok(agent) => {
|
Ok(agent) => {
|
||||||
let signer = agent.signer(self.public_key);
|
let signer = agent.signer(self.public_key);
|
||||||
if signer.is_ready()? {
|
if signer.is_ready()? {
|
||||||
Ok(signer)
|
Ok(signer.boxed())
|
||||||
} else {
|
} else {
|
||||||
Err(Error::KeyNotRegistered(self.public_key))
|
Err(Error::KeyNotRegistered(self.public_key))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue