use rocket::{ http::{Cookie, Cookies, SameSite, uri::Uri}, response::Redirect, request::{LenientForm,FlashMessage} }; use rocket_contrib::Template; use rocket::http::ext::IntoOwned; use std::borrow::Cow; use validator::{Validate, ValidationError, ValidationErrors}; use plume_models::{ db_conn::DbConn, users::{User, AUTH_COOKIE} }; #[get("/login")] fn new(user: Option, conn: DbConn) -> Template { Template::render("session/login", json!({ "account": user.map(|u| u.to_json(&*conn)), "errors": null, "form": null })) } #[derive(FromForm)] struct Message { m: String } #[get("/login?")] fn new_message(user: Option, message: Message, conn: DbConn) -> Template { Template::render("session/login", json!({ "account": user.map(|u| u.to_json(&*conn)), "message": message.m, "errors": null, "form": null })) } #[derive(FromForm, Validate, Serialize)] struct LoginForm { #[validate(length(min = "1", message = "We need an email or a username to identify you"))] email_or_name: String, #[validate(length(min = "1", message = "Your password can't be empty"))] password: String } #[post("/login", data = "")] fn create(conn: DbConn, data: LenientForm, flash: Option, mut cookies: Cookies) -> Result { let form = data.get(); let user = User::find_by_email(&*conn, form.email_or_name.to_string()) .map(|u| Ok(u)) .unwrap_or_else(|| User::find_local(&*conn, form.email_or_name.to_string()).map(|u| Ok(u)).unwrap_or(Err(()))); let mut errors = match form.validate() { Ok(_) => ValidationErrors::new(), Err(e) => e }; if let Err(_) = user.clone() { // Fake password verification, only to avoid different login times // that could be used to see if an email adress is registered or not User::get(&*conn, 1).map(|u| u.auth(form.password.clone())); let mut err = ValidationError::new("invalid_login"); err.message = Some(Cow::from("Invalid username or password")); errors.add("email_or_name", err) } else if !user.clone().expect("User not found").auth(form.password.clone()) { let mut err = ValidationError::new("invalid_login"); err.message = Some(Cow::from("Invalid username or password")); errors.add("email_or_name", err) } if errors.is_empty() { cookies.add_private(Cookie::build(AUTH_COOKIE, user.unwrap().id.to_string()) .same_site(SameSite::Lax) .finish()); let destination = flash .and_then(|f| if f.name() == "callback" { Some(f.msg().to_owned()) } else { None }) .unwrap_or("/".to_owned()); let uri = Uri::parse(&destination) .map(|x| x.into_owned()) .map_err(|_| { Template::render("session/login", json!({ "account": null, "errors": errors.inner(), "form": form })) })?; Ok(Redirect::to(uri)) } else { println!("{:?}", errors); Err(Template::render("session/login", json!({ "account": null, "errors": errors.inner(), "form": form }))) } } #[get("/logout")] fn delete(mut cookies: Cookies) -> Redirect { let cookie = cookies.get_private(AUTH_COOKIE).unwrap(); cookies.remove_private(cookie); Redirect::to("/") }