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
✓ Creating your 🌱 Ed25519 keypair...
! Adding your radicle key to ssh-agent...
✓ Your Radicle ID is did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi. This identifies your device.
👉 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 radicle::crypto::ssh;
use radicle::crypto::ssh::Passphrase;
use radicle::{profile, Profile};
use crate::terminal as term;
@ -86,9 +87,16 @@ pub fn init(options: Options) -> anyhow::Result<()> {
term::passphrase_confirm()
}?;
let spinner = term::spinner("Creating your 🌱 Ed25519 keypair...");
let profile = Profile::init(home, passphrase)?;
let profile = Profile::init(home, passphrase.clone())?;
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!(
"Your Radicle ID is {}. This identifies your device.",
term::format::highlight(profile.did())
@ -124,12 +132,7 @@ pub fn authenticate(profile: &Profile, options: Options) -> anyhow::Result<()> {
term::passphrase()
}?;
let spinner = term::spinner("Unlocking...");
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)?;
register(profile, passphrase)?;
spinner.finish();
term::success!("Radicle key added to ssh-agent");
@ -139,3 +142,16 @@ pub fn authenticate(profile: &Profile, options: Options) -> anyhow::Result<()> {
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};
pub const ERROR_PREFIX: Paint<&str> = Paint::red("");
pub const WARNING_PREFIX: Paint<&str> = Paint::yellow("!");
pub const TAB: &str = " ";
/// Render configuration.
@ -129,7 +130,7 @@ pub fn subcommand(msg: impl fmt::Display) {
pub fn warning(warning: &str) {
println!(
"{} {} {warning}",
Paint::yellow("!"),
WARNING_PREFIX,
Paint::yellow("Warning:").bold(),
);
}

View File

@ -3,7 +3,7 @@ use std::mem::ManuallyDrop;
use std::sync::{Arc, Mutex};
use std::{fmt, io, thread, time};
use crate::terminal::io::ERROR_PREFIX;
use crate::terminal::io::{ERROR_PREFIX, WARNING_PREFIX};
use crate::terminal::Paint;
/// How much time to wait between spinner animation updates.
@ -34,6 +34,7 @@ enum State {
Running { cursor: usize },
Canceled,
Done,
Warn,
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.
pub fn message(&mut self, msg: impl fmt::Display) {
let msg = msg.to_string();
@ -150,6 +158,13 @@ pub fn spinner(message: impl ToString) -> Spinner {
.ok();
break;
}
Progress {
state: State::Warn,
message,
} => {
writeln!(stdout, "{WARNING_PREFIX} {message}").ok();
break;
}
Progress {
state: State::Error,
message,