From a82546b70303000b4fc053a1ee21d3d8c7d6ad66 Mon Sep 17 00:00:00 2001 From: DanConwayDev Date: Fri, 28 Jun 2024 15:16:43 +0100 Subject: feat(login): login with nip46 remote signer and save details in git config --- src/client.rs | 31 +++- src/git.rs | 93 ++++++++++-- src/login.rs | 374 ++++++++++++++++++++++++++++++++++++++-------- src/main.rs | 7 + src/repo_ref.rs | 14 +- src/sub_commands/init.rs | 2 + src/sub_commands/login.rs | 22 ++- src/sub_commands/push.rs | 2 + src/sub_commands/send.rs | 12 +- 9 files changed, 465 insertions(+), 92 deletions(-) (limited to 'src') diff --git a/src/client.rs b/src/client.rs index 9dba528..44abb29 100644 --- a/src/client.rs +++ b/src/client.rs @@ -19,7 +19,7 @@ use indicatif::{MultiProgress, ProgressBar, ProgressState, ProgressStyle}; #[cfg(test)] use mockall::*; use nostr::Event; -use nostr_sdk::NostrSigner; +use nostr_sdk::{EventBuilder, NostrSigner}; #[allow(clippy::struct_field_names)] pub struct Client { @@ -292,3 +292,32 @@ fn get_dedup_events(relay_results: Vec>>) -> Vec } dedup_events } + +pub async fn sign_event(event_builder: EventBuilder, signer: &NostrSigner) -> Result { + if signer.r#type().eq(&nostr_signer::NostrSignerType::NIP46) { + let term = console::Term::stderr(); + term.write_line("signing event with remote signer...")?; + let event = signer + .sign_event_builder(event_builder) + .await + .context("failed to sign event")?; + term.clear_last_lines(1)?; + Ok(event) + } else { + signer + .sign_event_builder(event_builder) + .await + .context("failed to sign event") + } +} + +pub async fn fetch_public_key(signer: &NostrSigner) -> Result { + let term = console::Term::stderr(); + term.write_line("fetching npub from remote signer...")?; + let public_key = signer + .public_key() + .await + .context("failed to get npub from remote signer")?; + term.clear_last_lines(1)?; + Ok(public_key) +} diff --git a/src/git.rs b/src/git.rs index 46687ae..bb943a9 100644 --- a/src/git.rs +++ b/src/git.rs @@ -76,8 +76,9 @@ pub trait RepoActions { ) -> Result>; fn parse_starting_commits(&self, starting_commits: &str) -> Result>; fn ancestor_of(&self, decendant: &Sha1Hash, ancestor: &Sha1Hash) -> Result; - fn get_git_config_item(&self, item: &str, global: bool) -> Result>; + fn get_git_config_item(&self, item: &str, global: Option) -> Result>; fn save_git_config_item(&self, item: &str, value: &str, global: bool) -> Result<()>; + fn remove_git_config_item(&self, item: &str, global: bool) -> Result; } impl RepoActions for Repo { @@ -581,8 +582,15 @@ impl RepoActions for Repo { } } - fn get_git_config_item(&self, item: &str, global: bool) -> Result> { - match if global { + /// setting global to None will suppliment local config with global items + /// not in local + fn get_git_config_item(&self, item: &str, global: Option) -> Result> { + let just_global = if let Some(just_global) = global { + just_global + } else { + false + }; + match if just_global { self.git_repo .config() .context("cannot open git config")? @@ -593,11 +601,22 @@ impl RepoActions for Repo { } .get_entry(item) { - Ok(item) => Ok(Some( - item.value() - .context("cannot find git config item")? - .to_string(), - )), + Ok(item) => { + if let Some(global) = global { + if item.level().eq(&git2::ConfigLevel::Local) { + if global { + bail!("only local repository login available") + } + } else if !global { + bail!("only global repository login available") + } + } + Ok(Some( + item.value() + .context("cannot find git config item")? + .to_string(), + )) + } Err(_) => Ok(None), } } @@ -613,9 +632,33 @@ impl RepoActions for Repo { self.git_repo.config().context("cannot open git config")? } .set_str(item, value) - .context("cannot set git config value")?; + .context(format!( + "cannot set {} git config item {}", + if global { "global" } else { "local" }, + item + ))?; Ok(()) } + + /// returns false if item doesn't exist + fn remove_git_config_item(&self, item: &str, global: bool) -> Result { + if self.get_git_config_item(item, Some(global))?.is_none() { + Ok(false) + } else { + if global { + self.git_repo + .config() + .context("cannot open git config")? + .open_global() + .context("cannot open global git config")? + } else { + self.git_repo.config().context("cannot open git config")? + } + .remove(item) + .context("cannot remove existing git config item")?; + Ok(true) + } + } } fn oid_to_u8_20_bytes(oid: &Oid) -> [u8; 20] { @@ -849,7 +892,9 @@ mod tests { let git_repo = Repo::from_path(&test_repo.dir)?; git_repo.save_git_config_item("test.item", "testvalue", false)?; assert_eq!( - git_repo.get_git_config_item("test.item", false)?.unwrap(), + git_repo + .get_git_config_item("test.item", Some(false))? + .unwrap(), "testvalue", ); Ok(()) @@ -859,7 +904,10 @@ mod tests { fn get_git_config_item_returns_none_if_not_present() -> Result<()> { let test_repo = GitTestRepo::default(); let git_repo = Repo::from_path(&test_repo.dir)?; - assert_eq!(git_repo.get_git_config_item("test.item", false)?, None); + assert_eq!( + git_repo.get_git_config_item("test.item", Some(false))?, + None + ); Ok(()) } @@ -869,11 +917,32 @@ mod tests { let git_repo = Repo::from_path(&test_repo.dir)?; git_repo.save_git_config_item("test.item", "", false)?; assert_eq!( - git_repo.get_git_config_item("test.item", false)?, + git_repo.get_git_config_item("test.item", Some(false))?, Some("".to_string()), ); Ok(()) } + + #[test] + fn remove_local_git_config_item() -> Result<()> { + let test_repo = GitTestRepo::default(); + let git_repo = Repo::from_path(&test_repo.dir)?; + git_repo.save_git_config_item("test.item", "testvalue", false)?; + assert!(git_repo.remove_git_config_item("test.item", false)?); + assert_eq!( + git_repo.get_git_config_item("test.item", Some(false))?, + None, + ); + Ok(()) + } + + #[test] + fn remove_git_config_item_returns_false_if_item_wasnt_set() -> Result<()> { + let test_repo = GitTestRepo::default(); + let git_repo = Repo::from_path(&test_repo.dir)?; + assert!(!(git_repo.remove_git_config_item("test.item", false)?)); + Ok(()) + } } #[test] diff --git a/src/login.rs b/src/login.rs index e1669c1..218a079 100644 --- a/src/login.rs +++ b/src/login.rs @@ -1,11 +1,13 @@ -use std::str::FromStr; +use std::{fs::create_dir_all, str::FromStr, time::Duration}; use anyhow::{bail, Context, Result}; -use nostr::PublicKey; +use nostr::{nips::nip46::NostrConnectURI, PublicKey}; use nostr_database::Order; use nostr_sdk::{ - Alphabet, FromBech32, JsonUtil, Kind, NostrDatabase, NostrSigner, SingleLetterTag, ToBech32, + Alphabet, FromBech32, JsonUtil, Keys, Kind, NostrDatabase, NostrSigner, SingleLetterTag, + ToBech32, }; +use nostr_signer::Nip46Signer; use nostr_sqlite::SQLiteDatabase; #[cfg(not(test))] @@ -16,7 +18,7 @@ use crate::{ cli_interactor::{ Interactor, InteractorPrompt, PromptConfirmParms, PromptInputParms, PromptPasswordParms, }, - client::Connect, + client::{fetch_public_key, Connect}, config::{get_dirs, UserMetadata, UserRef, UserRelayRef, UserRelays}, git::{Repo, RepoActions}, key_handling::encryption::{decrypt_key, encrypt_key}, @@ -25,14 +27,25 @@ use crate::{ /// handles the encrpytion and storage of key material pub async fn launch( git_repo: &Repo, + bunker_uri: &Option, + bunker_app_key: &Option, nsec: &Option, password: &Option, #[cfg(test)] client: Option<&MockConnect>, #[cfg(not(test))] client: Option<&Client>, change_user: bool, ) -> Result<(NostrSigner, UserRef)> { - if let Ok(keys) = match get_keys_without_prompts(git_repo, nsec, password, change_user) { - Ok(keys) => Ok(keys), + if let Ok(signer) = match get_signer_without_prompts( + git_repo, + bunker_uri, + bunker_app_key, + nsec, + password, + change_user, + ) + .await + { + Ok(signer) => Ok(signer), Err(error) => { if error .to_string() @@ -60,7 +73,7 @@ pub async fn launch( .password(PromptPasswordParms::default().with_prompt("password")) .context("failed to get password input from interactor.password")?; if let Ok(keys) = get_keys_with_password(git_repo, &password) { - break Ok(keys); + break Ok(NostrSigner::Keys(keys)); } println!("incorrect password"); } @@ -73,9 +86,17 @@ pub async fn launch( } } { // get user ref - let user_ref = get_user_details(&keys.public_key(), client, git_repo).await?; + let user_ref = get_user_details( + &signer + .public_key() + .await + .context("cannot get public key from signer")?, + client, + git_repo, + ) + .await?; print_logged_in_as(&user_ref, client.is_none())?; - Ok((NostrSigner::Keys(keys), user_ref)) + Ok((signer, user_ref)) } else { fresh_login(git_repo, client, change_user).await } @@ -95,18 +116,45 @@ fn print_logged_in_as(user_ref: &UserRef, offline_mode: bool) -> Result<()> { Ok(()) } -fn get_keys_without_prompts( +async fn get_signer_without_prompts( git_repo: &Repo, + bunker_uri: &Option, + bunker_app_key: &Option, nsec: &Option, password: &Option, save_local: bool, -) -> Result { +) -> Result { if let Some(nsec) = nsec { - get_keys_from_nsec(git_repo, nsec, password, save_local) + Ok(NostrSigner::Keys(get_keys_from_nsec( + git_repo, nsec, password, save_local, + )?)) } else if let Some(password) = password { - get_keys_with_password(git_repo, password) + Ok(NostrSigner::Keys(get_keys_with_password( + git_repo, password, + )?)) + } else if let Some(bunker_uri) = bunker_uri { + if let Some(bunker_app_key) = bunker_app_key { + let signer = get_nip46_signer_from_uri_and_key(bunker_uri, bunker_app_key) + .await + .context("failed to connect with remote signer")?; + if save_local { + save_to_git_config( + git_repo, + &signer.public_key().await?.to_bech32()?, + &None, + &Some((bunker_uri.to_string(),bunker_app_key.to_string())), + false, + ) + .context("failed to save bunker details local git config nostr.bunker-uri and nostr.bunker-app-key")?; + } + Ok(signer) + } else { + bail!( + "bunker-app-key parameter must be provided alongside bunker-uri. if unknown, login interactively." + ) + } } else if !save_local { - get_keys_with_git_config_nsec_without_prompts(git_repo) + get_signer_with_git_config_nsec_or_bunker_without_prompts(git_repo).await } else { bail!("user wants prompts to specify new keys") } @@ -139,18 +187,82 @@ fn get_keys_from_nsec( if let Some(password) = password { s = encrypt_key(&keys, password)?; } - git_repo - .save_git_config_item("nostr.nsec", &s, false) - .context("failed to save encrypted nsec in local git config nostr.nsec")?; - git_repo.save_git_config_item("nostr.npub", &keys.public_key().to_bech32()?, false)?; + save_to_git_config( + git_repo, + &keys.public_key().to_bech32()?, + &Some(s), + &None, + false, + ) + .context("failed to save encrypted nsec in local git config nostr.nsec")?; } Ok(keys) } +fn save_to_git_config( + git_repo: &Repo, + npub: &str, + nsec: &Option, + bunker: &Option<(String, String)>, + global: bool, +) -> Result<()> { + if let Err(error) = silently_save_to_git_config(git_repo, npub, nsec, bunker, global) { + println!( + "failed to save login details to {} git config", + if global { "global" } else { "local" } + ); + if let Some(nsec) = nsec { + if nsec.contains("ncryptsec") { + println!("manually set git config nostr.nsec to: {nsec}"); + } else { + println!("manually set git config nostr.nsec"); + } + } + if let Some(bunker) = bunker { + println!("manually set git config as follows:"); + println!("nostr.bunker-uri: {}", bunker.0); + println!("nostr.bunker-app-key: {}", bunker.1); + } + Err(error) + } else { + println!( + "saved login details to {} git config", + if global { "global" } else { "local" } + ); + Ok(()) + } +} +fn silently_save_to_git_config( + git_repo: &Repo, + npub: &str, + nsec: &Option, + bunker: &Option<(String, String)>, + global: bool, +) -> Result<()> { + // must do this first otherwise it might remove the global items just added + if global { + git_repo.remove_git_config_item("nostr.npub", false)?; + git_repo.remove_git_config_item("nostr.nsec", false)?; + git_repo.remove_git_config_item("nostr.bunker-uri", false)?; + git_repo.remove_git_config_item("nostr.bunker-app-key", false)?; + } + if let Some(bunker) = bunker { + git_repo.remove_git_config_item("nostr.nsec", global)?; + git_repo.save_git_config_item("nostr.bunker-uri", &bunker.0, global)?; + git_repo.save_git_config_item("nostr.bunker-app-key", &bunker.1, global)?; + } + if let Some(nsec) = nsec { + git_repo.save_git_config_item("nostr.nsec", nsec, global)?; + git_repo.remove_git_config_item("nostr.bunker-uri", global)?; + git_repo.remove_git_config_item("nostr.bunker-app-key", global)?; + } + git_repo.save_git_config_item("nostr.npub", npub, global) +} + fn get_keys_with_password(git_repo: &Repo, password: &str) -> Result { decrypt_key( &git_repo - .get_git_config_item("nostr.nsec", false) + .get_git_config_item("nostr.nsec", None) .context("failed get git config")? .context("git config item nostr.nsec doesn't exist so cannot decrypt it")?, password, @@ -158,15 +270,74 @@ fn get_keys_with_password(git_repo: &Repo, password: &str) -> Result Result { - let nsec = &git_repo - .get_git_config_item("nostr.nsec", false) - .context("failed get git config")? - .context("git config item nostr.nsec doesn't exist")?; - if nsec.contains("ncryptsec") { - bail!("git config item nostr.nsec is an ncryptsec") +async fn get_nip46_signer_from_uri_and_key(uri: &str, app_key: &str) -> Result { + let term = console::Term::stderr(); + term.write_line("connecting to remote signer...")?; + let uri = NostrConnectURI::parse(uri)?; + let signer = NostrSigner::nip46( + Nip46Signer::new( + uri, + nostr::Keys::from_str(app_key).context("invalid app key")?, + Duration::from_secs(30), + None, + ) + .await?, + ); + term.clear_last_lines(1)?; + Ok(signer) +} + +async fn get_signer_with_git_config_nsec_or_bunker_without_prompts( + git_repo: &Repo, +) -> Result { + if let Ok(local_nsec) = &git_repo + .get_git_config_item("nostr.nsec", Some(false)) + .context("failed get local git config")? + .context("git local config item nostr.nsec doesn't exist") + { + if local_nsec.contains("ncryptsec") { + bail!("git global config item nostr.nsec is an ncryptsec") + } + Ok(NostrSigner::Keys( + nostr::Keys::from_str(local_nsec).context("invalid nsec parameter")?, + )) + } else if let Ok((uri, app_key)) = get_git_config_bunker_uri_and_app_key(git_repo, Some(false)) + { + get_nip46_signer_from_uri_and_key(&uri, &app_key).await + } else if let Ok(global_nsec) = &git_repo + .get_git_config_item("nostr.nsec", Some(true)) + .context("failed get global git config")? + .context("git global config item nostr.nsec doesn't exist") + { + if global_nsec.contains("ncryptsec") { + bail!("git global config item nostr.nsec is an ncryptsec") + } + Ok(NostrSigner::Keys( + nostr::Keys::from_str(global_nsec).context("invalid nsec parameter")?, + )) + } else if let Ok((uri, app_key)) = get_git_config_bunker_uri_and_app_key(git_repo, Some(true)) { + get_nip46_signer_from_uri_and_key(&uri, &app_key).await + } else { + bail!("cannot get nsec or bunker from git config") } - nostr::Keys::from_str(nsec).context("invalid nsec parameter") +} + +fn get_git_config_bunker_uri_and_app_key( + git_repo: &Repo, + global: Option, +) -> Result<(String, String)> { + Ok(( + git_repo + .get_git_config_item("nostr.bunker_url", global) + .context("failed get local git config")? + .context("git local config item nostr.bunker_url doesn't exist")? + .to_string(), + git_repo + .get_git_config_item("nostr.bunker-app-key", global) + .context("failed get local git config")? + .context("git local config item nostr.bunker-app-key doesn't exist")? + .to_string(), + )) } async fn fresh_login( @@ -175,50 +346,119 @@ async fn fresh_login( #[cfg(not(test))] client: Option<&Client>, always_save: bool, ) -> Result<(NostrSigner, UserRef)> { + let mut public_key: Option = None; // prompt for nsec - let mut prompt = "login with nsec"; - let keys = loop { - match nostr::Keys::from_str( - &Interactor::default() - .input(PromptInputParms::default().with_prompt(prompt)) - .context("failed to get nsec input from interactor")?, - ) { + let mut prompt = "login with bunker uri / nsec"; + let signer = loop { + let input = Interactor::default() + .input(PromptInputParms::default().with_prompt(prompt)) + .context("failed to get nsec input from interactor")?; + match nostr::Keys::from_str(&input) { Ok(key) => { - break key; - } - Err(_) => { - prompt = "invalid nsec. try again with nsec (or hex private key)"; + if let Err(error) = save_keys(git_repo, &key, always_save) { + println!("{error}"); + } + break NostrSigner::Keys(key); } + Err(_) => match NostrConnectURI::parse(&input) { + Ok(_) => { + let app_key = Keys::generate().secret_key()?.to_secret_hex(); + match get_nip46_signer_from_uri_and_key(&input, &app_key).await { + Ok(signer) => { + let pub_key = fetch_public_key(&signer).await?; + if let Err(error) = + save_bunker(git_repo, &pub_key, &input, &app_key, always_save) + { + println!("{error}"); + } + public_key = Some(pub_key); + break signer; + } + Err(_) => { + prompt = "invalid. try again with nostr address / nsec"; + } + } + } + Err(_) => { + prompt = "invalid. try again with nostr address / nsec"; + } + }, } }; + let public_key = if let Some(public_key) = public_key { + public_key + } else { + signer.public_key().await? + }; // lookup profile - // save keys - if let Err(error) = save_keys(git_repo, &keys, always_save) { - println!("{error}"); - } - let user_ref = get_user_details(&keys.public_key(), client, git_repo).await?; + let user_ref = get_user_details(&public_key, client, git_repo).await?; print_logged_in_as(&user_ref, client.is_none())?; - Ok((NostrSigner::Keys(keys), user_ref)) + Ok((signer, user_ref)) } -fn save_keys(git_repo: &Repo, keys: &nostr::Keys, always_save: bool) -> Result<()> { - let store = always_save +fn save_bunker( + git_repo: &Repo, + public_key: &PublicKey, + uri: &str, + app_key: &str, + always_save: bool, +) -> Result<()> { + if always_save || Interactor::default() - .confirm(PromptConfirmParms::default().with_prompt("save login details?"))?; + .confirm(PromptConfirmParms::default().with_prompt("save login details?"))? + { + let global = !Interactor::default().confirm( + PromptConfirmParms::default() + .with_prompt("just for this repository?") + .with_default(false), + )?; + let npub = public_key.to_bech32()?; + if let Err(error) = save_to_git_config( + git_repo, + &npub, + &None, + &Some((uri.to_string(), app_key.to_string())), + global, + ) { + if global { + if Interactor::default().confirm( + PromptConfirmParms::default() + .with_prompt("save in repository git config?") + .with_default(true), + )? { + save_to_git_config( + git_repo, + &npub, + &None, + &Some((uri.to_string(), app_key.to_string())), + false, + )?; + } + } else { + Err(error)?; + } + }; + } + Ok(()) +} - let global = !Interactor::default().confirm( - PromptConfirmParms::default() - .with_prompt("just for this repository?") - .with_default(false), - )?; +fn save_keys(git_repo: &Repo, keys: &nostr::Keys, always_save: bool) -> Result<()> { + if always_save + || Interactor::default() + .confirm(PromptConfirmParms::default().with_prompt("save login details?"))? + { + let global = !Interactor::default().confirm( + PromptConfirmParms::default() + .with_prompt("just for this repository?") + .with_default(false), + )?; - let encrypt = Interactor::default().confirm( - PromptConfirmParms::default() - .with_prompt("require password?") - .with_default(false), - )?; + let encrypt = Interactor::default().confirm( + PromptConfirmParms::default() + .with_prompt("require password?") + .with_default(false), + )?; - if store { let npub = keys.public_key().to_bech32()?; let nsec_string = if encrypt { let password = Interactor::default() @@ -233,22 +473,20 @@ fn save_keys(git_repo: &Repo, keys: &nostr::Keys, always_save: bool) -> Result<( keys.secret_key()?.to_bech32()? }; - if let Err(error) = git_repo.save_git_config_item("nostr.nsec", &nsec_string, global) { + if let Err(error) = + save_to_git_config(git_repo, &npub, &Some(nsec_string.clone()), &None, global) + { if global { - println!("failed to edit global git config instead"); if Interactor::default().confirm( PromptConfirmParms::default() .with_prompt("save in repository git config?") .with_default(true), )? { - git_repo.save_git_config_item("nostr.nsec", &nsec_string, false)?; - git_repo.save_git_config_item("nostr.npub", &npub, false)?; + save_to_git_config(git_repo, &npub, &Some(nsec_string.clone()), &None, false)?; } } else { - bail!(error) + Err(error)?; } - } else { - git_repo.save_git_config_item("nostr.npub", &npub, global)?; }; }; Ok(()) @@ -256,7 +494,7 @@ fn save_keys(git_repo: &Repo, keys: &nostr::Keys, always_save: bool) -> Result<( fn get_config_item(git_repo: &Repo, name: &str) -> Result { git_repo - .get_git_config_item(name, false) + .get_git_config_item(name, None) .context("failed get git config")? .context(format!("git config item {name} doesn't exist")) } @@ -350,6 +588,10 @@ async fn get_user_details( println!("searching for profile and relay updates..."); } let database = SQLiteDatabase::open(if std::env::var("NGITTEST").is_err() { + create_dir_all(get_dirs()?.config_dir()).context(format!( + "cannot create cache directory in: {:?}", + get_dirs()?.config_dir() + ))?; get_dirs()?.config_dir().join("cache.sqlite") } else { git_repo.get_path()?.join(".git/test-global-cache.sqlite") diff --git a/src/main.rs b/src/main.rs index 30ecea3..9f53084 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,5 @@ #![cfg_attr(not(test), warn(clippy::pedantic))] +#![allow(clippy::large_futures)] #![cfg_attr(not(test), warn(clippy::expect_used))] use anyhow::Result; @@ -19,6 +20,12 @@ mod sub_commands; pub struct Cli { #[command(subcommand)] command: Commands, + /// remote signer address + #[arg(long, global = true)] + bunker_uri: Option, + /// remote signer app secret key + #[arg(long, global = true)] + bunker_app_key: Option, /// nsec or hex private key #[arg(short, long, global = true)] nsec: Option, diff --git a/src/repo_ref.rs b/src/repo_ref.rs index 2b0d024..426640f 100644 --- a/src/repo_ref.rs +++ b/src/repo_ref.rs @@ -11,7 +11,7 @@ use crate::client::Client; use crate::client::MockConnect; use crate::{ cli_interactor::{Interactor, InteractorPrompt, PromptInputParms}, - client::Connect, + client::{sign_event, Connect}, git::{Repo, RepoActions}, }; @@ -95,8 +95,8 @@ pub static REPO_REF_KIND: u16 = 30_617; impl RepoRef { pub async fn to_event(&self, signer: &NostrSigner) -> Result { - signer - .sign_event_builder(nostr_sdk::EventBuilder::new( + sign_event( + nostr_sdk::EventBuilder::new( nostr::event::Kind::Custom(REPO_REF_KIND), "", [ @@ -152,9 +152,11 @@ impl RepoRef { // code languages and hashtags ] .concat(), - )) - .await - .context("failed to create repository reference event") + ), + signer, + ) + .await + .context("failed to create repository reference event") } } diff --git a/src/sub_commands/init.rs b/src/sub_commands/init.rs index 4afe83c..57785db 100644 --- a/src/sub_commands/init.rs +++ b/src/sub_commands/init.rs @@ -61,6 +61,8 @@ pub async fn launch(cli_args: &Cli, args: &SubCommandArgs) -> Result<()> { let (signer, user_ref) = login::launch( &git_repo, + &cli_args.bunker_uri, + &cli_args.bunker_app_key, &cli_args.nsec, &cli_args.password, Some(&client), diff --git a/src/sub_commands/login.rs b/src/sub_commands/login.rs index e71d431..6f49ba8 100644 --- a/src/sub_commands/login.rs +++ b/src/sub_commands/login.rs @@ -17,7 +17,16 @@ pub struct SubCommandArgs { pub async fn launch(args: &Cli, command_args: &SubCommandArgs) -> Result<()> { let git_repo = Repo::discover().context("cannot find a git repository")?; if command_args.offline { - login::launch(&git_repo, &args.nsec, &args.password, None, true).await?; + login::launch( + &git_repo, + &args.bunker_uri, + &args.bunker_app_key, + &args.nsec, + &args.password, + None, + true, + ) + .await?; Ok(()) } else { #[cfg(not(test))] @@ -25,7 +34,16 @@ pub async fn launch(args: &Cli, command_args: &SubCommandArgs) -> Result<()> { #[cfg(test)] let client = ::default(); - login::launch(&git_repo, &args.nsec, &args.password, Some(&client), true).await?; + login::launch( + &git_repo, + &args.bunker_uri, + &args.bunker_app_key, + &args.nsec, + &args.password, + Some(&client), + true, + ) + .await?; client.disconnect().await?; Ok(()) } diff --git a/src/sub_commands/push.rs b/src/sub_commands/push.rs index 92c1c18..3c471c0 100644 --- a/src/sub_commands/push.rs +++ b/src/sub_commands/push.rs @@ -150,6 +150,8 @@ pub async fn launch(cli_args: &Cli, args: &SubCommandArgs) -> Result<()> { let (signer, user_ref) = login::launch( &git_repo, + &cli_args.bunker_uri, + &cli_args.bunker_app_key, &cli_args.nsec, &cli_args.password, Some(&client), diff --git a/src/sub_commands/send.rs b/src/sub_commands/send.rs index 1d20e90..7c8f2ee 100644 --- a/src/sub_commands/send.rs +++ b/src/sub_commands/send.rs @@ -19,7 +19,7 @@ use crate::{ cli_interactor::{ Interactor, InteractorPrompt, PromptConfirmParms, PromptInputParms, PromptMultiChoiceParms, }, - client::Connect, + client::{sign_event, Connect}, git::{Repo, RepoActions}, login, repo_ref::{self, RepoRef, REPO_REF_KIND}, @@ -180,6 +180,8 @@ pub async fn launch(cli_args: &Cli, args: &SubCommandArgs) -> Result<()> { }; let (signer, user_ref) = login::launch( &git_repo, + &cli_args.bunker_uri, + &cli_args.bunker_app_key, &cli_args.nsec, &cli_args.password, Some(&client), @@ -593,7 +595,7 @@ pub async fn generate_cover_letter_and_patch_events( let mut events = vec![]; if let Some((title, description)) = cover_letter_title_description { - events.push(signer.sign_event_builder(EventBuilder::new( + events.push(sign_event(EventBuilder::new( nostr::event::Kind::Custom(PATCH_KIND), format!( "From {} Mon Sep 17 00:00:00 2001\nSubject: [PATCH 0/{}] {title}\n\n{description}", @@ -656,7 +658,7 @@ pub async fn generate_cover_letter_and_patch_events( .map(|pk| Tag::public_key(*pk)) .collect(), ].concat(), - )).await + ), signer).await .context("failed to create cover-letter event")?); } @@ -883,7 +885,7 @@ pub async fn generate_patch_event( .context("failed to get parent commit")?; let relay_hint = repo_ref.relays.first().map(nostr::UncheckedUrl::from); - signer.sign_event_builder(EventBuilder::new( + sign_event(EventBuilder::new( nostr::event::Kind::Custom(PATCH_KIND), git_repo .make_patch_from_commit(commit,&series_count) @@ -1000,7 +1002,7 @@ pub async fn generate_patch_event( ], ] .concat(), - )).await + ), signer).await .context("failed to sign event") } // TODO -- cgit v1.2.3