From b55e05eeb4194c00a247331a53c5f6f4ccdf73ad Mon Sep 17 00:00:00 2001 From: Araozu Date: Sat, 23 Sep 2023 10:56:21 -0500 Subject: [PATCH] [BE][Class] Get session cookie and store it for ~20m --- backend/src/online_classroom/json_result.rs | 23 +++++++ backend/src/online_classroom/mod.rs | 68 +++---------------- backend/src/online_classroom/session.rs | 75 +++++++++++++++++++++ backend/src/online_classroom/users.rs | 15 +++++ 4 files changed, 123 insertions(+), 58 deletions(-) create mode 100644 backend/src/online_classroom/json_result.rs create mode 100644 backend/src/online_classroom/session.rs create mode 100644 backend/src/online_classroom/users.rs diff --git a/backend/src/online_classroom/json_result.rs b/backend/src/online_classroom/json_result.rs new file mode 100644 index 0000000..f19e7f5 --- /dev/null +++ b/backend/src/online_classroom/json_result.rs @@ -0,0 +1,23 @@ +use rocket::serde::json::Json; +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Serialize, Deserialize)] +pub enum JsonResult { + Ok(A), + Error(JsonError), +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct JsonError { + reason: String, +} + +impl JsonResult { + pub fn error(reason: String) -> Json> { + Json(JsonResult::Error(JsonError { reason })) + } + + pub fn ok(data: A) -> Json> { + Json(JsonResult::Ok(data)) + } +} diff --git a/backend/src/online_classroom/mod.rs b/backend/src/online_classroom/mod.rs index 0d84ef0..0ff190c 100644 --- a/backend/src/online_classroom/mod.rs +++ b/backend/src/online_classroom/mod.rs @@ -4,7 +4,13 @@ use reqwest::Client; use rocket::{http::Status, serde::json::Json}; use serde::{Deserialize, Serialize}; -static SESSION_COOKIE: OnceCell = OnceCell::new(); +use self::session::ensure_session; + +pub mod json_result; +mod session; +pub mod users; + + #[derive(Debug, Serialize, Deserialize)] pub enum ConnectionResult { @@ -22,64 +28,10 @@ fn new_error(reason: String) -> ConnectionResult { /// Tries to connect to the online classroom, and get a session cookie #[get("/classroom/connect")] pub async fn connection() -> (Status, Json) { - // Get session cookie - let _cookie = match get_cookie().await { - Ok(cookie) => cookie, - Err(reason) => return (Status::InternalServerError, Json(new_error(reason))), - }; - - (Status::Ok, Json(ConnectionResult::Ok())) -} - -async fn get_cookie<'a>() -> Result<&'a String, String> { - match SESSION_COOKIE.get() { - Some(cookie) => Ok(cookie), - None => { - // Try to create cookie - let _ = create_cookie().await?; - - // Try to get cookie again - match SESSION_COOKIE.get() { - Some(cookie) => Ok(cookie), - None => Err("Error getting session cookie".into()), - } - } + match ensure_session().await { + Ok(_) => (Status::Ok, Json(ConnectionResult::Ok())), + Err(err) => (Status::Ok, Json(new_error(err))), } } -/// Tries to connect to the online classroom, and get a session cookie. -/// Stores the session coookie in a global static variable. -pub async fn create_cookie() -> Result<(), String> { - let clasroom_url = std::env::var("CLASSROOM_URL").expect("CLASSROOM_URL env var is not set!"); - let clasroom_user = - std::env::var("CLASSROOM_USER").expect("CLASSROOM_USER env var is not set!"); - let clasroom_password = - std::env::var("CLASSROOM_PASSWORD").expect("CLASSROOM_PASSWORD env var is not set!"); - let params = [ - ("login", clasroom_user), - ("password", clasroom_password), - ("submitAuth", "".into()), - ("_qf__formLogin", "".into()), - ]; - let client = Client::new(); - let result = client - .post(format!("{}/index.php", clasroom_url)) - .form(¶ms) - .send() - .await; - - match result { - Ok(response) => { - let Some(session_cookie) = response.cookies().find(|c| c.name() == "ch_sid") else { - return Err("Response succeeded, but no session cookie was foun".into()); - }; - - match SESSION_COOKIE.set(session_cookie.value().into()) { - Ok(_) => Ok(()), - Err(error) => Err(format!("Error setting session cookie: {:?}", error)), - } - } - Err(error) => Err(format!("Error connecting to online classroom: {:?}", error)), - } -} diff --git a/backend/src/online_classroom/session.rs b/backend/src/online_classroom/session.rs new file mode 100644 index 0000000..d2af07f --- /dev/null +++ b/backend/src/online_classroom/session.rs @@ -0,0 +1,75 @@ +use std::time::{SystemTime, UNIX_EPOCH}; + +use once_cell::sync::OnceCell; +use reqwest::Client; + +static SESSION_COOKIE: OnceCell = OnceCell::new(); +static SESSION_TIME: OnceCell = OnceCell::new(); + +/// Makes a request to the online classroom, and returns the html string +pub async fn request(url: String) -> Result { + todo!() +} + + +/// Makes sure that the session cookie is set, and that it is valid +pub async fn ensure_session() -> Result<(), String> { + let last_usage_time = match SESSION_TIME.get() { + Some(time) => *time, + None => 0, + }; + + // Get current time in seconds + let current_time = SystemTime::now() + .duration_since(UNIX_EPOCH) + .expect("Time went backwards") + .as_secs(); + + let time_passed = current_time - last_usage_time; + + // Default PHP session timeout is 1440 seconds. Use a 1400 seconds timeout to be safe + if time_passed > 1400 { + login().await?; + if let Err(err) = SESSION_TIME.set(current_time) { + return Err(format!("Error setting session time: {:?}", err)); + } + } + + Ok(()) +} + +/// Logins to the online classroom, and sets the session cookie +async fn login() -> Result<(), String> { + let clasroom_url = std::env::var("CLASSROOM_URL").expect("CLASSROOM_URL env var is not set!"); + let clasroom_user = + std::env::var("CLASSROOM_USER").expect("CLASSROOM_USER env var is not set!"); + let clasroom_password = + std::env::var("CLASSROOM_PASSWORD").expect("CLASSROOM_PASSWORD env var is not set!"); + + let params = [ + ("login", clasroom_user), + ("password", clasroom_password), + ("submitAuth", "".into()), + ("_qf__formLogin", "".into()), + ]; + let client = Client::new(); + let result = client + .post(format!("{}/index.php", clasroom_url)) + .form(¶ms) + .send() + .await; + + match result { + Ok(response) => { + let Some(session_cookie) = response.cookies().find(|c| c.name() == "ch_sid") else { + return Err("Response succeeded, but no session cookie was foun".into()); + }; + + match SESSION_COOKIE.set(session_cookie.value().into()) { + Ok(_) => Ok(()), + Err(error) => Err(format!("Error setting session cookie: {:?}", error)), + } + } + Err(error) => Err(format!("Error connecting to online classroom: {:?}", error)), + } +} diff --git a/backend/src/online_classroom/users.rs b/backend/src/online_classroom/users.rs new file mode 100644 index 0000000..26167ff --- /dev/null +++ b/backend/src/online_classroom/users.rs @@ -0,0 +1,15 @@ +use rocket::{http::Status, serde::json::Json}; + +use super::{ json_result::JsonResult}; + +// Instead of requesting pages and managing session & cookies manually, +// create a wrapper that: +// - Checks if the session cookie is valid +// - If not, tries to login +// - Makes the request +// - Returns the html string, or an error + +#[get("/classroom/users/")] +pub async fn get_users(full_name: String) -> (Status, Json>) { + (Status::Ok, JsonResult::ok(())) +}