use argon2::{password_hash::SaltString, Argon2, PasswordHasher}; use rocket::{ async_trait, http::Status, request::{self, FromRequest, Outcome}, Request, }; use serde::{Deserialize, Serialize}; use sqlx::{FromRow, SqlitePool}; #[derive(FromRow, Debug, Serialize, Deserialize)] pub struct User { id: i64, name: String, pw: String, is_cox: bool, is_admin: bool, is_guest: bool, } #[derive(Debug)] pub enum LoginError { SqlxError(sqlx::Error), InvalidAuthenticationCombo, NotLoggedIn, } impl From for LoginError { fn from(sqlx_error: sqlx::Error) -> Self { Self::SqlxError(sqlx_error) } } impl User { async fn find_by_name(db: &SqlitePool, name: String) -> Result { let user: User = sqlx::query_as!( User, " SELECT id, name, pw, is_cox, is_admin, is_guest FROM user WHERE name like ? ", name ) .fetch_one(db) .await?; Ok(user) } pub async fn login(db: &SqlitePool, name: String, pw: String) -> Result { let user = User::find_by_name(db, name).await?; let salt = SaltString::from_b64("dS/X5/sPEKTj4Rzs/CuvzQ").unwrap(); let argon2 = Argon2::default(); let password_hash = argon2 .hash_password(&pw.as_bytes(), &salt) .unwrap() .to_string(); if password_hash == user.pw { return Ok(user); } Err(LoginError::InvalidAuthenticationCombo) } } #[async_trait] impl<'r> FromRequest<'r> for User { type Error = LoginError; async fn from_request(req: &'r Request<'_>) -> request::Outcome { match req.cookies().get_private("loggedin_user") { Some(user) => { let user: User = serde_json::from_str(&user.value()).unwrap(); //TODO: fixme Outcome::Success(user) } None => Outcome::Failure((Status::Unauthorized, LoginError::NotLoggedIn)), } } } #[cfg(test)] mod test { use crate::testdb; use super::User; use sqlx::SqlitePool; #[sqlx::test] fn succ_login_with_test_db() { let pool = testdb!(); User::login(&pool, "admin".into(), "admin".into()) .await .unwrap(); } #[sqlx::test] fn wrong_pw() { let pool = testdb!(); assert!(User::login(&pool, "admin".into(), "admi".into()) .await .is_err()); } #[sqlx::test] fn wrong_username() { let pool = testdb!(); assert!(User::login(&pool, "admi".into(), "admin".into()) .await .is_err()); } }