tenebrous-sheets/src/models/users.rs

60 lines
1.7 KiB
Rust

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<String, ArgonError> {
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<User> {
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<Self, ()> {
let db = try_outcome!(request.guard::<TenebrousDbConn>().await);
let user_id: Option<i32> = request
.cookies()
.get_private("user_id")
.and_then(|cookie| cookie.value().parse().ok());
let user: &Option<User> = 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,
}