everything works
This commit is contained in:
parent
57eb4001ee
commit
eda946fa4c
3 changed files with 50 additions and 59 deletions
47
src/auth.rs
47
src/auth.rs
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
36
src/login.rs
36
src/login.rs
|
@ -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()),
|
||||||
|
|
26
src/main.rs
26
src/main.rs
|
@ -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");
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in a new issue