wip: login, auth working, still need refactoring

This commit is contained in:
2024-07-16 07:27:26 +02:00
parent c78e386645
commit 402585f968
16 changed files with 127 additions and 51 deletions

View File

@@ -1,3 +1,4 @@
pub mod models;
pub mod user_admin_repository;
pub mod utils;
pub mod views;

View File

@@ -5,11 +5,16 @@ use axum_login::{AuthUser, AuthzBackend};
use axum_login::{AuthnBackend, UserId};
use log::debug;
use password_auth::{generate_hash, is_hash_obsolete, verify_password};
use sea_orm::{ColumnTrait, DatabaseConnection, EntityTrait, ModelTrait, QueryFilter};
use sea_orm::{
ActiveModelTrait, ActiveValue, ColumnTrait, DatabaseConnection, EntityTrait, ModelTrait,
QueryFilter,
};
use serde::{Deserialize, Serialize};
use thiserror::Error;
use tokio::task;
use crate::utils::get_current_timestamp;
#[derive(Debug, Clone)]
pub struct UserRepository {
pub(crate) connection: DatabaseConnection,
@@ -35,6 +40,17 @@ impl UserRepository {
// As checking for obsoleteness errored out, we assume this a raw password.
generate_hash(password)
}
pub fn new_user(username: &str, password: &str) -> entity::user::ActiveModel {
entity::user::ActiveModel {
username: sea_orm::ActiveValue::Set(username.to_owned()),
password: sea_orm::ActiveValue::Set(UserRepository::encode_password(
password.to_owned(),
)),
date_joined: sea_orm::ActiveValue::Set(Some(get_current_timestamp())),
..<entity::user::ActiveModel as ActiveModelTrait>::default()
}
}
}
#[derive(Clone, Serialize, Deserialize)]
@@ -146,21 +162,23 @@ impl AuthnBackend for UserRepository {
.one(&self.connection)
.await?;
let user = if let Some(user) = user_found {
let rear_user: AuthenticatedUser = user.into();
Some(rear_user)
} else {
None
};
if let Some(user) = user_found {
let given_password = creds.password.clone();
let user_password = user.password.clone();
let verified =
task::spawn_blocking(move || verify_password(&given_password, &user_password))
.await?;
// Verifying the password is blocking and potentially slow, so we'll do so via
// `spawn_blocking`.
task::spawn_blocking(|| {
// We're using password-based authentication--this works by comparing our form
// input with an argon2 password hash.
Ok(user.filter(|user| verify_password(creds.password, &user.password).is_ok()))
})
.await?
if verified.is_ok() {
let mut db_user: entity::user::ActiveModel = user.into();
db_user.last_login = ActiveValue::Set(Some(get_current_timestamp()));
let user = db_user.update(&self.connection).await?;
let rear_user: AuthenticatedUser = user.into();
return Ok(Some(rear_user));
}
}
Ok(None) // No user found or verification failed
}
async fn get_user(&self, user_id: &UserId<Self>) -> Result<Option<Self::User>, Self::Error> {

View File

@@ -26,6 +26,7 @@ impl AdminRepository for UserRepository {
display_list: &["id", "username"],
fields: &[
"username",
"password",
"first_name",
"last_name",
"email",
@@ -35,7 +36,8 @@ impl AdminRepository for UserRepository {
],
// fields_readonly: &["last_login", "date_joined"]
}
.into()
.build()
.set_widget("password", Widget::default().as_password())
}
async fn get(&self, model: &RepositoryContext, id: &Self::Key) -> Option<RepositoryItem> {
@@ -83,14 +85,11 @@ impl AdminRepository for UserRepository {
async fn create(&mut self, model: &RepositoryContext, data: Value) -> Option<RepositoryItem> {
if let Value::Object(data) = data {
let username = data.get("username").unwrap().as_str().unwrap();
let password = data.get("password").unwrap().as_str().unwrap();
let mut user = entity::user::ActiveModel {
username: Set(username.to_owned()),
..Default::default()
};
let mut user = UserRepository::new_user(username, password);
let keys = [
"password",
"first_name",
"last_name",
"email",
@@ -102,16 +101,11 @@ impl AdminRepository for UserRepository {
for key in &keys {
if let Some(value) = data.get(*key) {
match *key {
"password" => {
user.password = Set(UserRepository::encode_password(
value.as_str().unwrap().to_owned(),
))
}
"first_name" => user.first_name = Set(value.as_str().map(|s| s.to_owned())),
"last_name" => user.last_name = Set(value.as_str().map(|s| s.to_owned())),
"email" => user.email = Set(value.as_str().map(|s| s.to_owned())),
"is_staff" => user.is_staff = Set(value.as_bool().unwrap_or(false)),
"is_active" => user.is_active = Set(value.as_bool().unwrap_or(false)),
"is_active" => user.is_active = Set(value.as_bool().unwrap_or(true)),
"is_superuser" => user.is_superuser = Set(value.as_bool().unwrap_or(false)),
_ => (),
}

8
rear_auth/src/utils.rs Normal file
View File

@@ -0,0 +1,8 @@
use sea_orm::prelude::DateTimeWithTimeZone;
pub fn get_current_timestamp() -> DateTimeWithTimeZone {
let utc_now = chrono::Utc::now();
// Apply the timezone offset to the UTC time: FixedOffset::east(offset_hours * 3600);
let local_time = utc_now;
local_time.into()
}

View File

@@ -70,7 +70,7 @@ mod post {
pub async fn logout(mut auth_session: AuthSession) -> impl IntoResponse {
match auth_session.logout().await {
Ok(_) => Redirect::to("/login").into_response(),
Ok(_) => Redirect::to("/admin/login").into_response(),
Err(_) => StatusCode::INTERNAL_SERVER_ERROR.into_response(),
}
}