minor re-org and tidy
This commit is contained in:
parent
eda946fa4c
commit
dfbf605257
5 changed files with 33 additions and 38 deletions
21
src/auth.rs
21
src/auth.rs
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
//-************************************************************************
|
//-************************************************************************
|
||||||
|
|
20
src/login.rs
20
src/login.rs
|
@ -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()),
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
});
|
});
|
||||||
|
|
23
src/users.rs
23
src/users.rs
|
@ -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()
|
||||||
|
|
Loading…
Reference in a new issue