diff options
| author | DanConwayDev <DanConwayDev@protonmail.com> | 2024-06-28 15:16:43 +0100 |
|---|---|---|
| committer | DanConwayDev <DanConwayDev@protonmail.com> | 2024-06-28 15:16:43 +0100 |
| commit | a82546b70303000b4fc053a1ee21d3d8c7d6ad66 (patch) | |
| tree | f8c4238a5ae27759b3c1a6adb5c865b07e339a66 /src/login.rs | |
| parent | 6b06e874119ceca1a9dac1b94dcfe6e06aacd7b9 (diff) | |
feat(login): login with nip46 remote signer
and save details in git config
Diffstat (limited to 'src/login.rs')
| -rw-r--r-- | src/login.rs | 374 |
1 files changed, 308 insertions, 66 deletions
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 @@ | |||
| 1 | use std::str::FromStr; | 1 | use std::{fs::create_dir_all, str::FromStr, time::Duration}; |
| 2 | 2 | ||
| 3 | use anyhow::{bail, Context, Result}; | 3 | use anyhow::{bail, Context, Result}; |
| 4 | use nostr::PublicKey; | 4 | use nostr::{nips::nip46::NostrConnectURI, PublicKey}; |
| 5 | use nostr_database::Order; | 5 | use nostr_database::Order; |
| 6 | use nostr_sdk::{ | 6 | use nostr_sdk::{ |
| 7 | Alphabet, FromBech32, JsonUtil, Kind, NostrDatabase, NostrSigner, SingleLetterTag, ToBech32, | 7 | Alphabet, FromBech32, JsonUtil, Keys, Kind, NostrDatabase, NostrSigner, SingleLetterTag, |
| 8 | ToBech32, | ||
| 8 | }; | 9 | }; |
| 10 | use nostr_signer::Nip46Signer; | ||
| 9 | use nostr_sqlite::SQLiteDatabase; | 11 | use nostr_sqlite::SQLiteDatabase; |
| 10 | 12 | ||
| 11 | #[cfg(not(test))] | 13 | #[cfg(not(test))] |
| @@ -16,7 +18,7 @@ use crate::{ | |||
| 16 | cli_interactor::{ | 18 | cli_interactor::{ |
| 17 | Interactor, InteractorPrompt, PromptConfirmParms, PromptInputParms, PromptPasswordParms, | 19 | Interactor, InteractorPrompt, PromptConfirmParms, PromptInputParms, PromptPasswordParms, |
| 18 | }, | 20 | }, |
| 19 | client::Connect, | 21 | client::{fetch_public_key, Connect}, |
| 20 | config::{get_dirs, UserMetadata, UserRef, UserRelayRef, UserRelays}, | 22 | config::{get_dirs, UserMetadata, UserRef, UserRelayRef, UserRelays}, |
| 21 | git::{Repo, RepoActions}, | 23 | git::{Repo, RepoActions}, |
| 22 | key_handling::encryption::{decrypt_key, encrypt_key}, | 24 | key_handling::encryption::{decrypt_key, encrypt_key}, |
| @@ -25,14 +27,25 @@ use crate::{ | |||
| 25 | /// handles the encrpytion and storage of key material | 27 | /// handles the encrpytion and storage of key material |
| 26 | pub async fn launch( | 28 | pub async fn launch( |
| 27 | git_repo: &Repo, | 29 | git_repo: &Repo, |
| 30 | bunker_uri: &Option<String>, | ||
| 31 | bunker_app_key: &Option<String>, | ||
| 28 | nsec: &Option<String>, | 32 | nsec: &Option<String>, |
| 29 | password: &Option<String>, | 33 | password: &Option<String>, |
| 30 | #[cfg(test)] client: Option<&MockConnect>, | 34 | #[cfg(test)] client: Option<&MockConnect>, |
| 31 | #[cfg(not(test))] client: Option<&Client>, | 35 | #[cfg(not(test))] client: Option<&Client>, |
| 32 | change_user: bool, | 36 | change_user: bool, |
| 33 | ) -> Result<(NostrSigner, UserRef)> { | 37 | ) -> Result<(NostrSigner, UserRef)> { |
| 34 | if let Ok(keys) = match get_keys_without_prompts(git_repo, nsec, password, change_user) { | 38 | if let Ok(signer) = match get_signer_without_prompts( |
| 35 | Ok(keys) => Ok(keys), | 39 | git_repo, |
| 40 | bunker_uri, | ||
| 41 | bunker_app_key, | ||
| 42 | nsec, | ||
| 43 | password, | ||
| 44 | change_user, | ||
| 45 | ) | ||
| 46 | .await | ||
| 47 | { | ||
| 48 | Ok(signer) => Ok(signer), | ||
| 36 | Err(error) => { | 49 | Err(error) => { |
| 37 | if error | 50 | if error |
| 38 | .to_string() | 51 | .to_string() |
| @@ -60,7 +73,7 @@ pub async fn launch( | |||
| 60 | .password(PromptPasswordParms::default().with_prompt("password")) | 73 | .password(PromptPasswordParms::default().with_prompt("password")) |
| 61 | .context("failed to get password input from interactor.password")?; | 74 | .context("failed to get password input from interactor.password")?; |
| 62 | if let Ok(keys) = get_keys_with_password(git_repo, &password) { | 75 | if let Ok(keys) = get_keys_with_password(git_repo, &password) { |
| 63 | break Ok(keys); | 76 | break Ok(NostrSigner::Keys(keys)); |
| 64 | } | 77 | } |
| 65 | println!("incorrect password"); | 78 | println!("incorrect password"); |
| 66 | } | 79 | } |
| @@ -73,9 +86,17 @@ pub async fn launch( | |||
| 73 | } | 86 | } |
| 74 | } { | 87 | } { |
| 75 | // get user ref | 88 | // get user ref |
| 76 | let user_ref = get_user_details(&keys.public_key(), client, git_repo).await?; | 89 | let user_ref = get_user_details( |
| 90 | &signer | ||
| 91 | .public_key() | ||
| 92 | .await | ||
| 93 | .context("cannot get public key from signer")?, | ||
| 94 | client, | ||
| 95 | git_repo, | ||
| 96 | ) | ||
| 97 | .await?; | ||
| 77 | print_logged_in_as(&user_ref, client.is_none())?; | 98 | print_logged_in_as(&user_ref, client.is_none())?; |
| 78 | Ok((NostrSigner::Keys(keys), user_ref)) | 99 | Ok((signer, user_ref)) |
| 79 | } else { | 100 | } else { |
| 80 | fresh_login(git_repo, client, change_user).await | 101 | fresh_login(git_repo, client, change_user).await |
| 81 | } | 102 | } |
| @@ -95,18 +116,45 @@ fn print_logged_in_as(user_ref: &UserRef, offline_mode: bool) -> Result<()> { | |||
| 95 | Ok(()) | 116 | Ok(()) |
| 96 | } | 117 | } |
| 97 | 118 | ||
| 98 | fn get_keys_without_prompts( | 119 | async fn get_signer_without_prompts( |
| 99 | git_repo: &Repo, | 120 | git_repo: &Repo, |
| 121 | bunker_uri: &Option<String>, | ||
| 122 | bunker_app_key: &Option<String>, | ||
| 100 | nsec: &Option<String>, | 123 | nsec: &Option<String>, |
| 101 | password: &Option<String>, | 124 | password: &Option<String>, |
| 102 | save_local: bool, | 125 | save_local: bool, |
| 103 | ) -> Result<nostr::Keys> { | 126 | ) -> Result<NostrSigner> { |
| 104 | if let Some(nsec) = nsec { | 127 | if let Some(nsec) = nsec { |
| 105 | get_keys_from_nsec(git_repo, nsec, password, save_local) | 128 | Ok(NostrSigner::Keys(get_keys_from_nsec( |
| 129 | git_repo, nsec, password, save_local, | ||
| 130 | )?)) | ||
| 106 | } else if let Some(password) = password { | 131 | } else if let Some(password) = password { |
| 107 | get_keys_with_password(git_repo, password) | 132 | Ok(NostrSigner::Keys(get_keys_with_password( |
| 133 | git_repo, password, | ||
| 134 | )?)) | ||
| 135 | } else if let Some(bunker_uri) = bunker_uri { | ||
| 136 | if let Some(bunker_app_key) = bunker_app_key { | ||
| 137 | let signer = get_nip46_signer_from_uri_and_key(bunker_uri, bunker_app_key) | ||
| 138 | .await | ||
| 139 | .context("failed to connect with remote signer")?; | ||
| 140 | if save_local { | ||
| 141 | save_to_git_config( | ||
| 142 | git_repo, | ||
| 143 | &signer.public_key().await?.to_bech32()?, | ||
| 144 | &None, | ||
| 145 | &Some((bunker_uri.to_string(),bunker_app_key.to_string())), | ||
| 146 | false, | ||
| 147 | ) | ||
| 148 | .context("failed to save bunker details local git config nostr.bunker-uri and nostr.bunker-app-key")?; | ||
| 149 | } | ||
| 150 | Ok(signer) | ||
| 151 | } else { | ||
| 152 | bail!( | ||
| 153 | "bunker-app-key parameter must be provided alongside bunker-uri. if unknown, login interactively." | ||
| 154 | ) | ||
| 155 | } | ||
| 108 | } else if !save_local { | 156 | } else if !save_local { |
| 109 | get_keys_with_git_config_nsec_without_prompts(git_repo) | 157 | get_signer_with_git_config_nsec_or_bunker_without_prompts(git_repo).await |
| 110 | } else { | 158 | } else { |
| 111 | bail!("user wants prompts to specify new keys") | 159 | bail!("user wants prompts to specify new keys") |
| 112 | } | 160 | } |
| @@ -139,18 +187,82 @@ fn get_keys_from_nsec( | |||
| 139 | if let Some(password) = password { | 187 | if let Some(password) = password { |
| 140 | s = encrypt_key(&keys, password)?; | 188 | s = encrypt_key(&keys, password)?; |
| 141 | } | 189 | } |
| 142 | git_repo | 190 | save_to_git_config( |
| 143 | .save_git_config_item("nostr.nsec", &s, false) | 191 | git_repo, |
| 144 | .context("failed to save encrypted nsec in local git config nostr.nsec")?; | 192 | &keys.public_key().to_bech32()?, |
| 145 | git_repo.save_git_config_item("nostr.npub", &keys.public_key().to_bech32()?, false)?; | 193 | &Some(s), |
| 194 | &None, | ||
| 195 | false, | ||
| 196 | ) | ||
| 197 | .context("failed to save encrypted nsec in local git config nostr.nsec")?; | ||
| 146 | } | 198 | } |
| 147 | Ok(keys) | 199 | Ok(keys) |
| 148 | } | 200 | } |
| 149 | 201 | ||
| 202 | fn save_to_git_config( | ||
| 203 | git_repo: &Repo, | ||
| 204 | npub: &str, | ||
| 205 | nsec: &Option<String>, | ||
| 206 | bunker: &Option<(String, String)>, | ||
| 207 | global: bool, | ||
| 208 | ) -> Result<()> { | ||
| 209 | if let Err(error) = silently_save_to_git_config(git_repo, npub, nsec, bunker, global) { | ||
| 210 | println!( | ||
| 211 | "failed to save login details to {} git config", | ||
| 212 | if global { "global" } else { "local" } | ||
| 213 | ); | ||
| 214 | if let Some(nsec) = nsec { | ||
| 215 | if nsec.contains("ncryptsec") { | ||
| 216 | println!("manually set git config nostr.nsec to: {nsec}"); | ||
| 217 | } else { | ||
| 218 | println!("manually set git config nostr.nsec"); | ||
| 219 | } | ||
| 220 | } | ||
| 221 | if let Some(bunker) = bunker { | ||
| 222 | println!("manually set git config as follows:"); | ||
| 223 | println!("nostr.bunker-uri: {}", bunker.0); | ||
| 224 | println!("nostr.bunker-app-key: {}", bunker.1); | ||
| 225 | } | ||
| 226 | Err(error) | ||
| 227 | } else { | ||
| 228 | println!( | ||
| 229 | "saved login details to {} git config", | ||
| 230 | if global { "global" } else { "local" } | ||
| 231 | ); | ||
| 232 | Ok(()) | ||
| 233 | } | ||
| 234 | } | ||
| 235 | fn silently_save_to_git_config( | ||
| 236 | git_repo: &Repo, | ||
| 237 | npub: &str, | ||
| 238 | nsec: &Option<String>, | ||
| 239 | bunker: &Option<(String, String)>, | ||
| 240 | global: bool, | ||
| 241 | ) -> Result<()> { | ||
| 242 | // must do this first otherwise it might remove the global items just added | ||
| 243 | if global { | ||
| 244 | git_repo.remove_git_config_item("nostr.npub", false)?; | ||
| 245 | git_repo.remove_git_config_item("nostr.nsec", false)?; | ||
| 246 | git_repo.remove_git_config_item("nostr.bunker-uri", false)?; | ||
| 247 | git_repo.remove_git_config_item("nostr.bunker-app-key", false)?; | ||
| 248 | } | ||
| 249 | if let Some(bunker) = bunker { | ||
| 250 | git_repo.remove_git_config_item("nostr.nsec", global)?; | ||
| 251 | git_repo.save_git_config_item("nostr.bunker-uri", &bunker.0, global)?; | ||
| 252 | git_repo.save_git_config_item("nostr.bunker-app-key", &bunker.1, global)?; | ||
| 253 | } | ||
| 254 | if let Some(nsec) = nsec { | ||
| 255 | git_repo.save_git_config_item("nostr.nsec", nsec, global)?; | ||
| 256 | git_repo.remove_git_config_item("nostr.bunker-uri", global)?; | ||
| 257 | git_repo.remove_git_config_item("nostr.bunker-app-key", global)?; | ||
| 258 | } | ||
| 259 | git_repo.save_git_config_item("nostr.npub", npub, global) | ||
| 260 | } | ||
| 261 | |||
| 150 | fn get_keys_with_password(git_repo: &Repo, password: &str) -> Result<nostr::Keys> { | 262 | fn get_keys_with_password(git_repo: &Repo, password: &str) -> Result<nostr::Keys> { |
| 151 | decrypt_key( | 263 | decrypt_key( |
| 152 | &git_repo | 264 | &git_repo |
| 153 | .get_git_config_item("nostr.nsec", false) | 265 | .get_git_config_item("nostr.nsec", None) |
| 154 | .context("failed get git config")? | 266 | .context("failed get git config")? |
| 155 | .context("git config item nostr.nsec doesn't exist so cannot decrypt it")?, | 267 | .context("git config item nostr.nsec doesn't exist so cannot decrypt it")?, |
| 156 | password, | 268 | password, |
| @@ -158,15 +270,74 @@ fn get_keys_with_password(git_repo: &Repo, password: &str) -> Result<nostr::Keys | |||
| 158 | .context("failed to decrypt stored nsec key with provided password") | 270 | .context("failed to decrypt stored nsec key with provided password") |
| 159 | } | 271 | } |
| 160 | 272 | ||
| 161 | fn get_keys_with_git_config_nsec_without_prompts(git_repo: &Repo) -> Result<nostr::Keys> { | 273 | async fn get_nip46_signer_from_uri_and_key(uri: &str, app_key: &str) -> Result<NostrSigner> { |
| 162 | let nsec = &git_repo | 274 | let term = console::Term::stderr(); |
| 163 | .get_git_config_item("nostr.nsec", false) | 275 | term.write_line("connecting to remote signer...")?; |
| 164 | .context("failed get git config")? | 276 | let uri = NostrConnectURI::parse(uri)?; |
| 165 | .context("git config item nostr.nsec doesn't exist")?; | 277 | let signer = NostrSigner::nip46( |
| 166 | if nsec.contains("ncryptsec") { | 278 | Nip46Signer::new( |
| 167 | bail!("git config item nostr.nsec is an ncryptsec") | 279 | uri, |
| 280 | nostr::Keys::from_str(app_key).context("invalid app key")?, | ||
| 281 | Duration::from_secs(30), | ||
| 282 | None, | ||
| 283 | ) | ||
| 284 | .await?, | ||
| 285 | ); | ||
| 286 | term.clear_last_lines(1)?; | ||
| 287 | Ok(signer) | ||
| 288 | } | ||
| 289 | |||
| 290 | async fn get_signer_with_git_config_nsec_or_bunker_without_prompts( | ||
| 291 | git_repo: &Repo, | ||
| 292 | ) -> Result<NostrSigner> { | ||
| 293 | if let Ok(local_nsec) = &git_repo | ||
| 294 | .get_git_config_item("nostr.nsec", Some(false)) | ||
| 295 | .context("failed get local git config")? | ||
| 296 | .context("git local config item nostr.nsec doesn't exist") | ||
| 297 | { | ||
| 298 | if local_nsec.contains("ncryptsec") { | ||
| 299 | bail!("git global config item nostr.nsec is an ncryptsec") | ||
| 300 | } | ||
| 301 | Ok(NostrSigner::Keys( | ||
| 302 | nostr::Keys::from_str(local_nsec).context("invalid nsec parameter")?, | ||
| 303 | )) | ||
| 304 | } else if let Ok((uri, app_key)) = get_git_config_bunker_uri_and_app_key(git_repo, Some(false)) | ||
| 305 | { | ||
| 306 | get_nip46_signer_from_uri_and_key(&uri, &app_key).await | ||
| 307 | } else if let Ok(global_nsec) = &git_repo | ||
| 308 | .get_git_config_item("nostr.nsec", Some(true)) | ||
| 309 | .context("failed get global git config")? | ||
| 310 | .context("git global config item nostr.nsec doesn't exist") | ||
| 311 | { | ||
| 312 | if global_nsec.contains("ncryptsec") { | ||
| 313 | bail!("git global config item nostr.nsec is an ncryptsec") | ||
| 314 | } | ||
| 315 | Ok(NostrSigner::Keys( | ||
| 316 | nostr::Keys::from_str(global_nsec).context("invalid nsec parameter")?, | ||
| 317 | )) | ||
| 318 | } else if let Ok((uri, app_key)) = get_git_config_bunker_uri_and_app_key(git_repo, Some(true)) { | ||
| 319 | get_nip46_signer_from_uri_and_key(&uri, &app_key).await | ||
| 320 | } else { | ||
| 321 | bail!("cannot get nsec or bunker from git config") | ||
| 168 | } | 322 | } |
| 169 | nostr::Keys::from_str(nsec).context("invalid nsec parameter") | 323 | } |
| 324 | |||
| 325 | fn get_git_config_bunker_uri_and_app_key( | ||
| 326 | git_repo: &Repo, | ||
| 327 | global: Option<bool>, | ||
| 328 | ) -> Result<(String, String)> { | ||
| 329 | Ok(( | ||
| 330 | git_repo | ||
| 331 | .get_git_config_item("nostr.bunker_url", global) | ||
| 332 | .context("failed get local git config")? | ||
| 333 | .context("git local config item nostr.bunker_url doesn't exist")? | ||
| 334 | .to_string(), | ||
| 335 | git_repo | ||
| 336 | .get_git_config_item("nostr.bunker-app-key", global) | ||
| 337 | .context("failed get local git config")? | ||
| 338 | .context("git local config item nostr.bunker-app-key doesn't exist")? | ||
| 339 | .to_string(), | ||
| 340 | )) | ||
| 170 | } | 341 | } |
| 171 | 342 | ||
| 172 | async fn fresh_login( | 343 | async fn fresh_login( |
| @@ -175,50 +346,119 @@ async fn fresh_login( | |||
| 175 | #[cfg(not(test))] client: Option<&Client>, | 346 | #[cfg(not(test))] client: Option<&Client>, |
| 176 | always_save: bool, | 347 | always_save: bool, |
| 177 | ) -> Result<(NostrSigner, UserRef)> { | 348 | ) -> Result<(NostrSigner, UserRef)> { |
| 349 | let mut public_key: Option<PublicKey> = None; | ||
| 178 | // prompt for nsec | 350 | // prompt for nsec |
| 179 | let mut prompt = "login with nsec"; | 351 | let mut prompt = "login with bunker uri / nsec"; |
| 180 | let keys = loop { | 352 | let signer = loop { |
| 181 | match nostr::Keys::from_str( | 353 | let input = Interactor::default() |
| 182 | &Interactor::default() | 354 | .input(PromptInputParms::default().with_prompt(prompt)) |
| 183 | .input(PromptInputParms::default().with_prompt(prompt)) | 355 | .context("failed to get nsec input from interactor")?; |
| 184 | .context("failed to get nsec input from interactor")?, | 356 | match nostr::Keys::from_str(&input) { |
| 185 | ) { | ||
| 186 | Ok(key) => { | 357 | Ok(key) => { |
| 187 | break key; | 358 | if let Err(error) = save_keys(git_repo, &key, always_save) { |
| 188 | } | 359 | println!("{error}"); |
| 189 | Err(_) => { | 360 | } |
| 190 | prompt = "invalid nsec. try again with nsec (or hex private key)"; | 361 | break NostrSigner::Keys(key); |
| 191 | } | 362 | } |
| 363 | Err(_) => match NostrConnectURI::parse(&input) { | ||
| 364 | Ok(_) => { | ||
| 365 | let app_key = Keys::generate().secret_key()?.to_secret_hex(); | ||
| 366 | match get_nip46_signer_from_uri_and_key(&input, &app_key).await { | ||
| 367 | Ok(signer) => { | ||
| 368 | let pub_key = fetch_public_key(&signer).await?; | ||
| 369 | if let Err(error) = | ||
| 370 | save_bunker(git_repo, &pub_key, &input, &app_key, always_save) | ||
| 371 | { | ||
| 372 | println!("{error}"); | ||
| 373 | } | ||
| 374 | public_key = Some(pub_key); | ||
| 375 | break signer; | ||
| 376 | } | ||
| 377 | Err(_) => { | ||
| 378 | prompt = "invalid. try again with nostr address / nsec"; | ||
| 379 | } | ||
| 380 | } | ||
| 381 | } | ||
| 382 | Err(_) => { | ||
| 383 | prompt = "invalid. try again with nostr address / nsec"; | ||
| 384 | } | ||
| 385 | }, | ||
| 192 | } | 386 | } |
| 193 | }; | 387 | }; |
| 388 | let public_key = if let Some(public_key) = public_key { | ||
| 389 | public_key | ||
| 390 | } else { | ||
| 391 | signer.public_key().await? | ||
| 392 | }; | ||
| 194 | // lookup profile | 393 | // lookup profile |
| 195 | // save keys | 394 | let user_ref = get_user_details(&public_key, client, git_repo).await?; |
| 196 | if let Err(error) = save_keys(git_repo, &keys, always_save) { | ||
| 197 | println!("{error}"); | ||
| 198 | } | ||
| 199 | let user_ref = get_user_details(&keys.public_key(), client, git_repo).await?; | ||
| 200 | print_logged_in_as(&user_ref, client.is_none())?; | 395 | print_logged_in_as(&user_ref, client.is_none())?; |
| 201 | Ok((NostrSigner::Keys(keys), user_ref)) | 396 | Ok((signer, user_ref)) |
| 202 | } | 397 | } |
| 203 | 398 | ||
| 204 | fn save_keys(git_repo: &Repo, keys: &nostr::Keys, always_save: bool) -> Result<()> { | 399 | fn save_bunker( |
| 205 | let store = always_save | 400 | git_repo: &Repo, |
| 401 | public_key: &PublicKey, | ||
| 402 | uri: &str, | ||
| 403 | app_key: &str, | ||
| 404 | always_save: bool, | ||
| 405 | ) -> Result<()> { | ||
| 406 | if always_save | ||
| 206 | || Interactor::default() | 407 | || Interactor::default() |
| 207 | .confirm(PromptConfirmParms::default().with_prompt("save login details?"))?; | 408 | .confirm(PromptConfirmParms::default().with_prompt("save login details?"))? |
| 409 | { | ||
| 410 | let global = !Interactor::default().confirm( | ||
| 411 | PromptConfirmParms::default() | ||
| 412 | .with_prompt("just for this repository?") | ||
| 413 | .with_default(false), | ||
| 414 | )?; | ||
| 415 | let npub = public_key.to_bech32()?; | ||
| 416 | if let Err(error) = save_to_git_config( | ||
| 417 | git_repo, | ||
| 418 | &npub, | ||
| 419 | &None, | ||
| 420 | &Some((uri.to_string(), app_key.to_string())), | ||
| 421 | global, | ||
| 422 | ) { | ||
| 423 | if global { | ||
| 424 | if Interactor::default().confirm( | ||
| 425 | PromptConfirmParms::default() | ||
| 426 | .with_prompt("save in repository git config?") | ||
| 427 | .with_default(true), | ||
| 428 | )? { | ||
| 429 | save_to_git_config( | ||
| 430 | git_repo, | ||
| 431 | &npub, | ||
| 432 | &None, | ||
| 433 | &Some((uri.to_string(), app_key.to_string())), | ||
| 434 | false, | ||
| 435 | )?; | ||
| 436 | } | ||
| 437 | } else { | ||
| 438 | Err(error)?; | ||
| 439 | } | ||
| 440 | }; | ||
| 441 | } | ||
| 442 | Ok(()) | ||
| 443 | } | ||
| 208 | 444 | ||
| 209 | let global = !Interactor::default().confirm( | 445 | fn save_keys(git_repo: &Repo, keys: &nostr::Keys, always_save: bool) -> Result<()> { |
| 210 | PromptConfirmParms::default() | 446 | if always_save |
| 211 | .with_prompt("just for this repository?") | 447 | || Interactor::default() |
| 212 | .with_default(false), | 448 | .confirm(PromptConfirmParms::default().with_prompt("save login details?"))? |
| 213 | )?; | 449 | { |
| 450 | let global = !Interactor::default().confirm( | ||
| 451 | PromptConfirmParms::default() | ||
| 452 | .with_prompt("just for this repository?") | ||
| 453 | .with_default(false), | ||
| 454 | )?; | ||
| 214 | 455 | ||
| 215 | let encrypt = Interactor::default().confirm( | 456 | let encrypt = Interactor::default().confirm( |
| 216 | PromptConfirmParms::default() | 457 | PromptConfirmParms::default() |
| 217 | .with_prompt("require password?") | 458 | .with_prompt("require password?") |
| 218 | .with_default(false), | 459 | .with_default(false), |
| 219 | )?; | 460 | )?; |
| 220 | 461 | ||
| 221 | if store { | ||
| 222 | let npub = keys.public_key().to_bech32()?; | 462 | let npub = keys.public_key().to_bech32()?; |
| 223 | let nsec_string = if encrypt { | 463 | let nsec_string = if encrypt { |
| 224 | let password = Interactor::default() | 464 | let password = Interactor::default() |
| @@ -233,22 +473,20 @@ fn save_keys(git_repo: &Repo, keys: &nostr::Keys, always_save: bool) -> Result<( | |||
| 233 | keys.secret_key()?.to_bech32()? | 473 | keys.secret_key()?.to_bech32()? |
| 234 | }; | 474 | }; |
| 235 | 475 | ||
| 236 | if let Err(error) = git_repo.save_git_config_item("nostr.nsec", &nsec_string, global) { | 476 | if let Err(error) = |
| 477 | save_to_git_config(git_repo, &npub, &Some(nsec_string.clone()), &None, global) | ||
| 478 | { | ||
| 237 | if global { | 479 | if global { |
| 238 | println!("failed to edit global git config instead"); | ||
| 239 | if Interactor::default().confirm( | 480 | if Interactor::default().confirm( |
| 240 | PromptConfirmParms::default() | 481 | PromptConfirmParms::default() |
| 241 | .with_prompt("save in repository git config?") | 482 | .with_prompt("save in repository git config?") |
| 242 | .with_default(true), | 483 | .with_default(true), |
| 243 | )? { | 484 | )? { |
| 244 | git_repo.save_git_config_item("nostr.nsec", &nsec_string, false)?; | 485 | save_to_git_config(git_repo, &npub, &Some(nsec_string.clone()), &None, false)?; |
| 245 | git_repo.save_git_config_item("nostr.npub", &npub, false)?; | ||
| 246 | } | 486 | } |
| 247 | } else { | 487 | } else { |
| 248 | bail!(error) | 488 | Err(error)?; |
| 249 | } | 489 | } |
| 250 | } else { | ||
| 251 | git_repo.save_git_config_item("nostr.npub", &npub, global)?; | ||
| 252 | }; | 490 | }; |
| 253 | }; | 491 | }; |
| 254 | Ok(()) | 492 | Ok(()) |
| @@ -256,7 +494,7 @@ fn save_keys(git_repo: &Repo, keys: &nostr::Keys, always_save: bool) -> Result<( | |||
| 256 | 494 | ||
| 257 | fn get_config_item(git_repo: &Repo, name: &str) -> Result<String> { | 495 | fn get_config_item(git_repo: &Repo, name: &str) -> Result<String> { |
| 258 | git_repo | 496 | git_repo |
| 259 | .get_git_config_item(name, false) | 497 | .get_git_config_item(name, None) |
| 260 | .context("failed get git config")? | 498 | .context("failed get git config")? |
| 261 | .context(format!("git config item {name} doesn't exist")) | 499 | .context(format!("git config item {name} doesn't exist")) |
| 262 | } | 500 | } |
| @@ -350,6 +588,10 @@ async fn get_user_details( | |||
| 350 | println!("searching for profile and relay updates..."); | 588 | println!("searching for profile and relay updates..."); |
| 351 | } | 589 | } |
| 352 | let database = SQLiteDatabase::open(if std::env::var("NGITTEST").is_err() { | 590 | let database = SQLiteDatabase::open(if std::env::var("NGITTEST").is_err() { |
| 591 | create_dir_all(get_dirs()?.config_dir()).context(format!( | ||
| 592 | "cannot create cache directory in: {:?}", | ||
| 593 | get_dirs()?.config_dir() | ||
| 594 | ))?; | ||
| 353 | get_dirs()?.config_dir().join("cache.sqlite") | 595 | get_dirs()?.config_dir().join("cache.sqlite") |
| 354 | } else { | 596 | } else { |
| 355 | git_repo.get_path()?.join(".git/test-global-cache.sqlite") | 597 | git_repo.get_path()?.join(".git/test-global-cache.sqlite") |