From 813faf83aaee44b7bda5243b8efb505ed5ec0582 Mon Sep 17 00:00:00 2001 From: Patrick Cleavelin Date: Wed, 1 Mar 2023 01:29:23 -0600 Subject: [PATCH] DISCORD AUTH!, but broke some endpoints --- Cargo.lock | 248 +++++++++++++++++++++++++++++++++++++++++++++++- Cargo.toml | 3 + src/main.rs | 22 +++-- src/routes.rs | 113 ++++++++++++++++++++-- src/settings.rs | 18 ++++ 5 files changed, 389 insertions(+), 15 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index be47f5e..11c5885 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -251,6 +251,16 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "core-foundation" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "core-foundation-sys" version = "0.8.3" @@ -401,6 +411,36 @@ dependencies = [ "num-traits 0.1.43", ] +[[package]] +name = "errno" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" +dependencies = [ + "errno-dragonfly", + "libc", + "winapi", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "fastrand" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" +dependencies = [ + "instant", +] + [[package]] name = "flate2" version = "1.0.25" @@ -430,6 +470,21 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + [[package]] name = "form_urlencoded" version = "1.1.0" @@ -675,6 +730,19 @@ dependencies = [ "tokio-rustls", ] +[[package]] +name = "hyper-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +dependencies = [ + "bytes", + "hyper", + "native-tls", + "tokio", + "tokio-native-tls", +] + [[package]] name = "iana-time-zone" version = "0.1.53" @@ -719,6 +787,25 @@ dependencies = [ "hashbrown", ] +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "io-lifetimes" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1abeb7a0dd0f8181267ff8adc397075586500b81b28a73e8a0208b00fc170fb3" +dependencies = [ + "libc", + "windows-sys 0.45.0", +] + [[package]] name = "ipnet" version = "2.7.1" @@ -761,6 +848,12 @@ dependencies = [ "cc", ] +[[package]] +name = "linux-raw-sys" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" + [[package]] name = "lock_api" version = "0.4.9" @@ -823,14 +916,17 @@ dependencies = [ "axum", "chrono", "futures", + "reqwest", "serde", "serde_json", "serenity", "songbird", + "thiserror", "tokio", "tower-http", "tracing", "tracing-subscriber", + "uuid 1.3.0", ] [[package]] @@ -879,6 +975,24 @@ dependencies = [ "getrandom", ] +[[package]] +name = "native-tls" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" +dependencies = [ + "lazy_static", + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + [[package]] name = "nu-ansi-term" version = "0.46.0" @@ -939,6 +1053,51 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" +[[package]] +name = "openssl" +version = "0.10.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b102428fd03bc5edf97f62620f7298614c45cedf287c271e7ed450bbaf83f2e1" +dependencies = [ + "bitflags", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-sys" +version = "0.9.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23bbbf7854cd45b83958ebe919f0e8e516793727652e27fda10a8384cfc790b7" +dependencies = [ + "autocfg", + "cc", + "libc", + "pkg-config", + "vcpkg", +] + [[package]] name = "ordered-float" version = "2.10.0" @@ -1164,11 +1323,13 @@ dependencies = [ "http-body", "hyper", "hyper-rustls", + "hyper-tls", "ipnet", "js-sys", "log", "mime", "mime_guess", + "native-tls", "once_cell", "percent-encoding", "pin-project-lite", @@ -1178,6 +1339,7 @@ dependencies = [ "serde_json", "serde_urlencoded", "tokio", + "tokio-native-tls", "tokio-rustls", "tokio-util", "tower-service", @@ -1205,6 +1367,20 @@ dependencies = [ "winapi", ] +[[package]] +name = "rustix" +version = "0.36.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f43abb88211988493c1abb44a70efa56ff0ce98f233b7b276146f1f3f7ba9644" +dependencies = [ + "bitflags", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys", + "windows-sys 0.45.0", +] + [[package]] name = "rustls" version = "0.20.8" @@ -1248,6 +1424,15 @@ dependencies = [ "zeroize", ] +[[package]] +name = "schannel" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "713cfb06c7059f3588fb8044c0fad1d09e3c01d225e25b9220dbfdcf16dbb1b3" +dependencies = [ + "windows-sys 0.42.0", +] + [[package]] name = "scoped-tls" version = "1.0.1" @@ -1276,6 +1461,29 @@ dependencies = [ "untrusted", ] +[[package]] +name = "security-framework" +version = "2.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a332be01508d814fed64bf28f798a146d73792121129962fdf335bb3c49a4254" +dependencies = [ + "bitflags", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31c9bb296072e961fcbd8853511dd39c2d8be2deb1e17c6860b1d30732b323b4" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "serde" version = "1.0.152" @@ -1475,7 +1683,7 @@ dependencies = [ "tracing-futures", "typemap_rev", "url", - "uuid", + "uuid 0.8.2", "xsalsa20poly1305", ] @@ -1541,6 +1749,19 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" +[[package]] +name = "tempfile" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af18f7ae1acd354b992402e9ec5864359d693cd8a79dcbef59f76891701c1e95" +dependencies = [ + "cfg-if", + "fastrand", + "redox_syscall", + "rustix", + "windows-sys 0.42.0", +] + [[package]] name = "termcolor" version = "1.2.0" @@ -1662,6 +1883,16 @@ dependencies = [ "syn", ] +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + [[package]] name = "tokio-rustls" version = "0.23.4" @@ -1925,12 +2156,27 @@ dependencies = [ "getrandom", ] +[[package]] +name = "uuid" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1674845326ee10d37ca60470760d4288a6f80f304007d92e5c53bab78c9cfd79" +dependencies = [ + "getrandom", +] + [[package]] name = "valuable" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + [[package]] name = "version_check" version = "0.9.4" diff --git a/Cargo.toml b/Cargo.toml index bdb37ef..112f26b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,11 +9,14 @@ edition = "2021" axum = "0.6.9" chrono = "0.4.23" futures = "0.3.26" +reqwest = "0.11.14" serde = "1.0.152" serde_json = "1.0.93" serenity = { version = "0.11.5", default-features = false, features = ["client", "gateway", "rustls_backend", "model", "cache", "voice"] } songbird = { version = "0.3.0", features = [ "builtin-queue" ] } +thiserror = "1.0.38" tokio = { version = "1.25.0", features = ["rt-multi-thread", "macros", "signal"] } tower-http = { version = "0.4.0", features = ["cors"] } tracing = "0.1.37" tracing-subscriber = "0.3.16" +uuid = { version = "1.3.0", features = ["v4"] } diff --git a/src/main.rs b/src/main.rs index c7c3646..21d2ecc 100644 --- a/src/main.rs +++ b/src/main.rs @@ -15,7 +15,7 @@ use std::env; use std::net::SocketAddr; use std::sync::Arc; use tokio::sync::mpsc; -use tower_http::cors::CorsLayer; +use tower_http::cors::{Any, CorsLayer}; use serde::Deserialize; use serenity::async_trait; @@ -118,14 +118,22 @@ fn spawn_api(settings: Arc>) -> tokio::task::JoinHandle<()> { tokio::spawn(async move { let api = Router::new() .route("/health", get(routes::health)) - .route("/me/:user", get(routes::me)) + .route("/me", get(routes::me)) .route("/intros/:guild", get(routes::intros)) - .route("/intros/:guild/:channel/:user/:intro", post(routes::add_intro_to_user)) - .route("/intros/:guild/:channel/:user/:intro/remove", post(routes::remove_intro_to_user)) + .route( + "/intros/:guild/:channel/:user/:intro", + post(routes::add_intro_to_user), + ) + .route( + "/intros/:guild/:channel/:user/:intro/remove", + post(routes::remove_intro_to_user), + ) + .route("/auth", get(routes::auth)) .layer( CorsLayer::new() - .allow_origin("*".parse::().unwrap()) - .allow_methods([Method::GET]), + .allow_origin(Any) + .allow_headers(Any) + .allow_methods([Method::GET, Method::POST]), ) .with_state(settings); let addr = SocketAddr::from(([0, 0, 0, 0], 7756)); @@ -257,7 +265,7 @@ async fn spawn_bot(settings: Arc>) -> Vec { error!( "Failed to find intro for user {} on guild {} in channel {}, IntroIndex: {}", - member.user.name, + member.user.name, channel.guild_id.as_u64(), channel.name(), intro.index diff --git a/src/routes.rs b/src/routes.rs index 4618791..e0e6e71 100644 --- a/src/routes.rs +++ b/src/routes.rs @@ -1,15 +1,21 @@ use std::{collections::HashMap, sync::Arc}; use axum::{ - extract::{Path, State}, + body::StreamBody, + extract::{Path, Query, State}, + http::HeaderMap, + response::IntoResponse, Json, }; -use serde::Serialize; +use futures::Stream; +use reqwest::StatusCode; +use serde::{Deserialize, Serialize}; use serde_json::{json, Value}; use tokio::sync::Mutex; -use tracing::error; +use tracing::{error, info}; +use uuid::Uuid; -use crate::settings::{GuildSettings, Intro, IntroIndex, Settings, UserSettings}; +use crate::settings::{Auth, AuthUser, GuildSettings, Intro, IntroIndex, Settings, UserSettings}; #[derive(Serialize)] pub(crate) enum IntroResponse<'a> { @@ -47,11 +53,92 @@ pub(crate) async fn health(State(state): State>>) -> Json axum::response::Response { + let body = match self { + Self::AuthError(msg) => msg, + Self::GetUser(error) => error.to_string(), + }; + + (StatusCode::INTERNAL_SERVER_ERROR, body).into_response() + } +} + +#[derive(Deserialize)] +struct DiscordUser { + pub username: String, +} + +pub(crate) async fn auth( + State(settings): State>>, + Query(params): Query>, +) -> Result, Error> { + let Some(code) = params.get("code") else { + return Err(Error::AuthError("no code".to_string())); + }; + + info!("attempting to get access token with code {}", code); + + let mut data = HashMap::new(); + data.insert("client_id", "577634620728934400"); + data.insert("client_secret", "zQ_OWUbn4NsSb1S50NBHTt05xadqqHro"); + data.insert("grant_type", "authorization_code"); + data.insert("code", code); + data.insert("redirect_uri", "http://localhost:5173/auth"); + + let client = reqwest::Client::new(); + + let auth: Auth = client + .post("https://discord.com/api/oauth2/token") + .form(&data) + .send() + .await + .map_err(|err| Error::AuthError(err.to_string()))? + .json() + .await + .map_err(|err| Error::AuthError(err.to_string()))?; + let token = Uuid::new_v4().to_string(); + + // Get authorized username + let user: DiscordUser = client + .get("https://discord.com/api/v10/users/@me") + .bearer_auth(&auth.access_token) + .send() + .await? + .json() + .await?; + + let mut settings = settings.lock().await; + settings.auth_users.insert( + token.clone(), + AuthUser { + auth, + name: user.username.clone(), + }, + ); + + Ok(Json(json!({"token": token, "username": user.username}))) +} + pub(crate) async fn add_intro_to_user( State(state): State>>, - Path((guild, channel, user, intro_index)): Path<(u64, String, String, usize)>, + headers: HeaderMap, + Path((guild, channel, intro_index)): Path<(u64, String, usize)>, ) { let mut settings = state.lock().await; + let Some(token) = headers.get("token").and_then(|v| v.to_str().ok()) else { return; }; + let user = match settings.auth_users.get(token) { + Some(user) => user.name.clone(), + None => return, + }; let Some(guild) = settings.guilds.get_mut(&guild) else { return; }; let Some(channel) = guild.channels.get_mut(&channel) else { return; }; @@ -69,9 +156,15 @@ pub(crate) async fn add_intro_to_user( pub(crate) async fn remove_intro_to_user( State(state): State>>, - Path((guild, channel, user, intro_index)): Path<(u64, String, String, usize)>, + headers: HeaderMap, + Path((guild, channel, intro_index)): Path<(u64, String, usize)>, ) { let mut settings = state.lock().await; + let Some(token) = headers.get("token").and_then(|v| v.to_str().ok()) else { return; }; + let user = match settings.auth_users.get(token) { + Some(user) => user.name.clone(), + None => return, + }; let Some(guild) = settings.guilds.get_mut(&guild) else { return; }; let Some(channel) = guild.channels.get_mut(&channel) else { return; }; @@ -102,9 +195,15 @@ pub(crate) async fn intros( pub(crate) async fn me( State(state): State>>, - Path(user): Path, + headers: HeaderMap, ) -> Json { let settings = state.lock().await; + let Some(token) = headers.get("token").and_then(|v| v.to_str().ok()) else { return Json(json!(MeResponse::NoUserFound)); }; + + let user = match settings.auth_users.get(token) { + Some(user) => user.name.clone(), + None => return Json(json!(MeResponse::NoUserFound)), + }; let mut me = Me { username: user.clone(), diff --git a/src/settings.rs b/src/settings.rs index e7cf9fc..98c46a9 100644 --- a/src/settings.rs +++ b/src/settings.rs @@ -3,6 +3,21 @@ use std::{collections::HashMap, sync::Arc}; use serde::{Deserialize, Serialize}; use serenity::prelude::TypeMapKey; +#[derive(Debug, Clone, Serialize, Deserialize)] +pub(crate) struct Auth { + pub(crate) access_token: String, + pub(crate) token_type: String, + pub(crate) expires_in: usize, + pub(crate) refresh_token: String, + pub(crate) scope: String, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub(crate) struct AuthUser { + pub auth: Auth, + pub name: String, +} + #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub(crate) struct Settings { @@ -11,6 +26,9 @@ pub(crate) struct Settings { #[serde(default)] pub(crate) run_bot: bool, pub(crate) guilds: HashMap, + + #[serde(skip)] + pub(crate) auth_users: HashMap, } impl TypeMapKey for Settings { type Value = Arc;