RJ75MX6YMJSXYKW2YK6ZJNG2SQMPCPKICMESMC4ZD4EB2SCVJ2KAC
F5DMFQAO2IVQXQQ36Z5FRW5WNXKNTY2NAWMRR3VXIOWTPJ5UUDVAC
W3M3C7CCWHJWRWHULDWO45D3OFD4NL3V4OTJVIJCYRQG57Z2JTWQC
5UNA2DEALCSRBINR27KSA6OMD6GQAXHYZ35ICQ7NB62G2XP4FT5QC
DSWQKJRHGLKXUDXKMRXCIQZKEGXYV3H5LPUMGSV4HQ4HYPTI47GQC
KULVODXDL6KFQDLXYAAM3YPYNUMTRWV3LP6NRTYUHU7XBM2OKYZAC
T7TT5B4G3RBWVEG3BK6TPZQJJ3PSBBUZZCNBUM5KWYPVVPJ2VKAAC
KFVJ3KMWXEGILN4NWIWPPX7AU65M4H4UEAUIAQL2QSXOW3B5RFGQC
FS2NWBVN2SZB2FFPB3JSYT5URTVZEAZWMQ7QW4JUYPNJVDVAJTTQC
3E77DEMDLYBFJEGS2SLQKLQJQIXG2Y4TBDHAG3UKFLMKQ53CFOKQC
S6TFYMRGWC4PSRLQ4OAKE2Z3JFEVGF3RZ5Z3MEN5XSA4INPQILOAC
use rocket::{
form::{Context, Form},
response::{Flash, Redirect},
};
use rocket_dyn_templates::Template;
use crate::database::Database;
fn new() -> Template {
Template::render("new_user", &Context::default())
}
#[derive(FromForm)]
struct NewUser {
// TODO validate against regexp: [a-z_][a-z0-9_-]*[$] - Needs a valid linux username
#[field(validate = len(..20))]
pub user_name: String,
#[field(validate = len(3..))]
pub email: String,
#[field(validate = len(6..64))]
pub password: String,
}
#[post("/", data = "<user>")]
async fn create(
db: &State<Database>,
cookie: &CookieJar<'_>,
user: Form<NewUser>,
) -> Result<Flash<Redirect>, Flash<Redirect>> {
// TODO When the form parsing fails, the users gets no feedback
let mut new_user = User {
id: -1,
name: user.user_name.clone(),
email: user.email.clone(),
password: user.password.clone(),
};
match new_user.create(db).await {
Ok(id) => {
new_user.id = id;
set_user_cookie(cookie, new_user);
return Ok(Flash::success(Redirect::to("/"), "Signed up succesfully"));
}
Err(_e) => Err(Flash::error(
Redirect::to("/users/new"),
"Something went wrong",
)), //TODO Show the error to the user in a flash message,
}
}
#[derive(FromForm)]
struct SignIn {
#[field(validate = len(..20))]
pub user_name: String,
#[field(validate = len(6..64))]
pub password: String,
}
#[get("/sign_in")]
async fn get_sign_in() -> Template {
Template::render("sign_in", &Context::default())
}
#[post("/sign_in", data = "<user>")]
async fn sign_in(
db: &State<Database>,
jar: &CookieJar<'_>,
user: Form<SignIn>,
) -> Result<Flash<Redirect>, Flash<Redirect>> {
// TODO figure out Rust and rewrite this without the nested matching
match User::authenticate(db, user.user_name.clone(), user.password.clone()).await {
Ok(u) => match u {
Some(u2) => {
set_user_cookie(jar, u2);
return Ok(Flash::success(Redirect::to("/"), "Signed in!"));
}
None => return Err(Flash::error(Redirect::to("./sign_in"), "Error signing in")),
},
Err(_e) => {
return Err(Flash::error(
Redirect::to("./sign_in"),
"SQL Error signing in",
))
}
}
}
static COOKIE_USER_ID_KEY: &str = "user_id";
#[get("/sign_out")]
fn sign_out(jar: &CookieJar<'_>) -> Flash<Redirect> {
jar.remove_private(Cookie::named(COOKIE_USER_ID_KEY));
Flash::success(Redirect::to("/"), "Signed out succesfully")
}
fn set_user_cookie(jar: &CookieJar<'_>, user: User) {
jar.add_private(Cookie::new(COOKIE_USER_ID_KEY, user.id.to_string()))
}
pub fn routes() -> Vec<Route> {
}
routes![
new,
new_already_user,
create,
get_sign_in,
sign_in,
sign_out
]
}
fn get_user_id_from_cookie(jar: &CookieJar) -> Option<i32> {
match jar.get_private(COOKIE_USER_ID_KEY) {
Some(id) => Some(id.value().parse::<i32>().ok()?),
None => None,
}
#[get("/new", rank = 1)]
fn new_already_user(_user: User) -> Redirect {
Redirect::to("/")
}
// TODO decide if this routing file is the place for this guard to be at?
#[rocket::async_trait]
impl<'r> FromRequest<'r> for User {
type Error = ();
async fn from_request(request: &'r Request<'_>) -> request::Outcome<Self, Self::Error> {
let db = try_outcome!(request.guard::<&State<Database>>().await);
// &State<Database>>().await);
let cookies = request.cookies();
let some_id = match get_user_id_from_cookie(cookies) {
Some(id) => id,
None => return Outcome::Failure((Status::Unauthorized, ())),
};
match User::find(db, some_id).await {
Ok(u) => Outcome::Success(u),
Err(_e) => Outcome::Failure((Status::Unauthorized, ())),
}
}
}
#[get("/new", rank = 2)]
use crate::models::users::User;
use rocket::outcome::try_outcome;
Request, Route, State,
http::{Cookie, CookieJar, Status},
request,
request::{FromRequest, Outcome},
use rocket::{
form::{Context, Form},
http::{Cookie, CookieJar, Status},
request,
request::{FromRequest, Outcome},
response::{Flash, Redirect},
Request, Route, State,
};
use rocket_dyn_templates::Template;
use crate::database::Database;
use crate::models::users::User;
use rocket::outcome::try_outcome;
// TODO decide if this routing file is the place for this guard to be at?
#[rocket::async_trait]
impl<'r> FromRequest<'r> for User {
type Error = ();
async fn from_request(request: &'r Request<'_>) -> request::Outcome<Self, Self::Error> {
let db = try_outcome!(request.guard::<&State<Database>>().await);
// &State<Database>>().await);
let cookies = request.cookies();
let some_id = match get_user_id_from_cookie(cookies) {
Some(id) => id,
None => return Outcome::Failure((Status::Unauthorized, ())),
};
match User::find(db, some_id).await {
Ok(u) => Outcome::Success(u),
Err(_e) => Outcome::Failure((Status::Unauthorized, ())),
}
}
}
#[get("/users/new", rank = 2)]
fn new() -> Template {
Template::render("new_user", &Context::default())
}
#[get("/users/new", rank = 1)]
fn new_already_user(_user: User) -> Redirect {
Redirect::to("/")
}
#[derive(FromForm)]
struct NewUser {
// TODO validate against regexp: [a-z_][a-z0-9_-]*[$] - Needs a valid linux username
#[field(validate = len(..20))]
pub user_name: String,
#[field(validate = len(3..))]
pub email: String,
#[field(validate = len(6..64))]
pub password: String,
}
#[post("/users", data = "<user>")]
async fn create(
db: &State<Database>,
cookie: &CookieJar<'_>,
user: Form<NewUser>,
) -> Result<Flash<Redirect>, Flash<Redirect>> {
// TODO When the form parsing fails, the users gets no feedback
let mut new_user = User {
id: -1,
name: user.user_name.clone(),
email: user.email.clone(),
password: user.password.clone(),
};
match new_user.create(db).await {
Ok(id) => {
new_user.id = id;
set_user_cookie(cookie, new_user);
return Ok(Flash::success(Redirect::to("/"), "Signed up succesfully"));
}
Err(_e) => Err(Flash::error(
Redirect::to("/users/new"),
"Something went wrong",
)), //TODO Show the error to the user in a flash message,
}
}
#[derive(FromForm)]
struct SignIn {
#[field(validate = len(..20))]
pub user_name: String,
#[field(validate = len(6..64))]
pub password: String,
}
#[get("/users/sign_in")]
async fn get_sign_in() -> Template {
Template::render("sign_in", &Context::default())
}
#[post("/users/sign_in", data = "<user>")]
async fn sign_in(
db: &State<Database>,
jar: &CookieJar<'_>,
user: Form<SignIn>,
) -> Result<Flash<Redirect>, Flash<Redirect>> {
// TODO figure out Rust and rewrite this without the nested matching
match User::authenticate(db, user.user_name.clone(), user.password.clone()).await {
Ok(u) => match u {
Some(u2) => {
set_user_cookie(jar, u2);
return Ok(Flash::success(Redirect::to("/"), "Signed in!"));
}
None => return Err(Flash::error(Redirect::to("./sign_in"), "Error signing in")),
},
Err(_e) => {
return Err(Flash::error(
Redirect::to("./sign_in"),
"SQL Error signing in",
))
}
}
}
static COOKIE_USER_ID_KEY: &str = "user_id";
#[get("/users/sign_out")]
fn sign_out(jar: &CookieJar<'_>) -> Flash<Redirect> {
jar.remove_private(Cookie::named(COOKIE_USER_ID_KEY));
Flash::success(Redirect::to("/"), "Signed out succesfully")
}
fn set_user_cookie(jar: &CookieJar<'_>, user: User) {
jar.add_private(Cookie::new(COOKIE_USER_ID_KEY, user.id.to_string()))
}
fn get_user_id_from_cookie(jar: &CookieJar) -> Option<i32> {
match jar.get_private(COOKIE_USER_ID_KEY) {
Some(id) => Some(id.value().parse::<i32>().ok()?),
None => None,
}
}
pub fn routes() -> Vec<Route> {
routes![
new,
new_already_user,
create,
get_sign_in,
sign_in,
sign_out
]
}