Skip to content

Commit

Permalink
add HTTP proxy support (#264)
Browse files Browse the repository at this point in the history
- allow extracting the http client from web api transport
- refactor most commands so that they can accept an external transport
- update confirmer to use transport's http client
- update remove command
- update TwoFactorClient so it doesn't need to be mutable
- update get_server_time so it requires a transport
- update remove_authenticator
- update login proceedure to use given transport
- update usages of do_login
- update setup
- update trade
- update code command
- update qr-login command
- make borrowcheck happy
- make WebApiTransport Clone and remove Default impl
- remove dead code
- fix lints

closes #177
  • Loading branch information
dyc3 authored Jul 2, 2023
1 parent 1d99bae commit fe663cf
Show file tree
Hide file tree
Showing 17 changed files with 164 additions and 105 deletions.
32 changes: 25 additions & 7 deletions src/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use clap::{clap_derive::ArgEnum, Parser};
use clap_complete::Shell;
use secrecy::SecretString;
use std::str::FromStr;
use steamguard::SteamGuardAccount;
use steamguard::{transport::Transport, SteamGuardAccount};

use crate::AccountManager;

Expand Down Expand Up @@ -40,23 +40,33 @@ pub(crate) trait ConstCommand {
}

/// A command that operates the manifest as a whole
pub(crate) trait ManifestCommand {
fn execute(&self, manager: &mut AccountManager) -> anyhow::Result<()>;
pub(crate) trait ManifestCommand<T>
where
T: Transport,
{
fn execute(&self, transport: T, manager: &mut AccountManager) -> anyhow::Result<()>;
}

/// A command that operates on individual accounts.
pub(crate) trait AccountCommand {
pub(crate) trait AccountCommand<T>
where
T: Transport,
{
fn execute(
&self,
transport: T,
manager: &mut AccountManager,
accounts: Vec<Arc<Mutex<SteamGuardAccount>>>,
) -> anyhow::Result<()>;
}

pub(crate) enum CommandType {
pub(crate) enum CommandType<T>
where
T: Transport,
{
Const(Box<dyn ConstCommand>),
Manifest(Box<dyn ManifestCommand>),
Account(Box<dyn AccountCommand>),
Manifest(Box<dyn ManifestCommand<T>>),
Account(Box<dyn AccountCommand<T>>),
}

#[derive(Debug, Clone, Parser)]
Expand Down Expand Up @@ -114,6 +124,14 @@ pub(crate) struct GlobalArgs {
long_help = "Disable checking for updates. By default, steamguard-cli will check for updates every now and then. This can be disabled with this flag."
)]
pub no_update_check: bool,

#[clap(
long,
env = "HTTP_PROXY",
help = "Use a proxy for HTTP requests.",
long_help = "Use a proxy for HTTP requests. This is useful if you are behind a firewall and need to use a proxy to access the internet."
)]
pub http_proxy: Option<String>,
}

#[derive(Debug, Clone, Parser)]
Expand Down
8 changes: 6 additions & 2 deletions src/commands/code.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,20 @@ pub struct CodeCommand {
pub offline: bool,
}

impl AccountCommand for CodeCommand {
impl<T> AccountCommand<T> for CodeCommand
where
T: Transport,
{
fn execute(
&self,
transport: T,
_manager: &mut AccountManager,
accounts: Vec<Arc<Mutex<SteamGuardAccount>>>,
) -> anyhow::Result<()> {
let server_time = if self.offline {
SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs()
} else {
steamapi::get_server_time()?.server_time()
steamapi::get_server_time(transport)?.server_time()
};
debug!("Time used to generate codes: {}", server_time);

Expand Down
7 changes: 5 additions & 2 deletions src/commands/decrypt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,11 @@ use super::*;
#[clap(about = "Decrypt all maFiles")]
pub struct DecryptCommand;

impl ManifestCommand for DecryptCommand {
fn execute(&self, manager: &mut AccountManager) -> anyhow::Result<()> {
impl<T> ManifestCommand<T> for DecryptCommand
where
T: Transport,
{
fn execute(&self, _transport: T, manager: &mut AccountManager) -> anyhow::Result<()> {
load_accounts_with_prompts(manager)?;
for mut entry in manager.iter_mut() {
entry.encryption = None;
Expand Down
7 changes: 5 additions & 2 deletions src/commands/encrypt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,11 @@ use super::*;
#[clap(about = "Encrypt all maFiles")]
pub struct EncryptCommand;

impl ManifestCommand for EncryptCommand {
fn execute(&self, manager: &mut AccountManager) -> anyhow::Result<()> {
impl<T> ManifestCommand<T> for EncryptCommand
where
T: Transport,
{
fn execute(&self, _transport: T, manager: &mut AccountManager) -> anyhow::Result<()> {
if !manager.has_passkey() {
let mut passkey;
loop {
Expand Down
7 changes: 5 additions & 2 deletions src/commands/import.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,11 @@ pub struct ImportCommand {
pub files: Vec<String>,
}

impl ManifestCommand for ImportCommand {
fn execute(&self, manager: &mut AccountManager) -> anyhow::Result<()> {
impl<T> ManifestCommand<T> for ImportCommand
where
T: Transport,
{
fn execute(&self, _transport: T, manager: &mut AccountManager) -> anyhow::Result<()> {
for file_path in self.files.iter() {
if self.sda {
let path = Path::new(&file_path);
Expand Down
6 changes: 5 additions & 1 deletion src/commands/qr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,13 @@ pub struct QrCommand {
pub ascii: bool,
}

impl AccountCommand for QrCommand {
impl<T> AccountCommand<T> for QrCommand
where
T: Transport,
{
fn execute(
&self,
_transport: T,
_manager: &mut AccountManager,
accounts: Vec<Arc<Mutex<SteamGuardAccount>>>,
) -> anyhow::Result<()> {
Expand Down
14 changes: 9 additions & 5 deletions src/commands/qr_login.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::sync::{Arc, Mutex};

use log::*;
use steamguard::{transport::WebApiTransport, QrApprover, QrApproverError};
use steamguard::{QrApprover, QrApproverError};

use crate::AccountManager;

Expand All @@ -17,9 +17,13 @@ pub struct QrLoginCommand {
pub url: String,
}

impl AccountCommand for QrLoginCommand {
impl<T> AccountCommand<T> for QrLoginCommand
where
T: Transport + Clone,
{
fn execute(
&self,
transport: T,
_manager: &mut AccountManager,
accounts: Vec<Arc<Mutex<SteamGuardAccount>>>,
) -> anyhow::Result<()> {
Expand All @@ -33,7 +37,7 @@ impl AccountCommand for QrLoginCommand {
info!("Approving login to {}", account.account_name);

if account.tokens.is_none() {
crate::do_login(&mut account)?;
crate::do_login(transport.clone(), &mut account)?;
}

loop {
Expand All @@ -42,15 +46,15 @@ impl AccountCommand for QrLoginCommand {
return Err(anyhow!("No tokens found for {}", account.account_name));
};

let mut approver = QrApprover::new(WebApiTransport::default(), tokens);
let mut approver = QrApprover::new(transport.clone(), tokens);
match approver.approve(&account, &self.url) {
Ok(_) => {
info!("Login approved.");
break;
}
Err(QrApproverError::Unauthorized) => {
warn!("tokens are invalid. Attempting to log in again.");
crate::do_login(&mut account)?;
crate::do_login(transport.clone(), &mut account)?;
}
Err(e) => {
error!("Failed to approve login: {}", e);
Expand Down
13 changes: 9 additions & 4 deletions src/commands/remove.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::sync::{Arc, Mutex};

use log::*;
use steamguard::{transport::TransportError, RemoveAuthenticatorError};
use steamguard::{steamapi::TwoFactorClient, transport::TransportError, RemoveAuthenticatorError};

use crate::{errors::UserError, tui, AccountManager};

Expand All @@ -11,9 +11,13 @@ use super::*;
#[clap(about = "Remove the authenticator from an account.")]
pub struct RemoveCommand;

impl AccountCommand for RemoveCommand {
impl<T> AccountCommand<T> for RemoveCommand
where
T: Transport + Clone,
{
fn execute(
&self,
transport: T,
manager: &mut AccountManager,
accounts: Vec<Arc<Mutex<SteamGuardAccount>>>,
) -> anyhow::Result<()> {
Expand All @@ -38,18 +42,19 @@ impl AccountCommand for RemoveCommand {
let mut successful = vec![];
for a in accounts {
let mut account = a.lock().unwrap();
let client = TwoFactorClient::new(transport.clone());

let mut revocation: Option<String> = None;
loop {
match account.remove_authenticator(revocation.as_ref()) {
match account.remove_authenticator(&client, revocation.as_ref()) {
Ok(_) => {
info!("Removed authenticator from {}", account.account_name);
successful.push(account.account_name.clone());
break;
}
Err(RemoveAuthenticatorError::TransportError(TransportError::Unauthorized)) => {
error!("Account {} is not logged in", account.account_name);
crate::do_login(&mut account)?;
crate::do_login(transport.clone(), &mut account)?;
continue;
}
Err(RemoveAuthenticatorError::IncorrectRevocationCode {
Expand Down
21 changes: 12 additions & 9 deletions src/commands/setup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use phonenumber::PhoneNumber;
use secrecy::ExposeSecret;
use steamguard::{
accountlinker::AccountLinkSuccess, phonelinker::PhoneLinker, steamapi::PhoneClient,
token::Tokens, transport::WebApiTransport, AccountLinkError, AccountLinker, FinalizeLinkError,
token::Tokens, AccountLinkError, AccountLinker, FinalizeLinkError,
};

use crate::{tui, AccountManager};
Expand All @@ -14,8 +14,11 @@ use super::*;
#[clap(about = "Set up a new account with steamguard-cli")]
pub struct SetupCommand;

impl ManifestCommand for SetupCommand {
fn execute(&self, manager: &mut AccountManager) -> anyhow::Result<()> {
impl<T> ManifestCommand<T> for SetupCommand
where
T: Transport + Clone,
{
fn execute(&self, transport: T, manager: &mut AccountManager) -> anyhow::Result<()> {
eprintln!("Log in to the account that you want to link to steamguard-cli");
eprint!("Username: ");
let username = tui::prompt().to_lowercase();
Expand All @@ -27,11 +30,11 @@ impl ManifestCommand for SetupCommand {
);
}
info!("Logging in to {}", username);
let tokens =
crate::do_login_raw(username).expect("Failed to log in. Account has not been linked.");
let tokens = crate::do_login_raw(transport.clone(), username)
.expect("Failed to log in. Account has not been linked.");

info!("Adding authenticator...");
let mut linker = AccountLinker::new(WebApiTransport::default(), tokens);
let mut linker = AccountLinker::new(transport.clone(), tokens);
let link: AccountLinkSuccess;
loop {
match linker.link() {
Expand All @@ -41,7 +44,7 @@ impl ManifestCommand for SetupCommand {
}
Err(AccountLinkError::MustProvidePhoneNumber) => {
eprintln!("Looks like you don't have a phone number on this account.");
do_add_phone_number(linker.tokens())?;
do_add_phone_number(transport.clone(), linker.tokens())?;
}
Err(AccountLinkError::MustConfirmEmail) => {
println!("Check your email and click the link.");
Expand Down Expand Up @@ -129,8 +132,8 @@ impl ManifestCommand for SetupCommand {
}
}

pub fn do_add_phone_number(tokens: &Tokens) -> anyhow::Result<()> {
let client = PhoneClient::new(WebApiTransport::default());
pub fn do_add_phone_number<T: Transport>(transport: T, tokens: &Tokens) -> anyhow::Result<()> {
let client = PhoneClient::new(transport);

let linker = PhoneLinker::new(client, tokens.clone());

Expand Down
14 changes: 9 additions & 5 deletions src/commands/trade.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,13 @@ pub struct TradeCommand {
pub fail_fast: bool,
}

impl AccountCommand for TradeCommand {
impl<T> AccountCommand<T> for TradeCommand
where
T: Transport + Clone,
{
fn execute(
&self,
transport: T,
manager: &mut AccountManager,
accounts: Vec<Arc<Mutex<SteamGuardAccount>>>,
) -> anyhow::Result<()> {
Expand All @@ -36,13 +40,13 @@ impl AccountCommand for TradeCommand {

if !account.is_logged_in() {
info!("Account does not have tokens, logging in");
crate::do_login(&mut account)?;
crate::do_login(transport.clone(), &mut account)?;
}

info!("{}: Checking for trade confirmations", account.account_name);
let confirmations: Vec<Confirmation>;
loop {
let confirmer = Confirmer::new(&account);
let confirmer = Confirmer::new(transport.clone(), &account);

match confirmer.get_trade_confirmations() {
Ok(confs) => {
Expand All @@ -51,7 +55,7 @@ impl AccountCommand for TradeCommand {
}
Err(ConfirmerError::InvalidTokens) => {
info!("obtaining new tokens");
crate::do_login(&mut account)?;
crate::do_login(transport.clone(), &mut account)?;
}
Err(err) => {
error!("Failed to get trade confirmations: {}", err);
Expand All @@ -65,7 +69,7 @@ impl AccountCommand for TradeCommand {
continue;
}

let confirmer = Confirmer::new(&account);
let confirmer = Confirmer::new(transport.clone(), &account);
let mut any_failed = false;
if self.accept_all {
info!("accepting all confirmations");
Expand Down
Loading

0 comments on commit fe663cf

Please sign in to comment.