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.rs284
1 files changed, 208 insertions, 76 deletions
diff --git a/src/bin/rbw-agent/actions.rs b/src/bin/rbw-agent/actions.rs
index cb30551..d29321c 100644
--- a/src/bin/rbw-agent/actions.rs
+++ b/src/bin/rbw-agent/actions.rs
@@ -4,6 +4,7 @@ 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());
@@ -23,49 +24,66 @@ pub async fn login(
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 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, None, None).await {
- Ok((
- access_token,
- refresh_token,
- iterations,
- protected_key,
- )) => {
- login_success(
- sock,
- state,
+ 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,
+ )
+ .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((
access_token,
refresh_token,
iterations,
protected_key,
- password,
- db,
- email,
- )
- .await?;
- break;
- }
- Err(rbw::error::Error::TwoFactorRequired { providers }) => {
- if providers.contains(
- &rbw::api::TwoFactorProviderType::Authenticator,
- ) {
- let (
+ )) => {
+ 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 (
access_token,
refresh_token,
iterations,
@@ -73,10 +91,79 @@ pub async fn login(
) = two_factor(
tty,
&email,
- &password,
+ creds,
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,
@@ -84,30 +171,68 @@ pub async fn login(
refresh_token,
iterations,
protected_key,
- password,
+ 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(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(e) => {
- return Err(e)
- .context("failed to log in to bitwarden instance")
}
}
}
@@ -121,7 +246,7 @@ pub async fn login(
async fn two_factor(
tty: Option<&str>,
email: &str,
- password: &rbw::locked::Password,
+ creds: rbw::locked::LoginCredentials,
provider: rbw::api::TwoFactorProviderType,
) -> anyhow::Result<(String, String, u32, String)> {
let mut err_msg = None;
@@ -144,8 +269,13 @@ async fn two_factor(
.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, Some(code), Some(provider))
- .await
+ match rbw::actions::login(
+ email,
+ creds.clone(),
+ Some(code),
+ Some(provider),
+ )
+ .await
{
Ok((access_token, refresh_token, iterations, protected_key)) => {
return Ok((
@@ -196,7 +326,7 @@ async fn login_success(
refresh_token: String,
iterations: u32,
protected_key: String,
- password: rbw::locked::Password,
+ password: Option<rbw::locked::Password>,
mut db: rbw::db::Db,
email: String,
) -> anyhow::Result<()> {
@@ -218,23 +348,25 @@ async fn login_success(
));
};
- let res = rbw::actions::unlock(
- &email,
- &password,
- iterations,
- &protected_key,
- &protected_private_key,
- &db.protected_org_keys,
- )
- .await;
+ if let Some(password) = password {
+ 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);
+ 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"),
}
- Err(e) => return Err(e).context("failed to unlock database"),
}
Ok(())