cli: Register key on first `auth`

Registers the secret key with ssh-agent when `rad auth` is first called.
This commit is contained in:
Alexis Sellier 2023-02-28 09:20:08 +01:00
parent 411c54a604
commit e4b44370d3
No known key found for this signature in database
4 changed files with 42 additions and 9 deletions

View File

@ -7,6 +7,7 @@ $ rad auth
Initializing your 🌱 profile and identity Initializing your 🌱 profile and identity
✓ Creating your 🌱 Ed25519 keypair... ✓ Creating your 🌱 Ed25519 keypair...
! Adding your radicle key to ssh-agent...
✓ Your Radicle ID is did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi. This identifies your device. ✓ Your Radicle ID is did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi. This identifies your device.
👉 To create a radicle project, run `rad init` from a git repository. 👉 To create a radicle project, run `rad init` from a git repository.

View File

@ -4,6 +4,7 @@ use std::ffi::OsString;
use anyhow::anyhow; use anyhow::anyhow;
use radicle::crypto::ssh; use radicle::crypto::ssh;
use radicle::crypto::ssh::Passphrase;
use radicle::{profile, Profile}; use radicle::{profile, Profile};
use crate::terminal as term; use crate::terminal as term;
@ -86,9 +87,16 @@ pub fn init(options: Options) -> anyhow::Result<()> {
term::passphrase_confirm() term::passphrase_confirm()
}?; }?;
let spinner = term::spinner("Creating your 🌱 Ed25519 keypair..."); let spinner = term::spinner("Creating your 🌱 Ed25519 keypair...");
let profile = Profile::init(home, passphrase)?; let profile = Profile::init(home, passphrase.clone())?;
spinner.finish(); spinner.finish();
let spinner = term::spinner("Adding your radicle key to ssh-agent...");
if register(&profile, passphrase).is_ok() {
spinner.finish();
} else {
spinner.warn();
}
term::success!( term::success!(
"Your Radicle ID is {}. This identifies your device.", "Your Radicle ID is {}. This identifies your device.",
term::format::highlight(profile.did()) term::format::highlight(profile.did())
@ -124,12 +132,7 @@ pub fn authenticate(profile: &Profile, options: Options) -> anyhow::Result<()> {
term::passphrase() term::passphrase()
}?; }?;
let spinner = term::spinner("Unlocking..."); let spinner = term::spinner("Unlocking...");
let mut agent = ssh::agent::Agent::connect()?; register(profile, passphrase)?;
let secret = profile
.keystore
.secret_key(passphrase)?
.ok_or_else(|| anyhow!("Key not found in {:?}", profile.keystore.path()))?;
agent.register(&secret)?;
spinner.finish(); spinner.finish();
term::success!("Radicle key added to ssh-agent"); term::success!("Radicle key added to ssh-agent");
@ -139,3 +142,16 @@ pub fn authenticate(profile: &Profile, options: Options) -> anyhow::Result<()> {
Ok(()) Ok(())
} }
/// Register key with ssh-agent.
pub fn register(profile: &Profile, passphrase: Passphrase) -> anyhow::Result<()> {
let mut agent = ssh::agent::Agent::connect()?;
let secret = profile
.keystore
.secret_key(passphrase)?
.ok_or_else(|| anyhow!("Key not found in {:?}", profile.keystore.path()))?;
agent.register(&secret)?;
Ok(())
}

View File

@ -19,6 +19,7 @@ use super::Error;
use super::{style, Paint}; use super::{style, Paint};
pub const ERROR_PREFIX: Paint<&str> = Paint::red(""); pub const ERROR_PREFIX: Paint<&str> = Paint::red("");
pub const WARNING_PREFIX: Paint<&str> = Paint::yellow("!");
pub const TAB: &str = " "; pub const TAB: &str = " ";
/// Render configuration. /// Render configuration.
@ -129,7 +130,7 @@ pub fn subcommand(msg: impl fmt::Display) {
pub fn warning(warning: &str) { pub fn warning(warning: &str) {
println!( println!(
"{} {} {warning}", "{} {} {warning}",
Paint::yellow("!"), WARNING_PREFIX,
Paint::yellow("Warning:").bold(), Paint::yellow("Warning:").bold(),
); );
} }

View File

@ -3,7 +3,7 @@ use std::mem::ManuallyDrop;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use std::{fmt, io, thread, time}; use std::{fmt, io, thread, time};
use crate::terminal::io::ERROR_PREFIX; use crate::terminal::io::{ERROR_PREFIX, WARNING_PREFIX};
use crate::terminal::Paint; use crate::terminal::Paint;
/// How much time to wait between spinner animation updates. /// How much time to wait between spinner animation updates.
@ -34,6 +34,7 @@ enum State {
Running { cursor: usize }, Running { cursor: usize },
Canceled, Canceled,
Done, Done,
Warn,
Error, Error,
} }
@ -84,6 +85,13 @@ impl Spinner {
} }
} }
/// Cancel the spinner with a warning sign.
pub fn warn(self) {
if let Ok(mut progress) = self.progress.lock() {
progress.state = State::Warn;
}
}
/// Set the spinner's message. /// Set the spinner's message.
pub fn message(&mut self, msg: impl fmt::Display) { pub fn message(&mut self, msg: impl fmt::Display) {
let msg = msg.to_string(); let msg = msg.to_string();
@ -150,6 +158,13 @@ pub fn spinner(message: impl ToString) -> Spinner {
.ok(); .ok();
break; break;
} }
Progress {
state: State::Warn,
message,
} => {
writeln!(stdout, "{WARNING_PREFIX} {message}").ok();
break;
}
Progress { Progress {
state: State::Error, state: State::Error,
message, message,