aboutsummaryrefslogtreecommitdiffstats
path: root/src/bin/rbw-agent/actions.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/bin/rbw-agent/actions.rs')
-rw-r--r--src/bin/rbw-agent/actions.rs354
1 files changed, 153 insertions, 201 deletions
diff --git a/src/bin/rbw-agent/actions.rs b/src/bin/rbw-agent/actions.rs
index d29321c..5918e2c 100644
--- a/src/bin/rbw-agent/actions.rs
+++ b/src/bin/rbw-agent/actions.rs
@@ -1,10 +1,86 @@
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,
+ )
+ .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,
+ )
+ .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>>,
tty: Option<&str>,
- apikey: bool,
) -> anyhow::Result<()> {
let db = load_db().await.unwrap_or_else(|_| rbw::db::Db::new());
@@ -24,66 +100,51 @@ pub async fn login(
let email = config_email().await?;
let mut err_msg = None;
- if apikey {
- 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,
- )
- .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,
- )
+ 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 password = rbw::pinentry::getpin(
+ &config_pinentry().await?,
+ "Master Password",
+ &format!("Log in to {}", host),
+ err.as_deref(),
+ tty,
+ )
+ .await
+ .context("failed to read password from pinentry")?;
+ match rbw::actions::login(&email, password.clone(), None, None)
.await
- .context("failed to read client_secret from pinentry")?;
- let creds = rbw::locked::LoginCredentials::from_apikey(
- rbw::locked::ApiKey::new(client_id, client_secret),
- );
- match rbw::actions::login(&email, creds.clone(), None, None)
- .await
- {
- Ok((
+ {
+ Ok((
+ access_token,
+ refresh_token,
+ iterations,
+ protected_key,
+ )) => {
+ login_success(
+ sock,
+ state,
access_token,
refresh_token,
iterations,
protected_key,
- )) => {
- login_success(
- sock,
- state,
- access_token,
- refresh_token,
- iterations,
- protected_key,
- None,
- db,
- email,
- )
- .await?;
- break;
- }
- Err(rbw::error::Error::TwoFactorRequired {
- providers,
- }) => {
- if providers.contains(
- &rbw::api::TwoFactorProviderType::Authenticator,
- ) {
- let (
+ password,
+ db,
+ email,
+ )
+ .await?;
+ break;
+ }
+ Err(rbw::error::Error::TwoFactorRequired { providers }) => {
+ if providers.contains(
+ &rbw::api::TwoFactorProviderType::Authenticator,
+ ) {
+ let (
access_token,
refresh_token,
iterations,
@@ -91,79 +152,10 @@ pub async fn login(
) = two_factor(
tty,
&email,
- creds,
+ password.clone(),
rbw::api::TwoFactorProviderType::Authenticator,
)
.await?;
- login_success(
- sock,
- state,
- access_token,
- refresh_token,
- iterations,
- protected_key,
- None,
- db,
- email,
- )
- .await?;
- break;
- } else {
- return Err(anyhow::anyhow!("TODO"));
- }
- }
- 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",
- )
- }
- }
- }
- } else {
- 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 password = rbw::pinentry::getpin(
- &config_pinentry().await?,
- "Master Password",
- &format!("Log in to {}", host),
- err.as_deref(),
- tty,
- )
- .await
- .context("failed to read password from pinentry")?;
- let creds = rbw::locked::LoginCredentials::from_password(
- password.clone(),
- );
- match rbw::actions::login(&email, creds.clone(), None, None)
- .await
- {
- Ok((
- access_token,
- refresh_token,
- iterations,
- protected_key,
- )) => {
login_success(
sock,
state,
@@ -171,69 +163,31 @@ pub async fn login(
refresh_token,
iterations,
protected_key,
- Some(password),
+ password,
db,
email,
)
.await?;
break;
+ } else {
+ return Err(anyhow::anyhow!("TODO"));
}
- Err(rbw::error::Error::TwoFactorRequired {
- providers,
- }) => {
- if providers.contains(
- &rbw::api::TwoFactorProviderType::Authenticator,
- ) {
- let (
- access_token,
- refresh_token,
- iterations,
- protected_key,
- ) = two_factor(
- tty,
- &email,
- creds.clone(),
- rbw::api::TwoFactorProviderType::Authenticator,
- )
- .await?;
- login_success(
- sock,
- state,
- access_token,
- refresh_token,
- iterations,
- protected_key,
- Some(password),
- db,
- email,
- )
- .await?;
- break;
- } else {
- return Err(anyhow::anyhow!("TODO"));
- }
- }
- 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",
- )
+ }
+ 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")
+ }
}
}
}
@@ -246,7 +200,7 @@ pub async fn login(
async fn two_factor(
tty: Option<&str>,
email: &str,
- creds: rbw::locked::LoginCredentials,
+ password: rbw::locked::Password,
provider: rbw::api::TwoFactorProviderType,
) -> anyhow::Result<(String, String, u32, String)> {
let mut err_msg = None;
@@ -271,7 +225,7 @@ async fn two_factor(
.context("code was not valid utf8")?;
match rbw::actions::login(
email,
- creds.clone(),
+ password.clone(),
Some(code),
Some(provider),
)
@@ -326,7 +280,7 @@ async fn login_success(
refresh_token: String,
iterations: u32,
protected_key: String,
- password: Option<rbw::locked::Password>,
+ password: rbw::locked::Password,
mut db: rbw::db::Db,
email: String,
) -> anyhow::Result<()> {
@@ -348,25 +302,23 @@ async fn login_success(
));
};
- if let Some(password) = password {
- let res = rbw::actions::unlock(
- &email,
- &password,
- iterations,
- &protected_key,
- &protected_private_key,
- &db.protected_org_keys,
- )
- .await;
+ let res = rbw::actions::unlock(
+ &email,
+ &password,
+ iterations,
+ &protected_key,
+ &protected_private_key,
+ &db.protected_org_keys,
+ )
+ .await;
- match res {
- Ok((keys, org_keys)) => {
- let mut state = state.write().await;
- state.priv_key = Some(keys);
- state.org_keys = Some(org_keys);
- }
- Err(e) => return Err(e).context("failed to unlock database"),
+ match res {
+ Ok((keys, org_keys)) => {
+ let mut state = state.write().await;
+ state.priv_key = Some(keys);
+ state.org_keys = Some(org_keys);
}
+ Err(e) => return Err(e).context("failed to unlock database"),
}
Ok(())