Skip to content

Commit

Permalink
Merge #106
Browse files Browse the repository at this point in the history
106: fix: harden login routine even more r=crepererum a=crepererum



Co-authored-by: Marco Neumann <[email protected]>
  • Loading branch information
bors[bot] and crepererum authored Oct 28, 2023
2 parents 8271257 + 1fd6f48 commit f468389
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 35 deletions.
54 changes: 19 additions & 35 deletions src/login.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,18 @@ use clap::Parser;
use thirtyfour::{By, WebDriver};
use tracing::debug;

use crate::thirtyfour_util::FindExt;
use crate::{non_empty_string::NonEmptyString, thirtyfour_util::FindExt};

/// Login CLI config.
#[derive(Debug, Parser)]
pub struct LoginCLIConfig {
/// Username
#[clap(long, env = "TUTANOTA_CLI_USERNAME")]
username: String,
username: NonEmptyString,

/// Password
#[clap(long, env = "TUTANOTA_CLI_PASSWORD")]
password: String,
password: NonEmptyString,
}

/// Perform tutanota webinterface login.
Expand All @@ -37,38 +37,22 @@ pub async fn perform_login(config: LoginCLIConfig, webdriver: &WebDriver) -> Res
.context("find password input")?;
debug!("found username and password inputs");

tokio::time::timeout(Duration::from_secs(20), async {
loop {
input_username
.send_keys(&config.username)
.await
.context("enter username")?;
input_password
.send_keys(&config.password)
.await
.context("enter password")?;

// in some cases the UI seems to swallow the value (maybe because some JS wasn't loaded
// yet, who knows), so double-check the data and retry if the values are missing
let username = input_username
.prop("value")
.await
.context("check username value")?;
let password = input_password
.prop("value")
.await
.context("check password value")?;
if username.as_ref() == Some(&config.username)
&& password.as_ref() == Some(&config.password)
{
return Ok(()) as Result<()>;
}

tokio::time::sleep(Duration::from_secs(1)).await;
}
})
.await
.context("wait for login fields to converge")??;
input_username
.focus()
.await
.context("focus on username input")?;
input_username
.send_keys(config.username)
.await
.context("enter username")?;
input_password
.focus()
.await
.context("focus on password input")?;
input_password
.send_keys(config.password)
.await
.context("enter password")?;
debug!("entered username and password");

let login_button = webdriver
Expand Down
1 change: 1 addition & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ mod commands;
mod error;
mod logging;
mod login;
mod non_empty_string;
mod storage;
mod thirtyfour_util;
mod webdriver;
Expand Down
43 changes: 43 additions & 0 deletions src/non_empty_string.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
use std::{ops::Deref, str::FromStr};

/// Non-empty [`String`].
#[derive(Clone)]
pub struct NonEmptyString(String);

impl std::fmt::Debug for NonEmptyString {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
self.0.fmt(f)
}
}

impl std::fmt::Display for NonEmptyString {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
self.0.fmt(f)
}
}

impl Deref for NonEmptyString {
type Target = str;

fn deref(&self) -> &Self::Target {
self.0.deref()
}
}

impl AsRef<str> for NonEmptyString {
fn as_ref(&self) -> &str {
self.0.as_ref()
}
}

impl FromStr for NonEmptyString {
type Err = String;

fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
if s.is_empty() {
Err("cannot be empty".to_owned())
} else {
Ok(Self(s.to_owned()))
}
}
}

0 comments on commit f468389

Please sign in to comment.