diff --git a/Cargo.lock b/Cargo.lock index e2cb601..d013e80 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -912,6 +912,31 @@ dependencies = [ "ahash", ] +[[package]] +name = "headers" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3e372db8e5c0d213e0cd0b9be18be2aca3d44cf2fe30a9d46a65581cd454584" +dependencies = [ + "base64 0.13.1", + "bitflags", + "bytes", + "headers-core", + "http", + "httpdate", + "mime", + "sha1", +] + +[[package]] +name = "headers-core" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7f66481bfee273957b1f20485a4ff3362987f85b2c236580d81b4eb7a326429" +dependencies = [ + "http", +] + [[package]] name = "hermit-abi" version = "0.2.6" @@ -1096,6 +1121,25 @@ version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "12b6ee2129af8d4fb011108c73d99a1b83a85977f23b82460c0ae2e25bb4b57f" +[[package]] +name = "is-docker" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "928bae27f42bc99b60d9ac7334e3a21d10ad8f1835a4e12ec3ec0464765ed1b3" +dependencies = [ + "once_cell", +] + +[[package]] +name = "is-wsl" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "173609498df190136aa7dea1a91db051746d339e18476eed5ca40521f02d7aa5" +dependencies = [ + "is-docker", + "once_cell", +] + [[package]] name = "itertools" version = "0.10.5" @@ -1214,14 +1258,17 @@ dependencies = [ "matrix-sdk-indexeddb", "matrix-sdk-sled", "mime", + "rand 0.8.5", "reqwest", "ruma", "serde", "serde_json", "thiserror", "tokio", + "tokio-stream", "tracing", "url", + "warp", "wasm-timer", "zeroize", ] @@ -1390,6 +1437,16 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" +[[package]] +name = "mime_guess" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef" +dependencies = [ + "mime", + "unicase", +] + [[package]] name = "mio" version = "0.8.6" @@ -1442,6 +1499,16 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" +[[package]] +name = "open" +version = "4.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "873240a4a404d44c8cd1bf394359245d466a5695771fea15a79cafbc5e5cf4d7" +dependencies = [ + "is-wsl", + "pathdiff", +] + [[package]] name = "openssl" version = "0.10.50" @@ -1545,6 +1612,12 @@ dependencies = [ "subtle", ] +[[package]] +name = "pathdiff" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" + [[package]] name = "pbkdf2" version = "0.11.0" @@ -2100,6 +2173,15 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "rustls-pemfile" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d194b56d58803a43635bdc398cd17e383d6f71f9182b9a192c127ca42494a59b" +dependencies = [ + "base64 0.21.0", +] + [[package]] name = "ryu" version = "1.0.13" @@ -2115,6 +2197,7 @@ dependencies = [ "dirs", "matrix-sdk", "once_cell", + "open", "rand 0.8.5", "reqwest", "rpassword", @@ -2135,6 +2218,12 @@ dependencies = [ "windows-sys 0.42.0", ] +[[package]] +name = "scoped-tls" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" + [[package]] name = "scopeguard" version = "1.1.0" @@ -2216,6 +2305,17 @@ dependencies = [ "serde", ] +[[package]] +name = "sha1" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.6", +] + [[package]] name = "sha2" version = "0.9.9" @@ -2473,6 +2573,17 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-stream" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fb52b74f05dbf495a8fba459fdc331812b96aa086d9eb78101fa0d4569c3313" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + [[package]] name = "tokio-util" version = "0.7.7" @@ -2526,6 +2637,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" dependencies = [ "cfg-if", + "log", "pin-project-lite", "tracing-attributes", "tracing-core", @@ -2563,6 +2675,15 @@ version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" +[[package]] +name = "unicase" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" +dependencies = [ + "version_check", +] + [[package]] name = "unicode-bidi" version = "0.3.13" @@ -2686,6 +2807,35 @@ dependencies = [ "try-lock", ] +[[package]] +name = "warp" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27e1a710288f0f91a98dd8a74f05b76a10768db245ce183edf64dc1afdc3016c" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "headers", + "http", + "hyper", + "log", + "mime", + "mime_guess", + "percent-encoding", + "pin-project", + "rustls-pemfile", + "scoped-tls", + "serde", + "serde_json", + "serde_urlencoded", + "tokio", + "tokio-stream", + "tokio-util", + "tower-service", + "tracing", +] + [[package]] name = "wasi" version = "0.9.0+wasi-snapshot-preview1" diff --git a/Cargo.toml b/Cargo.toml index 9c72c90..ac59194 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,8 +10,9 @@ authors = [ "@0xf8:projectsegfau.lt", "@jjj333:pain.agency" ] anyhow = "1.0.70" argparse = "0.2.2" dirs = "5.0.0" -matrix-sdk = "0.6.2" +matrix-sdk = { version = "0.6.2", features = [ "sso-login" ] } once_cell = "1.17.1" +open = "4.0.2" rand = "0.8.5" reqwest = "0.11.16" rpassword = "7.2.0" diff --git a/src/matrix.rs b/src/matrix.rs index c32269a..4763ec7 100644 --- a/src/matrix.rs +++ b/src/matrix.rs @@ -32,7 +32,7 @@ enum LoginChoice { } impl LoginChoice { - pub async fn login(&self, client: &Client, user: String, hs: String) -> anyhow::Result { + pub async fn login(&self, client: &Client, user: String, hs: String) -> anyhow::Result<()> { match self { LoginChoice::Password => Self::login_password(client, user, hs).await, LoginChoice::Sso => Self::login_sso(client, None).await, @@ -40,7 +40,7 @@ impl LoginChoice { } } - async fn login_password(client: &Client, user: String, _hs: String) -> anyhow::Result { + async fn login_password(client: &Client, user: String, _hs: String) -> anyhow::Result<()> { loop { let password = prompt_password("Password\n> ")?; @@ -52,7 +52,7 @@ impl LoginChoice { { Ok(_) => { println!("[*] Logged in as {user}"); - return Ok(client.to_owned()); + break; } Err(e) => { println!("[!] Error logging in: {e}"); @@ -60,40 +60,27 @@ impl LoginChoice { } } } + + Ok(()) } - async fn login_sso(client: &Client, idp: Option) -> anyhow::Result { - println!("Warning! SSO is a work in progress. Do not expect it to work."); - - let redirect_url = String::new(); + async fn login_sso(client: &Client, idp: Option) -> anyhow::Result<()> { + let mut login_builder = client.login_sso(|url| async move { + open::that_in_background(url); + println!("[*] Waiting for SSO token..."); + Ok(()) + }).initial_device_display_name("scam-police"); + if let Some(idp) = idp.to_owned() { + login_builder = login_builder.identity_provider_id(&idp.id); - let token = if let Some(idp) = idp { - client.get_sso_login_url(&redirect_url, Some(&idp.id)).await + login_builder.send().await?; } else { - client.get_sso_login_url(&redirect_url, None).await - }; - - println!("{redirect_url:?}, {token:?}"); - - match token { - Ok(t) => { - match client - .login_token(&t) - .initial_device_display_name("scam-police") - .send() - .await { - Ok(_) => { - Ok(client.to_owned()) - } - Err(e) => { - anyhow::bail!("{e}") - } - } - }, - Err(e) => { - anyhow::bail!("Failed to get SSO token: {e}") - } + login_builder.send().await?; } + + println!("[+] Got SSO token!"); + + Ok(()) } } @@ -102,7 +89,7 @@ impl LoginChoice { // pub async fn login(data_dir: &Path, session_file: &Path, mxid: String) -> anyhow::Result { - println!("[*] No previous session, logging in with mxid..."); + println!("[*] Logging in as {mxid}..."); let (user, hs) = resolve_mxid(mxid).await?; let (client, client_session) = build_client(data_dir, hs.to_owned()).await?; @@ -125,45 +112,48 @@ pub async fn login(data_dir: &Path, session_file: &Path, mxid: String) -> anyhow } } - let client = match login_choices.to_owned().len() { + match login_choices.to_owned().len() { 0 => anyhow::bail!("No supported login types"), 1 => login_choices.to_owned().get(0).unwrap().login(&client, user, hs.to_owned()).await, _ => { use terminal_menu::*; - let mut menu_items = vec![label("-- Scam Police Login --")]; + let mut menu_items = vec![label("----- Scam Police Login -----")]; let choices: Vec<(LoginChoice, String)> = login_choices.into_iter().map(|a| (a.to_owned(), match a { LoginChoice::Password => format!("Password"), LoginChoice::Sso => format!("SSO"), - LoginChoice::SsoIdp(idp) => format!("SSO ({})", idp.name), + LoginChoice::SsoIdp(idp) => format!("SSO via {}", idp.name), })).collect(); for choice in choices.to_owned() { menu_items.push(button(choice.1)); } + + menu_items.push(button("Abort login")); + menu_items.push(label("-----------------------------")); + let menu = menu(menu_items); run(&menu); let menu = mut_menu(&menu); let mut selected: Option = None; let name = menu.selected_item_name().to_string(); - for c in choices { - if c.1 == name { - selected = Some(c.0.to_owned()); + if name == "Abort login" { + selected = None; + } else { + for c in choices { + if c.1 == name { + selected = Some(c.0.to_owned()); + } } } match selected { Some(s) => s.login(&client, user, hs.to_owned()).await, - None => anyhow::bail!("Invalid selection, aborting login") + None => anyhow::bail!("Aborting login") } } - }; - - let client = match client { - Ok(client) => client, - Err(e) => anyhow::bail!("{e}") - }; + }?; let user_session = client .session() @@ -201,7 +191,7 @@ pub async fn build_client(data_dir: &Path, hs: String) -> anyhow::Result<(Client .await { Ok(client) => { - println!("[*] Homeserver OK"); + println!("[+] Homeserver OK"); return Ok(( client, ClientSession { @@ -277,7 +267,7 @@ pub async fn sync<'a>( client: Client, initial_sync_token: Option, ) -> anyhow::Result<(Client, SyncSettings<'a>)> { - println!("[*] Initial sync..."); + println!("[*] Running initial sync..."); let filter = FilterDefinition::empty(); @@ -304,7 +294,7 @@ pub async fn sync<'a>( } } - println!("[*] The bot is ready!"); + println!("[*] Scam police is now running!"); Ok((client, sync_settings)) }