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.rs109
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 {