diff --git a/src/server/auth.rs b/src/server/auth.rs new file mode 100644 index 0000000..f2aea43 --- /dev/null +++ b/src/server/auth.rs @@ -0,0 +1,91 @@ +use axum_login::{AuthUser, AuthnBackend, UserId}; +use diesel::prelude::*; +use diesel_async::RunQueryDsl; + +use crate::models::user::{DbUser, UserCredentials}; +use crate::server::database::{DbConn, DbPool}; +use crate::util::error::{Contextualize, Error, Result}; + +impl AuthUser for DbUser { + type Id = i32; + + fn id(&self) -> Self::Id { + self.id + } + + fn session_auth_hash(&self) -> &[u8] { + self.hashed_password.auth_hash() + } +} + +#[derive(Clone)] +pub struct AuthBackend { + pub db_pool: DbPool, +} + +impl AuthnBackend for AuthBackend { + type User = DbUser; + type Credentials = UserCredentials; + type Error = Error; + + async fn authenticate( + &self, + attempt_creds: Self::Credentials, + ) -> Result, Self::Error> { + let mut db_conn = self + .db_pool + .get() + .await + .err_context("Failed to get database pool connection")?; + + let user = get_user_by_username(&mut db_conn, attempt_creds.username) + .await + .err_context("Error fetching user for authentication check")?; + + let Some(user) = user else { return Ok(None) }; + + let password_result = user + .hashed_password + .check(attempt_creds.password) + .err_context("Error checking user password attempt")?; + + if password_result { + Ok(Some(user)) + } else { + Ok(None) + } + } + + async fn get_user(&self, user_id: &UserId) -> Result, Self::Error> { + let mut db_conn = self + .db_pool + .get() + .await + .err_context("Failed to get database pool connection")?; + + get_user_by_id(&mut db_conn, *user_id) + .await + .err_context("Failed fetching user for session") + } +} + +pub async fn get_user_by_id(db_conn: &mut DbConn, id: i32) -> Result> { + crate::schema::users::table + .find(id) + .first(db_conn) + .await + .optional() + .err_context("Error fetching user from database by id") +} + +pub async fn get_user_by_username( + db_conn: &mut DbConn, + username: String, +) -> Result> { + crate::schema::users::table + .filter(crate::schema::users::username.eq(username)) + .first(db_conn) + .await + .optional() + .err_context("Error fetching user from database by username") +} diff --git a/src/server/mod.rs b/src/server/mod.rs index 83e024a..7246556 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -1,3 +1,4 @@ +pub mod auth; pub mod config; pub mod database; pub mod key_val_store;