improve error handling in accountlinker

This commit is contained in:
Carson McManus 2021-08-09 18:44:42 -04:00
parent db2ec59c07
commit 298d29dc07
5 changed files with 57 additions and 43 deletions

21
Cargo.lock generated
View file

@ -1625,6 +1625,7 @@ dependencies = [
"serde", "serde",
"serde_json", "serde_json",
"standback", "standback",
"thiserror",
"uuid", "uuid",
] ]
@ -1781,6 +1782,26 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8eaa81235c7058867fa8c0e7314f33dcce9c215f535d1913822a2b3f5e289f3c" checksum = "8eaa81235c7058867fa8c0e7314f33dcce9c215f535d1913822a2b3f5e289f3c"
[[package]]
name = "thiserror"
version = "1.0.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93119e4feac1cbe6c798c34d3a53ea0026b0b1de6a120deef895137c0529bfe2"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "060d69a0afe7796bf42e9e2ff91f5ee691fb15c53d38b4b62a9a53eb23164745"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "thread_local" name = "thread_local"
version = "0.3.4" version = "0.3.4"

View file

@ -9,8 +9,8 @@ use std::{
sync::{Arc, Mutex}, sync::{Arc, Mutex},
}; };
use steamguard::{ use steamguard::{
steamapi, AccountLinker, Confirmation, ConfirmationType, LoginError, SteamGuardAccount, steamapi, AccountLinker, Confirmation, ConfirmationType, FinalizeLinkError, LoginError,
UserLogin, FinalizeLinkError SteamGuardAccount, UserLogin,
}; };
use termion::{ use termion::{
event::{Event, Key}, event::{Event, Key},
@ -172,7 +172,7 @@ fn main() {
let sms_code = prompt(); let sms_code = prompt();
let mut tries = 0; let mut tries = 0;
loop { loop {
match linker.finalize(&mut account, sms_code) { match linker.finalize(&mut account, sms_code.clone()) {
Ok(_) => break, Ok(_) => break,
Err(FinalizeLinkError::WantMore) => { Err(FinalizeLinkError::WantMore) => {
debug!("steam wants more 2fa codes (tries: {})", tries); debug!("steam wants more 2fa codes (tries: {})", tries);
@ -182,7 +182,7 @@ fn main() {
break; break;
} }
continue; continue;
}, }
Err(err) => { Err(err) => {
error!("Failed to finalize: {}", err); error!("Failed to finalize: {}", err);
break; break;

View file

@ -23,3 +23,4 @@ uuid = { version = "0.8", features = ["v4"] }
log = "0.4.14" log = "0.4.14"
scraper = "0.12.0" scraper = "0.12.0"
maplit = "1.0.2" maplit = "1.0.2"
thiserror = "1.0.26"

View file

@ -1,9 +1,9 @@
use crate::{ use crate::{
steamapi::{AddAuthenticatorResponse, Session, SteamApiClient}, steamapi::{AddAuthenticatorResponse, Session, SteamApiClient, FinalizeAddAuthenticatorResponse},
SteamGuardAccount, SteamGuardAccount,
}; };
use log::*; use log::*;
use std::error::Error; use thiserror::Error;
use std::fmt::Display; use std::fmt::Display;
#[derive(Debug)] #[derive(Debug)]
@ -30,28 +30,27 @@ impl AccountLinker {
}; };
} }
pub fn link(&mut self) -> anyhow::Result<SteamGuardAccount> { pub fn link(&mut self) -> anyhow::Result<SteamGuardAccount, AccountLinkError> {
ensure!(!self.finalized);
let has_phone = self.client.has_phone()?; let has_phone = self.client.has_phone()?;
if has_phone && !self.phone_number.is_empty() { if has_phone && !self.phone_number.is_empty() {
bail!(AccountLinkError::MustRemovePhoneNumber); return Err(AccountLinkError::MustRemovePhoneNumber);
} }
if !has_phone && self.phone_number.is_empty() { if !has_phone && self.phone_number.is_empty() {
bail!(AccountLinkError::MustProvidePhoneNumber); return Err(AccountLinkError::MustProvidePhoneNumber);
} }
if !has_phone { if !has_phone {
if self.sent_confirmation_email { if self.sent_confirmation_email {
if !self.client.check_email_confirmation()? { if !self.client.check_email_confirmation()? {
bail!("Failed email confirmation check"); return Err(anyhow!("Failed email confirmation check"))?;
} }
} else if !self.client.add_phone_number(self.phone_number.clone())? { } else if !self.client.add_phone_number(self.phone_number.clone())? {
bail!("Failed to add phone number"); return Err(anyhow!("Failed to add phone number"))?;
} else { } else {
self.sent_confirmation_email = true; self.sent_confirmation_email = true;
bail!(AccountLinkError::MustConfirmEmail); return Err(AccountLinkError::MustConfirmEmail);
} }
} }
@ -60,7 +59,7 @@ impl AccountLinker {
match resp.status { match resp.status {
29 => { 29 => {
bail!(AccountLinkError::AuthenticatorPresent); return Err(AccountLinkError::AuthenticatorPresent);
} }
1 => { 1 => {
let mut account = resp.to_steam_guard_account(); let mut account = resp.to_steam_guard_account();
@ -69,7 +68,7 @@ impl AccountLinker {
return Ok(account); return Ok(account);
} }
status => { status => {
bail!("Unknown add authenticator status code: {}", status); return Err(anyhow!("Unknown add authenticator status code: {}", status))?;
} }
} }
} }
@ -79,30 +78,27 @@ impl AccountLinker {
&mut self, &mut self,
account: &mut SteamGuardAccount, account: &mut SteamGuardAccount,
sms_code: String, sms_code: String,
) -> anyhow::Result<()> { ) -> anyhow::Result<(), FinalizeLinkError> {
ensure!(!account.fully_enrolled);
ensure!(!self.finalized);
let time = crate::steamapi::get_server_time(); let time = crate::steamapi::get_server_time();
let code = account.generate_code(time); let code = account.generate_code(time);
let resp = self let resp: FinalizeAddAuthenticatorResponse = self
.client .client
.finalize_authenticator(sms_code.clone(), code, time)?; .finalize_authenticator(sms_code.clone(), code, time)?;
info!("finalize response status: {}", resp.status); info!("finalize response status: {}", resp.status);
match resp.status { match resp.status {
89 => { 89 => {
bail!(FinalizeLinkError::BadSmsCode); return Err(FinalizeLinkError::BadSmsCode);
} }
_ => {} _ => {}
} }
if !resp.success { if !resp.success {
bail!("Failed to finalize authenticator. Status: {}", resp.status); return Err(FinalizeLinkError::Failure { status: resp.status })?;
} }
if resp.want_more { if resp.want_more {
bail!(FinalizeLinkError::WantMore); return Err(FinalizeLinkError::WantMore);
} }
self.finalized = true; self.finalized = true;
@ -115,38 +111,35 @@ fn generate_device_id() -> String {
return format!("android:{}", uuid::Uuid::new_v4().to_string()); return format!("android:{}", uuid::Uuid::new_v4().to_string());
} }
#[derive(Debug)] #[derive(Error, Debug)]
pub enum AccountLinkError { pub enum AccountLinkError {
/// No phone number on the account /// No phone number on the account
#[error("A phone number is needed, but not already present on the account.")]
MustProvidePhoneNumber, MustProvidePhoneNumber,
/// A phone number is already on the account /// A phone number is already on the account
#[error("A phone number was provided, but one is already present on the account.")]
MustRemovePhoneNumber, MustRemovePhoneNumber,
/// User need to click link from confirmation email /// User need to click link from confirmation email
#[error("An email has been sent to the user's email, click the link in that email.")]
MustConfirmEmail, MustConfirmEmail,
/// Must provide an SMS code /// Must provide an SMS code
#[error("Awaiting finalization")]
AwaitingFinalization, AwaitingFinalization,
#[error("Authenticator is already present.")]
AuthenticatorPresent, AuthenticatorPresent,
#[error(transparent)]
Unknown(#[from] anyhow::Error),
} }
impl Display for AccountLinkError { #[derive(Error, Debug)]
fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
write!(f, "{:?}", self)
}
}
impl Error for AccountLinkError {}
#[derive(Debug)]
pub enum FinalizeLinkError { pub enum FinalizeLinkError {
#[error("Provided SMS code was incorrect.")]
BadSmsCode, BadSmsCode,
/// Steam wants more 2fa codes to verify that we can generate valid codes. Call finalize again. /// Steam wants more 2fa codes to verify that we can generate valid codes. Call finalize again.
#[error("Steam wants more 2fa codes for verification.")]
WantMore, WantMore,
#[error("Finalization was not successful. Status code {status:?}")]
Failure{ status: i32 },
#[error(transparent)]
Unknown(#[from] anyhow::Error),
} }
impl Display for FinalizeLinkError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
write!(f, "{:?}", self)
}
}
impl Error for FinalizeLinkError {}

View file

@ -1,4 +1,4 @@
pub use accountlinker::{AccountLinkError, AccountLinker}; pub use accountlinker::{AccountLinkError, AccountLinker, FinalizeLinkError};
use anyhow::Result; use anyhow::Result;
pub use confirmation::{Confirmation, ConfirmationType}; pub use confirmation::{Confirmation, ConfirmationType};
use hmacsha1::hmac_sha1; use hmacsha1::hmac_sha1;
@ -11,7 +11,6 @@ use reqwest::{
use scraper::{Html, Selector}; use scraper::{Html, Selector};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::{collections::HashMap, convert::TryInto}; use std::{collections::HashMap, convert::TryInto};
pub use steamapi::AddAuthenticatorResponse;
pub use userlogin::{LoginError, UserLogin}; pub use userlogin::{LoginError, UserLogin};
#[macro_use] #[macro_use]
extern crate lazy_static; extern crate lazy_static;