use crate::db::{Dao, TenebrousDbConn}; use argon2::{self, Config, Error as ArgonError}; use rand::Rng; use rocket::outcome::IntoOutcome; use rocket::request::{self, FromRequest, Request}; use serde::Serialize; pub(crate) fn hash_password(raw_password: &str) -> Result { let salt = rand::thread_rng().gen::<[u8; 16]>(); let config = Config::default(); argon2::hash_encoded(raw_password.as_bytes(), &salt, &config) } #[derive(Eq, PartialEq, Serialize, Debug, sqlx::FromRow)] pub struct User { pub id: i32, pub username: String, pub password: String, } impl User { pub fn verify_password(&self, raw_password: &str) -> bool { argon2::verify_encoded(&self.password, raw_password.as_bytes()).unwrap_or(false) } } async fn attempt_load_user<'a>(db: &'a TenebrousDbConn<'a>, id: i32) -> Option { db.load_user_by_id(id).await.ok().flatten() } /// Trait implementation to get the logged in user. #[rocket::async_trait] impl<'a, 'r> FromRequest<'a, 'r> for &'a User { type Error = (); async fn from_request(request: &'a Request<'r>) -> request::Outcome { let db = try_outcome!(request.guard::().await); let user_id: Option = request .cookies() .get_private("user_id") .and_then(|cookie| cookie.value().parse().ok()); let user: &Option = request .local_cache_async(async { match user_id { Some(id) => attempt_load_user(&db, id).await, _ => None, } }) .await; user.as_ref().or_forward(()) } } pub struct NewUser<'a> { pub username: &'a str, pub password: &'a str, }