minor re-org and tidy

This commit is contained in:
Joe Ardent 2023-12-18 16:48:54 -08:00
parent eda946fa4c
commit dfbf605257
5 changed files with 33 additions and 38 deletions

View file

@ -1,6 +1,7 @@
use argon2::Argon2; use argon2::Argon2;
use async_trait::async_trait; use async_trait::async_trait;
use axum_login::{AuthnBackend, UserId}; use axum_login::{AuthUser, AuthnBackend, UserId};
use julid::Julid;
use password_hash::{PasswordHash, PasswordVerifier}; use password_hash::{PasswordHash, PasswordVerifier};
use sqlx::SqlitePool; use sqlx::SqlitePool;
use tower_sessions::{cookie::time::Duration, Expiry, SessionManagerLayer, SqliteStore}; use tower_sessions::{cookie::time::Duration, Expiry, SessionManagerLayer, SqliteStore};
@ -46,9 +47,7 @@ pub struct AuthError;
#[async_trait] #[async_trait]
impl AuthnBackend for AuthStore { impl AuthnBackend for AuthStore {
type User = User; type User = User;
type Credentials = Credentials; type Credentials = Credentials;
type Error = AuthError; type Error = AuthError;
async fn authenticate( async fn authenticate(
@ -57,9 +56,7 @@ impl AuthnBackend for AuthStore {
) -> Result<Option<Self::User>, Self::Error> { ) -> Result<Option<Self::User>, Self::Error> {
let username = creds.username.trim(); let username = creds.username.trim();
let password = creds.password.trim(); let password = creds.password.trim();
let user = User::try_get(username, &self) let user = User::try_get(username, self).await.map_err(|_| AuthError)?;
.await
.map_err(|_| AuthError)?;
let verifier = Argon2::default(); let verifier = Argon2::default();
let hash = PasswordHash::new(&user.pwhash).map_err(|_| AuthError)?; let hash = PasswordHash::new(&user.pwhash).map_err(|_| AuthError)?;
@ -79,6 +76,18 @@ impl AuthnBackend for AuthStore {
} }
} }
impl AuthUser for User {
type Id = Julid;
fn id(&self) -> Self::Id {
self.id
}
fn session_auth_hash(&self) -> &[u8] {
self.pwhash.as_bytes()
}
}
pub async fn session_layer(pool: SqlitePool) -> SessionManagerLayer<SqliteStore> { pub async fn session_layer(pool: SqlitePool) -> SessionManagerLayer<SqliteStore> {
let store = SqliteStore::new(pool); let store = SqliteStore::new(pool);
store store

View file

@ -3,9 +3,6 @@ use sqlx::SqlitePool;
#[macro_use] #[macro_use]
extern crate justerror; extern crate justerror;
#[cfg(test)]
pub mod test_utils;
/// Some public interfaces for interacting with the database outside of the web /// Some public interfaces for interacting with the database outside of the web
/// app /// app
pub use db::get_db_pool; pub use db::get_db_pool;
@ -89,6 +86,9 @@ pub async fn app(db_pool: sqlx::SqlitePool) -> IntoMakeService<axum::Router> {
.into_make_service() .into_make_service()
} }
#[cfg(test)]
pub mod test_utils;
//-************************************************************************ //-************************************************************************
// tests for the proc macro for optional user // tests for the proc macro for optional user
//-************************************************************************ //-************************************************************************

View file

@ -1,4 +1,3 @@
use axum::{ use axum::{
http::StatusCode, http::StatusCode,
response::{IntoResponse, Redirect, Response}, response::{IntoResponse, Redirect, Response},
@ -6,7 +5,6 @@ use axum::{
}; };
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::{auth::Credentials, AuthSession, LoginPage, LogoutPage, LogoutSuccessPage}; use crate::{auth::Credentials, AuthSession, LoginPage, LogoutPage, LogoutSuccessPage};
//-************************************************************************ //-************************************************************************
@ -44,7 +42,7 @@ impl IntoResponse for LoginError {
} }
// for receiving form submissions // for receiving form submissions
#[derive(Debug, Default, Deserialize, Serialize, PartialEq, Eq)] #[derive(Clone, Debug, Default, Deserialize, Serialize, PartialEq, Eq)]
pub struct LoginPostForm { pub struct LoginPostForm {
pub username: String, pub username: String,
pub password: String, pub password: String,
@ -52,10 +50,10 @@ pub struct LoginPostForm {
} }
impl From<LoginPostForm> for Credentials { impl From<LoginPostForm> for Credentials {
fn from(value: LoginPostForm) -> Self { fn from(form: LoginPostForm) -> Self {
Self { Self {
username: value.username, username: form.username,
password: value.password, password: form.password,
} }
} }
} }
@ -68,9 +66,10 @@ impl From<LoginPostForm> for Credentials {
#[axum::debug_handler] #[axum::debug_handler]
pub async fn post_login( pub async fn post_login(
mut auth: AuthSession, mut auth: AuthSession,
Form(login): Form<LoginPostForm>, Form(mut login_form): Form<LoginPostForm>,
) -> Result<impl IntoResponse, LoginError> { ) -> Result<impl IntoResponse, LoginError> {
let user = auth.authenticate(login.into()).await.map_err(|e| { let dest = login_form.destination.take();
let user = auth.authenticate(login_form.into()).await.map_err(|e| {
tracing::debug!("{e}"); tracing::debug!("{e}");
LoginErrorKind::Unknown LoginErrorKind::Unknown
})?; })?;
@ -81,7 +80,10 @@ pub async fn post_login(
auth.login(&user) auth.login(&user)
.await .await
.map_err(|_| LoginErrorKind::Internal)?; .map_err(|_| LoginErrorKind::Internal)?;
Ok(Redirect::to("/")) match dest {
Some(dest) => Ok(Redirect::to(&dest)),
_ => Ok(Redirect::to("/")),
}
} }
_ => Err(LoginErrorKind::BadPassword.into()), _ => Err(LoginErrorKind::BadPassword.into()),
} }

View file

@ -24,7 +24,6 @@ fn main() {
rt.block_on(async { rt.block_on(async {
let addr: SocketAddr = ([0, 0, 0, 0], 3000).into(); let addr: SocketAddr = ([0, 0, 0, 0], 3000).into();
tracing::debug!("binding to {addr:?}"); tracing::debug!("binding to {addr:?}");
let listener = tokio::net::TcpListener::bind(&addr).await.unwrap(); let listener = tokio::net::TcpListener::bind(&addr).await.unwrap();
axum::serve(listener, app).await.unwrap(); axum::serve(listener, app).await.unwrap();
}); });

View file

@ -1,19 +1,15 @@
use std::{ use std::fmt::{Debug, Display};
fmt::{Debug, Display},
time::{SystemTime, UNIX_EPOCH},
};
use axum::{ use axum::{
extract::{Request, State}, extract::{Request, State},
middleware::Next, middleware::Next,
response::IntoResponse, response::IntoResponse,
}; };
use axum_login::AuthUser;
use julid::Julid; use julid::Julid;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use sqlx::SqlitePool; use sqlx::SqlitePool;
use crate::auth::AuthSession; use crate::AuthSession;
const USERNAME_QUERY: &str = "select * from users where username = $1"; const USERNAME_QUERY: &str = "select * from users where username = $1";
const LAST_SEEN_QUERY: &str = "update users set last_seen = (select unixepoch()) where id = $1"; const LAST_SEEN_QUERY: &str = "update users set last_seen = (select unixepoch()) where id = $1";
@ -53,20 +49,8 @@ impl Display for User {
} }
} }
impl AuthUser for User {
type Id = Julid;
fn id(&self) -> Self::Id {
self.id
}
fn session_auth_hash(&self) -> &[u8] {
self.pwhash.as_bytes()
}
}
impl User { impl User {
pub async fn try_get(username: &str, db: &SqlitePool) -> Result<User, impl std::error::Error> { pub async fn try_get(username: &str, db: &SqlitePool) -> Result<Self, impl std::error::Error> {
sqlx::query_as(USERNAME_QUERY) sqlx::query_as(USERNAME_QUERY)
.bind(username) .bind(username)
.fetch_one(db) .fetch_one(db)
@ -98,6 +82,7 @@ pub async fn handle_update_last_seen(
request: Request, request: Request,
next: Next, next: Next,
) -> impl IntoResponse { ) -> impl IntoResponse {
use std::time::{SystemTime, UNIX_EPOCH};
if let Some(user) = auth.user { if let Some(user) = auth.user {
if let Some(then) = user.last_seen { if let Some(then) = user.last_seen {
let now = SystemTime::now() let now = SystemTime::now()