add --password argument and STEAMGUARD_CLI_STEAM_PASSWORD environment variable (#326)

closes #324
This commit is contained in:
Carson McManus 2023-09-28 17:52:42 -04:00 committed by GitHub
parent 04295dc742
commit 0c256a0e05
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 63 additions and 15 deletions

View file

@ -44,7 +44,12 @@ pub(crate) trait ManifestCommand<T>
where where
T: Transport, 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. /// A command that operates on individual accounts.
@ -57,6 +62,7 @@ where
transport: T, transport: T,
manager: &mut AccountManager, manager: &mut AccountManager,
accounts: Vec<Arc<Mutex<SteamGuardAccount>>>, accounts: Vec<Arc<Mutex<SteamGuardAccount>>>,
args: &GlobalArgs,
) -> anyhow::Result<()>; ) -> 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." 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<String>, pub username: Option<String>,
#[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<SecretString>,
#[clap( #[clap(
short, short,
long, long,

View file

@ -29,6 +29,7 @@ where
transport: T, transport: T,
_manager: &mut AccountManager, _manager: &mut AccountManager,
accounts: Vec<Arc<Mutex<SteamGuardAccount>>>, accounts: Vec<Arc<Mutex<SteamGuardAccount>>>,
_args: &GlobalArgs,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
let server_time = if self.offline { let server_time = if self.offline {
SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs() SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs()

View file

@ -12,7 +12,12 @@ impl<T> ManifestCommand<T> for DecryptCommand
where where
T: Transport, 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)?; load_accounts_with_prompts(manager)?;
#[cfg(feature = "keyring")] #[cfg(feature = "keyring")]

View file

@ -16,7 +16,12 @@ impl<T> ManifestCommand<T> for EncryptCommand
where where
T: Transport, 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() { if !manager.has_passkey() {
let passkey: Option<SecretString>; let passkey: Option<SecretString>;
loop { loop {

View file

@ -19,7 +19,12 @@ impl<T> ManifestCommand<T> for ImportCommand
where where
T: Transport, 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() { for file_path in self.files.iter() {
debug!("loading entry: {:?}", file_path); debug!("loading entry: {:?}", file_path);
match manager.import_account(file_path) { match manager.import_account(file_path) {

View file

@ -27,6 +27,7 @@ where
_transport: T, _transport: T,
_manager: &mut AccountManager, _manager: &mut AccountManager,
accounts: Vec<Arc<Mutex<SteamGuardAccount>>>, accounts: Vec<Arc<Mutex<SteamGuardAccount>>>,
_args: &GlobalArgs,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
use anyhow::Context; use anyhow::Context;

View file

@ -26,6 +26,7 @@ where
transport: T, transport: T,
_manager: &mut AccountManager, _manager: &mut AccountManager,
accounts: Vec<Arc<Mutex<SteamGuardAccount>>>, accounts: Vec<Arc<Mutex<SteamGuardAccount>>>,
args: &GlobalArgs,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
ensure!( ensure!(
accounts.len() == 1, accounts.len() == 1,
@ -37,7 +38,7 @@ where
info!("Approving login to {}", account.account_name); info!("Approving login to {}", account.account_name);
if account.tokens.is_none() { if account.tokens.is_none() {
crate::do_login(transport.clone(), &mut account)?; crate::do_login(transport.clone(), &mut account, args.password.clone())?;
} }
loop { loop {
@ -57,7 +58,7 @@ where
} }
Err(QrApproverError::Unauthorized) => { Err(QrApproverError::Unauthorized) => {
warn!("tokens are invalid. Attempting to log in again."); 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) => { Err(e) => {
error!("Failed to approve login: {}", e); error!("Failed to approve login: {}", e);

View file

@ -20,6 +20,7 @@ where
transport: T, transport: T,
manager: &mut AccountManager, manager: &mut AccountManager,
accounts: Vec<Arc<Mutex<SteamGuardAccount>>>, accounts: Vec<Arc<Mutex<SteamGuardAccount>>>,
args: &GlobalArgs,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
eprintln!( eprintln!(
"This will remove the mobile authenticator from {} accounts: {}", "This will remove the mobile authenticator from {} accounts: {}",
@ -54,7 +55,7 @@ where
} }
Err(RemoveAuthenticatorError::TransportError(TransportError::Unauthorized)) => { Err(RemoveAuthenticatorError::TransportError(TransportError::Unauthorized)) => {
error!("Account {} is not logged in", account.account_name); 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; continue;
} }
Err(RemoveAuthenticatorError::IncorrectRevocationCode { Err(RemoveAuthenticatorError::IncorrectRevocationCode {

View file

@ -18,7 +18,12 @@ impl<T> ManifestCommand<T> for SetupCommand
where where
T: Transport + Clone, 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"); eprintln!("Log in to the account that you want to link to steamguard-cli");
eprint!("Username: "); eprint!("Username: ");
let username = tui::prompt().to_lowercase(); let username = tui::prompt().to_lowercase();
@ -30,7 +35,7 @@ where
); );
} }
info!("Logging in to {}", username); 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."); .expect("Failed to log in. Account has not been linked.");
info!("Adding authenticator..."); info!("Adding authenticator...");

View file

@ -34,13 +34,14 @@ where
transport: T, transport: T,
manager: &mut AccountManager, manager: &mut AccountManager,
accounts: Vec<Arc<Mutex<SteamGuardAccount>>>, accounts: Vec<Arc<Mutex<SteamGuardAccount>>>,
args: &GlobalArgs,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
for a in accounts { for a in accounts {
let mut account = a.lock().unwrap(); let mut account = a.lock().unwrap();
if !account.is_logged_in() { if !account.is_logged_in() {
info!("Account does not have tokens, logging 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); info!("{}: Checking for trade confirmations", account.account_name);
@ -55,7 +56,7 @@ where
} }
Err(ConfirmerError::InvalidTokens) => { Err(ConfirmerError::InvalidTokens) => {
info!("obtaining new tokens"); info!("obtaining new tokens");
crate::do_login(transport.clone(), &mut account)?; crate::do_login(transport.clone(), &mut account, args.password.clone())?;
} }
Err(err) => { Err(err) => {
error!("Failed to get trade confirmations: {}", err); error!("Failed to get trade confirmations: {}", err);

View file

@ -17,6 +17,7 @@ use crate::tui;
pub fn do_login<T: Transport + Clone>( pub fn do_login<T: Transport + Clone>(
transport: T, transport: T,
account: &mut SteamGuardAccount, account: &mut SteamGuardAccount,
password: Option<SecretString>,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
if let Some(tokens) = account.tokens.as_mut() { if let Some(tokens) = account.tokens.as_mut() {
info!("Refreshing access token..."); info!("Refreshing access token...");
@ -44,7 +45,11 @@ pub fn do_login<T: Transport + Clone>(
account.account_name = tui::prompt(); account.account_name = tui::prompt();
} }
let _ = std::io::stdout().flush(); 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() { if !password.expose_secret().is_empty() {
debug!("password is present"); debug!("password is present");
} else { } else {
@ -65,9 +70,14 @@ pub fn do_login<T: Transport + Clone>(
pub fn do_login_raw<T: Transport + Clone>( pub fn do_login_raw<T: Transport + Clone>(
transport: T, transport: T,
username: String, username: String,
password: Option<SecretString>,
) -> anyhow::Result<Tokens> { ) -> anyhow::Result<Tokens> {
let _ = std::io::stdout().flush(); 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() { if !password.expose_secret().is_empty() {
debug!("password is present"); debug!("password is present");
} else { } else {

View file

@ -232,7 +232,7 @@ fn run(args: commands::Args) -> anyhow::Result<()> {
let transport = WebApiTransport::new(http_client); let transport = WebApiTransport::new(http_client);
if let CommandType::Manifest(cmd) = cmd { if let CommandType::Manifest(cmd) = cmd {
cmd.execute(transport, &mut manager)?; cmd.execute(transport, &mut manager, &globalargs)?;
return Ok(()); return Ok(());
} }
@ -269,7 +269,7 @@ fn run(args: commands::Args) -> anyhow::Result<()> {
); );
if let CommandType::Account(cmd) = cmd { if let CommandType::Account(cmd) = cmd {
return cmd.execute(transport, &mut manager, selected_accounts); return cmd.execute(transport, &mut manager, selected_accounts, &globalargs);
} }
Ok(()) Ok(())