use rocket::{ catch, catchers, fairing::AdHoc, form::Form, fs::FileServer, get, post, request::FlashMessage, response::{Flash, Redirect}, routes, Build, FromForm, Rocket, State, }; use rocket_dyn_templates::Template; use serde::Deserialize; use sqlx::SqlitePool; use tera::Context; use crate::model::user::{User, UserWithRoles}; pub(crate) mod admin; mod auth; mod boatdamage; mod cox; mod ergo; mod log; mod misc; mod planned; mod stat; #[derive(FromForm, Debug)] struct LoginForm<'r> { name: &'r str, password: &'r str, } #[get("/")] async fn index(db: &State, user: User, flash: Option>) -> Template { let mut context = Context::new(); if let Some(msg) = flash { context.insert("flash", &msg.into_inner()); } context.insert("loggedin_user", &UserWithRoles::from_user(user, db).await); Template::render("index", context.into_json()) } #[post("/", data = "")] async fn wikiauth(db: &State, login: Form>) -> String { match User::login(db, login.name, login.password).await { Ok(_) => "SUCC".into(), Err(_) => "FAIL".into(), } } #[catch(401)] //Unauthorized fn unauthorized_error() -> Redirect { Redirect::to("/auth") } #[catch(403)] //forbidden fn forbidden_error() -> Flash { Flash::error(Redirect::to("/"), "Keine Berechtigung für diese Aktion. Wenn du der Meinung bist, dass du das machen darfst, melde dich bitte bei it@rudernlinz.at.") } #[derive(Deserialize)] #[serde(crate = "rocket::serde")] pub struct Config { rss_key: String, smtp_pw: String, } pub fn config(rocket: Rocket) -> Rocket { rocket .mount("/", routes![index]) .mount("/auth", auth::routes()) .mount("/wikiauth", routes![wikiauth]) .mount("/log", log::routes()) .mount("/planned", planned::routes()) .mount("/ergo", ergo::routes()) .mount("/stat", stat::routes()) .mount("/boatdamage", boatdamage::routes()) .mount("/cox", cox::routes()) .mount("/admin", admin::routes()) .mount("/", misc::routes()) .mount("/public", FileServer::from("static/")) .register("/", catchers![unauthorized_error, forbidden_error]) .attach(Template::fairing()) .attach(AdHoc::config::()) } #[cfg(test)] mod test { use rocket::{ http::{ContentType, Status}, local::asynchronous::Client, }; use sqlx::SqlitePool; use crate::testdb; #[sqlx::test] fn test_index() { let db = testdb!(); let rocket = rocket::build().manage(db.clone()); let rocket = crate::tera::config(rocket); let client = Client::tracked(rocket).await.unwrap(); let login = client .post("/auth") .header(ContentType::Form) // Set the content type to form .body("name=cox&password=cox"); // Add the form data to the request body; login.dispatch().await; let req = client.get("/"); let response = req.dispatch().await; assert_eq!(response.status(), Status::Ok); assert!(response .into_string() .await .unwrap() .contains("Ruderassistent")); } #[sqlx::test] fn test_without_login() { let db = testdb!(); let rocket = rocket::build().manage(db.clone()); let rocket = crate::tera::config(rocket); let client = Client::tracked(rocket).await.unwrap(); let req = client.get("/"); let response = req.dispatch().await; assert_eq!(response.status(), Status::SeeOther); assert_eq!(response.headers().get("Location").next(), Some("/auth")); } #[sqlx::test] fn test_public() { let db = testdb!(); let rocket = rocket::build().manage(db.clone()); let rocket = crate::tera::config(rocket); let client = Client::tracked(rocket).await.unwrap(); let req = client.get("/public/main.css"); let response = req.dispatch().await; assert_eq!(response.status(), Status::Ok); let req = client.get("/public/main.js"); let response = req.dispatch().await; assert_eq!(response.status(), Status::Ok); } }