From 0c256a0e055077accbca114d79bb361d1ca14ef3 Mon Sep 17 00:00:00 2001 From: Carson McManus Date: Thu, 28 Sep 2023 17:52:42 -0400 Subject: [PATCH] add `--password` argument and `STEAMGUARD_CLI_STEAM_PASSWORD` environment variable (#326) closes #324 --- src/commands.rs | 15 ++++++++++++++- src/commands/code.rs | 1 + src/commands/decrypt.rs | 7 ++++++- src/commands/encrypt.rs | 7 ++++++- src/commands/import.rs | 7 ++++++- src/commands/qr.rs | 1 + src/commands/qr_login.rs | 5 +++-- src/commands/remove.rs | 3 ++- src/commands/setup.rs | 9 +++++++-- src/commands/trade.rs | 5 +++-- src/login.rs | 14 ++++++++++++-- src/main.rs | 4 ++-- 12 files changed, 63 insertions(+), 15 deletions(-) diff --git a/src/commands.rs b/src/commands.rs index 9fcb4a9..fca7cd4 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -44,7 +44,12 @@ pub(crate) trait ManifestCommand where T: Transport, { - fn execute(&self, transport: T, manager: &mut AccountManager) -> anyhow::Result<()>; + fn execute( + &self, + transport: T, + manager: &mut AccountManager, + args: &GlobalArgs, + ) -> anyhow::Result<()>; } /// A command that operates on individual accounts. @@ -57,6 +62,7 @@ where transport: T, manager: &mut AccountManager, accounts: Vec>>, + args: &GlobalArgs, ) -> anyhow::Result<()>; } @@ -92,6 +98,13 @@ pub(crate) struct GlobalArgs { long_help = "Select the account you want by steam username. Case-sensitive. By default, the first account in the manifest is selected." )] pub username: Option, + #[clap( + long, + conflicts_with = "all", + help = "Steam account password. You really shouldn't use this if you can avoid it.", + env = "STEAMGUARD_CLI_STEAM_PASSWORD" + )] + pub password: Option, #[clap( short, long, diff --git a/src/commands/code.rs b/src/commands/code.rs index 9429c72..fc1273f 100644 --- a/src/commands/code.rs +++ b/src/commands/code.rs @@ -29,6 +29,7 @@ where transport: T, _manager: &mut AccountManager, accounts: Vec>>, + _args: &GlobalArgs, ) -> anyhow::Result<()> { let server_time = if self.offline { SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs() diff --git a/src/commands/decrypt.rs b/src/commands/decrypt.rs index 94fd5f4..0ff498e 100644 --- a/src/commands/decrypt.rs +++ b/src/commands/decrypt.rs @@ -12,7 +12,12 @@ impl ManifestCommand for DecryptCommand where T: Transport, { - fn execute(&self, _transport: T, manager: &mut AccountManager) -> anyhow::Result<()> { + fn execute( + &self, + _transport: T, + manager: &mut AccountManager, + _args: &GlobalArgs, + ) -> anyhow::Result<()> { load_accounts_with_prompts(manager)?; #[cfg(feature = "keyring")] diff --git a/src/commands/encrypt.rs b/src/commands/encrypt.rs index 09629d7..7e3870e 100644 --- a/src/commands/encrypt.rs +++ b/src/commands/encrypt.rs @@ -16,7 +16,12 @@ impl ManifestCommand for EncryptCommand where T: Transport, { - fn execute(&self, _transport: T, manager: &mut AccountManager) -> anyhow::Result<()> { + fn execute( + &self, + _transport: T, + manager: &mut AccountManager, + _args: &GlobalArgs, + ) -> anyhow::Result<()> { if !manager.has_passkey() { let passkey: Option; loop { diff --git a/src/commands/import.rs b/src/commands/import.rs index 93c9055..b7c4770 100644 --- a/src/commands/import.rs +++ b/src/commands/import.rs @@ -19,7 +19,12 @@ impl ManifestCommand for ImportCommand where T: Transport, { - fn execute(&self, _transport: T, manager: &mut AccountManager) -> anyhow::Result<()> { + fn execute( + &self, + _transport: T, + manager: &mut AccountManager, + _args: &GlobalArgs, + ) -> anyhow::Result<()> { for file_path in self.files.iter() { debug!("loading entry: {:?}", file_path); match manager.import_account(file_path) { diff --git a/src/commands/qr.rs b/src/commands/qr.rs index d647518..dfa48ea 100644 --- a/src/commands/qr.rs +++ b/src/commands/qr.rs @@ -27,6 +27,7 @@ where _transport: T, _manager: &mut AccountManager, accounts: Vec>>, + _args: &GlobalArgs, ) -> anyhow::Result<()> { use anyhow::Context; diff --git a/src/commands/qr_login.rs b/src/commands/qr_login.rs index 884d5ff..5d7c348 100644 --- a/src/commands/qr_login.rs +++ b/src/commands/qr_login.rs @@ -26,6 +26,7 @@ where transport: T, _manager: &mut AccountManager, accounts: Vec>>, + args: &GlobalArgs, ) -> anyhow::Result<()> { ensure!( accounts.len() == 1, @@ -37,7 +38,7 @@ where info!("Approving login to {}", account.account_name); if account.tokens.is_none() { - crate::do_login(transport.clone(), &mut account)?; + crate::do_login(transport.clone(), &mut account, args.password.clone())?; } loop { @@ -57,7 +58,7 @@ where } Err(QrApproverError::Unauthorized) => { warn!("tokens are invalid. Attempting to log in again."); - crate::do_login(transport.clone(), &mut account)?; + crate::do_login(transport.clone(), &mut account, args.password.clone())?; } Err(e) => { error!("Failed to approve login: {}", e); diff --git a/src/commands/remove.rs b/src/commands/remove.rs index bae01a4..35f7e15 100644 --- a/src/commands/remove.rs +++ b/src/commands/remove.rs @@ -20,6 +20,7 @@ where transport: T, manager: &mut AccountManager, accounts: Vec>>, + args: &GlobalArgs, ) -> anyhow::Result<()> { eprintln!( "This will remove the mobile authenticator from {} accounts: {}", @@ -54,7 +55,7 @@ where } Err(RemoveAuthenticatorError::TransportError(TransportError::Unauthorized)) => { error!("Account {} is not logged in", account.account_name); - crate::do_login(transport.clone(), &mut account)?; + crate::do_login(transport.clone(), &mut account, args.password.clone())?; continue; } Err(RemoveAuthenticatorError::IncorrectRevocationCode { diff --git a/src/commands/setup.rs b/src/commands/setup.rs index 5659eed..283633f 100644 --- a/src/commands/setup.rs +++ b/src/commands/setup.rs @@ -18,7 +18,12 @@ impl ManifestCommand for SetupCommand where T: Transport + Clone, { - fn execute(&self, transport: T, manager: &mut AccountManager) -> anyhow::Result<()> { + fn execute( + &self, + transport: T, + manager: &mut AccountManager, + args: &GlobalArgs, + ) -> anyhow::Result<()> { eprintln!("Log in to the account that you want to link to steamguard-cli"); eprint!("Username: "); let username = tui::prompt().to_lowercase(); @@ -30,7 +35,7 @@ where ); } info!("Logging in to {}", username); - let tokens = crate::do_login_raw(transport.clone(), username) + let tokens = crate::do_login_raw(transport.clone(), username, args.password.clone()) .expect("Failed to log in. Account has not been linked."); info!("Adding authenticator..."); diff --git a/src/commands/trade.rs b/src/commands/trade.rs index 2aa27e7..4aaab6b 100644 --- a/src/commands/trade.rs +++ b/src/commands/trade.rs @@ -34,13 +34,14 @@ where transport: T, manager: &mut AccountManager, accounts: Vec>>, + args: &GlobalArgs, ) -> anyhow::Result<()> { for a in accounts { let mut account = a.lock().unwrap(); if !account.is_logged_in() { info!("Account does not have tokens, logging in"); - crate::do_login(transport.clone(), &mut account)?; + crate::do_login(transport.clone(), &mut account, args.password.clone())?; } info!("{}: Checking for trade confirmations", account.account_name); @@ -55,7 +56,7 @@ where } Err(ConfirmerError::InvalidTokens) => { info!("obtaining new tokens"); - crate::do_login(transport.clone(), &mut account)?; + crate::do_login(transport.clone(), &mut account, args.password.clone())?; } Err(err) => { error!("Failed to get trade confirmations: {}", err); diff --git a/src/login.rs b/src/login.rs index 2753968..8086645 100644 --- a/src/login.rs +++ b/src/login.rs @@ -17,6 +17,7 @@ use crate::tui; pub fn do_login( transport: T, account: &mut SteamGuardAccount, + password: Option, ) -> anyhow::Result<()> { if let Some(tokens) = account.tokens.as_mut() { info!("Refreshing access token..."); @@ -44,7 +45,11 @@ pub fn do_login( account.account_name = tui::prompt(); } let _ = std::io::stdout().flush(); - let password = tui::prompt_password()?; + let password = if let Some(p) = password { + p + } else { + tui::prompt_password()? + }; if !password.expose_secret().is_empty() { debug!("password is present"); } else { @@ -65,9 +70,14 @@ pub fn do_login( pub fn do_login_raw( transport: T, username: String, + password: Option, ) -> anyhow::Result { let _ = std::io::stdout().flush(); - let password = tui::prompt_password()?; + let password = if let Some(p) = password { + p + } else { + tui::prompt_password()? + }; if !password.expose_secret().is_empty() { debug!("password is present"); } else { diff --git a/src/main.rs b/src/main.rs index ce88a31..86f24b9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -232,7 +232,7 @@ fn run(args: commands::Args) -> anyhow::Result<()> { let transport = WebApiTransport::new(http_client); if let CommandType::Manifest(cmd) = cmd { - cmd.execute(transport, &mut manager)?; + cmd.execute(transport, &mut manager, &globalargs)?; return Ok(()); } @@ -269,7 +269,7 @@ fn run(args: commands::Args) -> anyhow::Result<()> { ); if let CommandType::Account(cmd) = cmd { - return cmd.execute(transport, &mut manager, selected_accounts); + return cmd.execute(transport, &mut manager, selected_accounts, &globalargs); } Ok(())