use crate::db::{Dao, TenebrousDbConn}; use crate::schema::users; use argon2::{self, Config, Error as ArgonError}; use rand::Rng; use rocket::outcome::IntoOutcome; use rocket::request::{self, FromRequest, Request}; use serde_derive::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, Queryable)] 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, id: i32) -> Option { db.load_user_by_id(id).await.ok().flatten() } #[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(()) } } #[derive(Insertable)] #[table_name = "users"] pub struct NewUser { pub username: String, pub password: String, }