everything works

This commit is contained in:
Joe Ardent 2023-12-17 17:38:22 -08:00
parent 57eb4001ee
commit eda946fa4c
3 changed files with 50 additions and 59 deletions

View file

@ -1,13 +1,9 @@
use argon2::Argon2;
use async_trait::async_trait; use async_trait::async_trait;
use axum_login::{AuthnBackend, UserId}; use axum_login::{AuthnBackend, UserId};
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,
};
use crate::User; use crate::User;
@ -38,23 +34,48 @@ impl std::ops::Deref for AuthStore {
} }
} }
#[derive(Default, Debug, Clone, PartialEq, Eq)]
pub struct Credentials {
pub username: String,
pub password: String,
}
#[Error]
pub struct AuthError;
#[async_trait] #[async_trait]
impl AuthnBackend for AuthStore { impl AuthnBackend for AuthStore {
type User = User; type User = User;
type Credentials = String; type Credentials = Credentials;
type Error = sqlx::Error; type Error = AuthError;
async fn authenticate( async fn authenticate(
&self, &self,
_creds: Self::Credentials, creds: Self::Credentials,
) -> Result<Option<Self::User>, Self::Error> { ) -> Result<Option<Self::User>, Self::Error> {
todo!() let username = creds.username.trim();
let password = creds.password.trim();
let user = User::try_get(username, &self)
.await
.map_err(|_| AuthError)?;
let verifier = Argon2::default();
let hash = PasswordHash::new(&user.pwhash).map_err(|_| AuthError)?;
match verifier.verify_password(password.as_bytes(), &hash) {
Ok(_) => Ok(Some(user)),
_ => Ok(None),
}
} }
async fn get_user(&self, _user_id: &UserId<Self>) -> Result<Option<Self::User>, Self::Error> { async fn get_user(&self, user_id: &UserId<Self>) -> Result<Option<Self::User>, Self::Error> {
todo!() let user = sqlx::query_as("select * from users where id = ?")
.bind(user_id)
.fetch_optional(&self.0)
.await
.map_err(|_| AuthError)?;
Ok(user)
} }
} }

View file

@ -1,17 +1,13 @@
use argon2::{
password_hash::{PasswordHash, PasswordVerifier},
Argon2,
};
use axum::{ use axum::{
extract::State,
http::StatusCode, http::StatusCode,
response::{IntoResponse, Redirect, Response}, response::{IntoResponse, Redirect, Response},
Form, Form,
}; };
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use sqlx::SqlitePool;
use crate::{AuthSession, LoginPage, LogoutPage, LogoutSuccessPage, User};
use crate::{auth::Credentials, AuthSession, LoginPage, LogoutPage, LogoutSuccessPage};
//-************************************************************************ //-************************************************************************
// Constants // Constants
@ -52,6 +48,16 @@ impl IntoResponse for LoginError {
pub struct LoginPostForm { pub struct LoginPostForm {
pub username: String, pub username: String,
pub password: String, pub password: String,
pub destination: Option<String>,
}
impl From<LoginPostForm> for Credentials {
fn from(value: LoginPostForm) -> Self {
Self {
username: value.username,
password: value.password,
}
}
} }
//-************************************************************************ //-************************************************************************
@ -62,29 +68,19 @@ pub struct LoginPostForm {
#[axum::debug_handler] #[axum::debug_handler]
pub async fn post_login( pub async fn post_login(
mut auth: AuthSession, mut auth: AuthSession,
State(pool): State<SqlitePool>,
Form(login): Form<LoginPostForm>, Form(login): Form<LoginPostForm>,
) -> Result<impl IntoResponse, LoginError> { ) -> Result<impl IntoResponse, LoginError> {
let username = &login.username; let user = auth.authenticate(login.into()).await.map_err(|e| {
let username = username.trim();
let pw = &login.password;
let pw = pw.trim();
let user = User::try_get(username, &pool).await.map_err(|e| {
tracing::debug!("{e}"); tracing::debug!("{e}");
LoginErrorKind::Unknown LoginErrorKind::Unknown
})?; })?;
let verifier = Argon2::default(); match user {
let hash = PasswordHash::new(&user.pwhash).map_err(|_| LoginErrorKind::Internal)?; Some(user) => {
match verifier.verify_password(pw.as_bytes(), &hash) {
Ok(_) => {
// log them in and set a session cookie // log them in and set a session cookie
auth.login(&user) auth.login(&user)
.await .await
.map_err(|_| LoginErrorKind::Internal)?; .map_err(|_| LoginErrorKind::Internal)?;
Ok(Redirect::to("/")) Ok(Redirect::to("/"))
} }
_ => Err(LoginErrorKind::BadPassword.into()), _ => Err(LoginErrorKind::BadPassword.into()),

View file

@ -1,6 +1,5 @@
use std::net::SocketAddr; use std::net::SocketAddr;
use tokio::signal;
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
use what2watch::get_db_pool; use what2watch::get_db_pool;
@ -28,32 +27,7 @@ fn main() {
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();
//.with_graceful_shutdown(shutdown_signal()) // removed in 0.7 because of upstream dep changes
}); });
rt.block_on(pool.close()); rt.block_on(pool.close());
} }
async fn shutdown_signal() {
let ctrl_c = async {
signal::ctrl_c()
.await
.expect("failed to install Ctrl+C handler");
};
#[cfg(unix)]
let terminate = async {
signal::unix::signal(signal::unix::SignalKind::terminate())
.expect("failed to install signal handler")
.recv()
.await;
};
tokio::select! {
_ = ctrl_c => {},
_ = terminate => {},
}
println!(" signal received, starting graceful shutdown");
}