tenebrous-sheets/src/routes/auth.rs

142 lines
3.9 KiB
Rust

use crate::db::{Dao, TenebrousDbConn};
use crate::models::users::{self, NewUser, User};
use log::error;
use rocket::http::{Cookie, CookieJar};
use rocket::request::{FlashMessage, Form};
use rocket::response::{Flash, Redirect};
use rocket_contrib::templates::Template;
use std::collections::HashMap;
pub(crate) fn routes() -> Vec<rocket::Route> {
routes![
login,
logout,
logged_in_user,
login_page,
register_page,
register
]
}
#[derive(FromForm)]
struct Login {
username: String,
password: String,
}
#[derive(FromForm)]
struct Registration {
username: String,
password: String,
}
fn add_login_cookie(cookies: &CookieJar<'_>, user: &User) {
cookies.add_private(Cookie::new("user_id", user.id.to_string()));
}
fn remove_login_cookie(cookies: &CookieJar<'_>) {
cookies.remove_private(Cookie::named("user_id"));
}
fn login_error_redirect<S: AsRef<str>>(message: S) -> Flash<Redirect> {
Flash::error(Redirect::to(uri!(login_page)), message.as_ref())
}
fn registration_error_redirect<S: AsRef<str>>(message: S) -> Flash<Redirect> {
Flash::error(Redirect::to(uri!(register_page)), message.as_ref())
}
#[post("/login", data = "<login>")]
async fn login(
cookies: &CookieJar<'_>,
login: Form<Login>,
conn: TenebrousDbConn,
) -> Result<Redirect, Flash<Redirect>> {
let user = conn
.load_user(login.username.clone())
.await
.map_err(|e| {
error!("login - error loading user user: {}", e);
login_error_redirect("Internal error.")
})?
.ok_or_else(|| login_error_redirect("Invalid username or password."))?;
if user.verify_password(&login.password) {
add_login_cookie(cookies, &user);
Ok(Redirect::to(uri!(super::root::index)))
} else {
Err(login_error_redirect("Invalid username or password."))
}
}
#[post("/logout")]
fn logout(cookies: &CookieJar<'_>) -> Flash<Redirect> {
remove_login_cookie(cookies);
Flash::success(Redirect::to(uri!(login_page)), "Successfully logged out.")
}
#[get("/login")]
fn logged_in_user(_user: &User) -> Redirect {
Redirect::to(uri!(super::root::index))
}
#[get("/login", rank = 2)]
fn login_page(flash: Option<FlashMessage>) -> Template {
let mut context = HashMap::new();
if let Some(ref msg) = flash {
context.insert("flash", msg.msg());
}
Template::render("login", &context)
}
#[get("/register")]
fn register_page(flash: Option<FlashMessage>) -> Template {
let mut context = HashMap::new();
if let Some(ref msg) = flash {
context.insert("flash", msg.msg());
}
Template::render("registration", &context)
}
#[post("/register", data = "<registration>")]
async fn register(
mut cookies: &CookieJar<'_>,
registration: Form<Registration>,
conn: TenebrousDbConn,
) -> Result<Redirect, Flash<Redirect>> {
let existing_user = conn
.load_user(registration.username.clone())
.await
.map_err(|e| {
error!("registration - error loading existing user: {}", e);
registration_error_redirect("There was an error attempting to register.")
})?;
if existing_user.is_some() {
return Err(registration_error_redirect(format!(
"The username {} is already taken.",
registration.username
)));
}
let hashed_pw = users::hash_password(&registration.password).map_err(|e| {
error!("registration - password hashing error: {}", e);
registration_error_redirect("There was an error attempting to register.")
})?;
let user = NewUser {
username: registration.username.clone(),
password: hashed_pw,
};
let user = conn.insert_user(user).await.map_err(|e| {
error!("registration - could not insert user: {}", e);
registration_error_redirect("There was an error completing registration.")
})?;
add_login_cookie(&mut cookies, &user);
Ok(Redirect::to(uri!(super::root::index)))
}