add HTTP proxy support (#264)
- 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
This commit is contained in:
parent
1d99bae469
commit
fe663cf43f
17 changed files with 164 additions and 105 deletions
|
@ -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;
|
||||
|
||||
|
@ -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)]
|
||||
|
@ -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)]
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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<()> {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use log::*;
|
||||
use steamguard::{transport::WebApiTransport, QrApprover, QrApproverError};
|
||||
use steamguard::{QrApprover, QrApproverError};
|
||||
|
||||
use crate::AccountManager;
|
||||
|
||||
|
@ -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<()> {
|
||||
|
@ -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 {
|
||||
|
@ -42,7 +46,7 @@ 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.");
|
||||
|
@ -50,7 +54,7 @@ impl AccountCommand for QrLoginCommand {
|
|||
}
|
||||
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);
|
||||
|
|
|
@ -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};
|
||||
|
||||
|
@ -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<()> {
|
||||
|
@ -38,10 +42,11 @@ 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());
|
||||
|
@ -49,7 +54,7 @@ impl AccountCommand for RemoveCommand {
|
|||
}
|
||||
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 {
|
||||
|
|
|
@ -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};
|
||||
|
@ -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();
|
||||
|
@ -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() {
|
||||
|
@ -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.");
|
||||
|
@ -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());
|
||||
|
||||
|
|
|
@ -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<()> {
|
||||
|
@ -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) => {
|
||||
|
@ -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);
|
||||
|
@ -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");
|
||||
|
|
41
src/main.rs
41
src/main.rs
|
@ -10,7 +10,7 @@ use std::{
|
|||
use steamguard::{
|
||||
protobufs::steammessages_auth_steamclient::{EAuthSessionGuardType, EAuthTokenPlatformType},
|
||||
refresher::TokenRefresher,
|
||||
transport::WebApiTransport,
|
||||
transport::{Transport, WebApiTransport},
|
||||
};
|
||||
use steamguard::{steamapi, DeviceDetails, LoginError, SteamGuardAccount, UserLogin};
|
||||
use steamguard::{steamapi::AuthenticationClient, token::Tokens};
|
||||
|
@ -83,7 +83,7 @@ fn main() {
|
|||
fn run(args: commands::Args) -> anyhow::Result<()> {
|
||||
let globalargs = args.global;
|
||||
|
||||
let cmd: CommandType = match args.sub.unwrap_or(Subcommands::Code(args.code)) {
|
||||
let cmd: CommandType<WebApiTransport> = match args.sub.unwrap_or(Subcommands::Code(args.code)) {
|
||||
Subcommands::Debug(args) => CommandType::Const(Box::new(args)),
|
||||
Subcommands::Completion(args) => CommandType::Const(Box::new(args)),
|
||||
Subcommands::Setup(args) => CommandType::Manifest(Box::new(args)),
|
||||
|
@ -198,8 +198,16 @@ fn run(args: commands::Args) -> anyhow::Result<()> {
|
|||
}
|
||||
}
|
||||
|
||||
let mut http_client = reqwest::blocking::Client::builder();
|
||||
if let Some(proxy) = &globalargs.http_proxy {
|
||||
let proxy = reqwest::Proxy::all(proxy)?;
|
||||
http_client = http_client.proxy(proxy);
|
||||
}
|
||||
let http_client = http_client.build()?;
|
||||
let transport = WebApiTransport::new(http_client);
|
||||
|
||||
if let CommandType::Manifest(cmd) = cmd {
|
||||
cmd.execute(&mut manager)?;
|
||||
cmd.execute(transport, &mut manager)?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
|
@ -237,7 +245,7 @@ fn run(args: commands::Args) -> anyhow::Result<()> {
|
|||
);
|
||||
|
||||
if let CommandType::Account(cmd) = cmd {
|
||||
return cmd.execute(&mut manager, selected_accounts);
|
||||
return cmd.execute(transport, &mut manager, selected_accounts);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
@ -271,10 +279,13 @@ fn get_selected_accounts(
|
|||
Ok(selected_accounts)
|
||||
}
|
||||
|
||||
fn do_login(account: &mut SteamGuardAccount) -> anyhow::Result<()> {
|
||||
fn do_login<T: Transport + Clone>(
|
||||
transport: T,
|
||||
account: &mut SteamGuardAccount,
|
||||
) -> anyhow::Result<()> {
|
||||
if let Some(tokens) = account.tokens.as_mut() {
|
||||
info!("Refreshing access token...");
|
||||
let client = AuthenticationClient::new(WebApiTransport::default());
|
||||
let client = AuthenticationClient::new(transport.clone());
|
||||
let mut refresher = TokenRefresher::new(client);
|
||||
match refresher.refresh(account.steam_id, tokens) {
|
||||
Ok(token) => {
|
||||
|
@ -304,14 +315,19 @@ fn do_login(account: &mut SteamGuardAccount) -> anyhow::Result<()> {
|
|||
} else {
|
||||
debug!("password is empty");
|
||||
}
|
||||
let tokens = do_login_impl(account.account_name.clone(), password, Some(account))?;
|
||||
let tokens = do_login_impl(
|
||||
transport,
|
||||
account.account_name.clone(),
|
||||
password,
|
||||
Some(account),
|
||||
)?;
|
||||
let steam_id = tokens.access_token().decode()?.steam_id();
|
||||
account.set_tokens(tokens);
|
||||
account.steam_id = steam_id;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn do_login_raw(username: String) -> anyhow::Result<Tokens> {
|
||||
fn do_login_raw<T: Transport + Clone>(transport: T, username: String) -> anyhow::Result<Tokens> {
|
||||
let _ = std::io::stdout().flush();
|
||||
let password = rpassword::prompt_password_stdout("Password: ").unwrap();
|
||||
if !password.is_empty() {
|
||||
|
@ -319,15 +335,16 @@ fn do_login_raw(username: String) -> anyhow::Result<Tokens> {
|
|||
} else {
|
||||
debug!("password is empty");
|
||||
}
|
||||
do_login_impl(username, password, None)
|
||||
do_login_impl(transport, username, password, None)
|
||||
}
|
||||
|
||||
fn do_login_impl(
|
||||
fn do_login_impl<T: Transport + Clone>(
|
||||
transport: T,
|
||||
username: String,
|
||||
password: String,
|
||||
account: Option<&SteamGuardAccount>,
|
||||
) -> anyhow::Result<Tokens> {
|
||||
let mut login = UserLogin::new(WebApiTransport::default(), build_device_details());
|
||||
let mut login = UserLogin::new(transport.clone(), build_device_details());
|
||||
|
||||
let mut password = password;
|
||||
let confirmation_methods;
|
||||
|
@ -375,7 +392,7 @@ fn do_login_impl(
|
|||
EAuthSessionGuardType::k_EAuthSessionGuardType_DeviceCode => {
|
||||
let code = if let Some(account) = account {
|
||||
debug!("Generating 2fa code...");
|
||||
let time = steamapi::get_server_time()?.server_time();
|
||||
let time = steamapi::get_server_time(transport)?.server_time();
|
||||
account.generate_code(time)
|
||||
} else {
|
||||
eprint!("Enter the 2fa code from your device: ");
|
||||
|
|
|
@ -10,20 +10,30 @@ use reqwest::{
|
|||
use secrecy::ExposeSecret;
|
||||
use serde::Deserialize;
|
||||
|
||||
use crate::{steamapi, SteamGuardAccount};
|
||||
use crate::{
|
||||
steamapi::{self},
|
||||
transport::Transport,
|
||||
SteamGuardAccount,
|
||||
};
|
||||
|
||||
lazy_static! {
|
||||
static ref STEAM_COOKIE_URL: Url = "https://steamcommunity.com".parse::<Url>().unwrap();
|
||||
}
|
||||
|
||||
/// Provides an interface that wraps the Steam mobile confirmation API.
|
||||
pub struct Confirmer<'a> {
|
||||
///
|
||||
/// Only compatible with WebApiTransport.
|
||||
pub struct Confirmer<'a, T> {
|
||||
account: &'a SteamGuardAccount,
|
||||
transport: T,
|
||||
}
|
||||
|
||||
impl<'a> Confirmer<'a> {
|
||||
pub fn new(account: &'a SteamGuardAccount) -> Self {
|
||||
Self { account }
|
||||
impl<'a, T> Confirmer<'a, T>
|
||||
where
|
||||
T: Transport + Clone,
|
||||
{
|
||||
pub fn new(transport: T, account: &'a SteamGuardAccount) -> Self {
|
||||
Self { account, transport }
|
||||
}
|
||||
|
||||
fn get_confirmation_query_params<'q>(
|
||||
|
@ -72,11 +82,9 @@ impl<'a> Confirmer<'a> {
|
|||
|
||||
pub fn get_trade_confirmations(&self) -> Result<Vec<Confirmation>, ConfirmerError> {
|
||||
let cookies = self.build_cookie_jar();
|
||||
let client = reqwest::blocking::ClientBuilder::new()
|
||||
.cookie_store(true)
|
||||
.build()?;
|
||||
let client = self.transport.innner_http_client()?;
|
||||
|
||||
let time = steamapi::get_server_time()?.server_time();
|
||||
let time = steamapi::get_server_time(self.transport.clone())?.server_time();
|
||||
let resp = client
|
||||
.get(
|
||||
"https://steamcommunity.com/mobileconf/getlist"
|
||||
|
@ -117,11 +125,9 @@ impl<'a> Confirmer<'a> {
|
|||
let operation = action.to_operation();
|
||||
|
||||
let cookies = self.build_cookie_jar();
|
||||
let client = reqwest::blocking::ClientBuilder::new()
|
||||
.cookie_store(true)
|
||||
.build()?;
|
||||
let client = self.transport.innner_http_client()?;
|
||||
|
||||
let time = steamapi::get_server_time()?.server_time();
|
||||
let time = steamapi::get_server_time(self.transport.clone())?.server_time();
|
||||
let mut query_params = self.get_confirmation_query_params("conf", time);
|
||||
query_params.push(("op", operation.into()));
|
||||
query_params.push(("cid", Cow::Borrowed(&conf.id)));
|
||||
|
@ -185,11 +191,9 @@ impl<'a> Confirmer<'a> {
|
|||
let operation = action.to_operation();
|
||||
|
||||
let cookies = self.build_cookie_jar();
|
||||
let client = reqwest::blocking::ClientBuilder::new()
|
||||
.cookie_store(true)
|
||||
.build()?;
|
||||
let client = self.transport.innner_http_client()?;
|
||||
|
||||
let time = steamapi::get_server_time()?.server_time();
|
||||
let time = steamapi::get_server_time(self.transport.clone())?.server_time();
|
||||
let mut query_params = self.get_confirmation_query_params("conf", time);
|
||||
query_params.push(("op", operation.into()));
|
||||
for conf in confs.iter() {
|
||||
|
@ -262,11 +266,9 @@ impl<'a> Confirmer<'a> {
|
|||
}
|
||||
|
||||
let cookies = self.build_cookie_jar();
|
||||
let client = reqwest::blocking::ClientBuilder::new()
|
||||
.cookie_store(true)
|
||||
.build()?;
|
||||
let client = self.transport.innner_http_client()?;
|
||||
|
||||
let time = steamapi::get_server_time()?.server_time();
|
||||
let time = steamapi::get_server_time(self.transport.clone())?.server_time();
|
||||
let query_params = self.get_confirmation_query_params("details", time);
|
||||
|
||||
let resp = client
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
use crate::protobufs::service_twofactor::CTwoFactor_RemoveAuthenticator_Request;
|
||||
use crate::steamapi::EResult;
|
||||
use crate::{
|
||||
steamapi::twofactor::TwoFactorClient, token::TwoFactorSecret, transport::WebApiTransport,
|
||||
};
|
||||
use crate::{steamapi::twofactor::TwoFactorClient, token::TwoFactorSecret};
|
||||
pub use accountlinker::{AccountLinkError, AccountLinker, FinalizeLinkError};
|
||||
pub use confirmation::*;
|
||||
pub use qrapprover::{QrApprover, QrApproverError};
|
||||
|
@ -10,7 +8,7 @@ pub use secrecy::{ExposeSecret, SecretString};
|
|||
use serde::{Deserialize, Serialize};
|
||||
use std::io::Read;
|
||||
use token::Tokens;
|
||||
use transport::TransportError;
|
||||
use transport::{Transport, TransportError};
|
||||
pub use userlogin::{DeviceDetails, LoginError, UserLogin};
|
||||
|
||||
#[macro_use]
|
||||
|
@ -99,8 +97,9 @@ impl SteamGuardAccount {
|
|||
|
||||
/// Removes the mobile authenticator from the steam account. If this operation succeeds, this object can no longer be considered valid.
|
||||
/// Returns whether or not the operation was successful.
|
||||
pub fn remove_authenticator(
|
||||
pub fn remove_authenticator<T: Transport>(
|
||||
&self,
|
||||
client: &TwoFactorClient<T>,
|
||||
revocation_code: Option<&String>,
|
||||
) -> Result<(), RemoveAuthenticatorError> {
|
||||
if !matches!(revocation_code, Some(_)) && self.revocation_code.expose_secret().is_empty() {
|
||||
|
@ -109,7 +108,6 @@ impl SteamGuardAccount {
|
|||
let Some(tokens) = &self.tokens else {
|
||||
return Err(RemoveAuthenticatorError::TransportError(TransportError::Unauthorized));
|
||||
};
|
||||
let mut client = TwoFactorClient::new(WebApiTransport::default());
|
||||
let mut req = CTwoFactor_RemoveAuthenticator_Request::new();
|
||||
req.set_revocation_code(
|
||||
revocation_code
|
||||
|
|
|
@ -2,9 +2,8 @@ pub mod authentication;
|
|||
pub mod phone;
|
||||
pub mod twofactor;
|
||||
|
||||
use crate::{
|
||||
protobufs::service_twofactor::CTwoFactor_Time_Response, token::Jwt, transport::WebApiTransport,
|
||||
};
|
||||
use crate::transport::Transport;
|
||||
use crate::{protobufs::service_twofactor::CTwoFactor_Time_Response, token::Jwt};
|
||||
use reqwest::Url;
|
||||
use serde::Deserialize;
|
||||
|
||||
|
@ -20,8 +19,8 @@ lazy_static! {
|
|||
/// Queries Steam for the current time. A convenience function around TwoFactorClient.
|
||||
///
|
||||
/// Endpoint: `/ITwoFactorService/QueryTime/v0001`
|
||||
pub fn get_server_time() -> anyhow::Result<CTwoFactor_Time_Response> {
|
||||
let mut client = TwoFactorClient::new(WebApiTransport::default());
|
||||
pub fn get_server_time<T: Transport>(client: T) -> anyhow::Result<CTwoFactor_Time_Response> {
|
||||
let client = TwoFactorClient::new(client);
|
||||
let resp = client.query_time()?;
|
||||
if resp.result != EResult::OK {
|
||||
return Err(anyhow::anyhow!("QueryTime failed: {:?}", resp));
|
||||
|
|
|
@ -26,7 +26,7 @@ where
|
|||
}
|
||||
|
||||
pub fn add_authenticator(
|
||||
&mut self,
|
||||
&self,
|
||||
req: CTwoFactor_AddAuthenticator_Request,
|
||||
access_token: &Jwt,
|
||||
) -> anyhow::Result<ApiResponse<CTwoFactor_AddAuthenticator_Response>> {
|
||||
|
@ -41,7 +41,7 @@ where
|
|||
}
|
||||
|
||||
pub fn finalize_authenticator(
|
||||
&mut self,
|
||||
&self,
|
||||
req: CTwoFactor_FinalizeAddAuthenticator_Request,
|
||||
access_token: &Jwt,
|
||||
) -> anyhow::Result<ApiResponse<CTwoFactor_FinalizeAddAuthenticator_Response>> {
|
||||
|
@ -56,7 +56,7 @@ where
|
|||
}
|
||||
|
||||
pub fn remove_authenticator(
|
||||
&mut self,
|
||||
&self,
|
||||
req: CTwoFactor_RemoveAuthenticator_Request,
|
||||
access_token: &Jwt,
|
||||
) -> Result<ApiResponse<CTwoFactor_RemoveAuthenticator_Response>, TransportError> {
|
||||
|
@ -71,7 +71,7 @@ where
|
|||
}
|
||||
|
||||
pub fn query_status(
|
||||
&mut self,
|
||||
&self,
|
||||
req: CTwoFactor_Status_Request,
|
||||
access_token: &Jwt,
|
||||
) -> anyhow::Result<ApiResponse<CTwoFactor_Status_Response>> {
|
||||
|
@ -83,7 +83,7 @@ where
|
|||
Ok(resp)
|
||||
}
|
||||
|
||||
pub fn query_time(&mut self) -> anyhow::Result<ApiResponse<CTwoFactor_Time_Response>> {
|
||||
pub fn query_time(&self) -> anyhow::Result<ApiResponse<CTwoFactor_Time_Response>> {
|
||||
let req = ApiRequest::new(SERVICE_NAME, "QueryTime", 1, CTwoFactor_Time_Request::new());
|
||||
let resp = self
|
||||
.transport
|
||||
|
|
|
@ -12,6 +12,10 @@ pub trait Transport {
|
|||
) -> Result<ApiResponse<Res>, TransportError>;
|
||||
|
||||
fn close(&mut self);
|
||||
|
||||
fn innner_http_client(&self) -> anyhow::Result<reqwest::blocking::Client> {
|
||||
bail!("Transport does not support extracting HTTP client")
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
|
|
|
@ -10,31 +10,15 @@ lazy_static! {
|
|||
static ref STEAM_API_BASE: String = "https://api.steampowered.com".into();
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct WebApiTransport {
|
||||
client: reqwest::blocking::Client,
|
||||
}
|
||||
|
||||
impl Default for WebApiTransport {
|
||||
fn default() -> Self {
|
||||
Self::new(reqwest::blocking::Client::new())
|
||||
}
|
||||
}
|
||||
|
||||
impl WebApiTransport {
|
||||
pub fn new(client: reqwest::blocking::Client) -> Self {
|
||||
Self { client }
|
||||
}
|
||||
|
||||
// pub fn new_with_proxy(proxy: &str) -> Self {
|
||||
// Self {
|
||||
// client: reqwest::blocking::Client::builder()
|
||||
// // .danger_accept_invalid_certs(true)
|
||||
// .proxy(reqwest::Proxy::all(proxy).unwrap())
|
||||
// .build()
|
||||
// .unwrap(),
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
impl Transport for WebApiTransport {
|
||||
|
@ -129,6 +113,10 @@ impl Transport for WebApiTransport {
|
|||
}
|
||||
|
||||
fn close(&mut self) {}
|
||||
|
||||
fn innner_http_client(&self) -> anyhow::Result<reqwest::blocking::Client> {
|
||||
Ok(self.client.clone())
|
||||
}
|
||||
}
|
||||
|
||||
fn encode_msg<T: MessageFull>(msg: &T, config: base64::Config) -> anyhow::Result<String> {
|
||||
|
|
Loading…
Reference in a new issue