diff options
Diffstat (limited to 'src/bin/rbw-agent/actions.rs')
-rw-r--r-- | src/bin/rbw-agent/actions.rs | 109 |
1 files changed, 93 insertions, 16 deletions
diff --git a/src/bin/rbw-agent/actions.rs b/src/bin/rbw-agent/actions.rs index 4f4096e..1cc71c3 100644 --- a/src/bin/rbw-agent/actions.rs +++ b/src/bin/rbw-agent/actions.rs @@ -1,5 +1,84 @@ use anyhow::Context as _; +pub async fn register( + sock: &mut crate::sock::Sock, + tty: Option<&str>, +) -> anyhow::Result<()> { + let db = load_db().await.unwrap_or_else(|_| rbw::db::Db::new()); + + if db.needs_login() { + let url_str = config_base_url().await?; + let url = reqwest::Url::parse(&url_str) + .context("failed to parse base url")?; + let host = if let Some(host) = url.host_str() { + host + } else { + return Err(anyhow::anyhow!( + "couldn't find host in rbw base url {}", + url_str + )); + }; + + let email = config_email().await?; + + let mut err_msg = None; + for i in 1_u8..=3 { + let err = if i > 1 { + // this unwrap is safe because we only ever continue the loop + // if we have set err_msg + Some(format!("{} (attempt {}/3)", err_msg.unwrap(), i)) + } else { + None + }; + let client_id = rbw::pinentry::getpin( + &config_pinentry().await?, + "API key client__id", + &format!("Log in to {}", host), + err.as_deref(), + tty, + false, + ) + .await + .context("failed to read client_id from pinentry")?; + let client_secret = rbw::pinentry::getpin( + &config_pinentry().await?, + "API key client__secret", + &format!("Log in to {}", host), + err.as_deref(), + tty, + false, + ) + .await + .context("failed to read client_secret from pinentry")?; + let apikey = rbw::locked::ApiKey::new(client_id, client_secret); + match rbw::actions::register(&email, apikey.clone()).await { + Ok(()) => { + break; + } + Err(rbw::error::Error::IncorrectPassword { message }) => { + if i == 3 { + return Err(rbw::error::Error::IncorrectPassword { + message, + }) + .context("failed to log in to bitwarden instance"); + } else { + err_msg = Some(message); + continue; + } + } + Err(e) => { + return Err(e) + .context("failed to log in to bitwarden instance") + } + } + } + } + + respond_ack(sock).await?; + + Ok(()) +} + pub async fn login( sock: &mut crate::sock::Sock, state: std::sync::Arc<tokio::sync::RwLock<crate::agent::State>>, @@ -37,16 +116,18 @@ pub async fn login( &format!("Log in to {}", host), err.as_deref(), tty, + true, ) .await .context("failed to read password from pinentry")?; - match rbw::actions::login(&email, &password, None, None).await { + match rbw::actions::login(&email, password.clone(), None, None) + .await + { Ok(( access_token, refresh_token, iterations, protected_key, - _, )) => { login_success( sock, @@ -74,7 +155,7 @@ pub async fn login( ) = two_factor( tty, &email, - &password, + password.clone(), rbw::api::TwoFactorProviderType::Authenticator, ) .await?; @@ -122,7 +203,7 @@ pub async fn login( async fn two_factor( tty: Option<&str>, email: &str, - password: &rbw::locked::Password, + password: rbw::locked::Password, provider: rbw::api::TwoFactorProviderType, ) -> anyhow::Result<(String, String, u32, String)> { let mut err_msg = None; @@ -140,26 +221,21 @@ async fn two_factor( "Enter the 6 digit verification code from your authenticator app.", err.as_deref(), tty, + true, ) .await .context("failed to read code from pinentry")?; let code = std::str::from_utf8(code.password()) .context("code was not valid utf8")?; match rbw::actions::login( - &email, - &password, + email, + password.clone(), Some(code), Some(provider), ) .await { - Ok(( - access_token, - refresh_token, - iterations, - protected_key, - _, - )) => { + Ok((access_token, refresh_token, iterations, protected_key)) => { return Ok(( access_token, refresh_token, @@ -300,6 +376,7 @@ pub async fn unlock( "Unlock the local database", err.as_deref(), tty, + true, ) .await .context("failed to read password from pinentry")?; @@ -430,7 +507,7 @@ pub async fn decrypt( .context("failed to parse encrypted secret")?; let plaintext = String::from_utf8( cipherstring - .decrypt_symmetric(&keys) + .decrypt_symmetric(keys) .context("failed to decrypt encrypted secret")?, ) .context("failed to parse decrypted secret")?; @@ -512,7 +589,7 @@ async fn config_email() -> anyhow::Result<String> { async fn load_db() -> anyhow::Result<rbw::db::Db> { let config = rbw::config::Config::load_async().await?; if let Some(email) = &config.email { - rbw::db::Db::load_async(&config.server_name(), &email) + rbw::db::Db::load_async(&config.server_name(), email) .await .map_err(anyhow::Error::new) } else { @@ -523,7 +600,7 @@ async fn load_db() -> anyhow::Result<rbw::db::Db> { async fn save_db(db: &rbw::db::Db) -> anyhow::Result<()> { let config = rbw::config::Config::load_async().await?; if let Some(email) = &config.email { - db.save_async(&config.server_name(), &email) + db.save_async(&config.server_name(), email) .await .map_err(anyhow::Error::new) } else { |