wip: login, auth working, still need refactoring
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
pub mod models;
|
||||
pub mod user_admin_repository;
|
||||
pub mod utils;
|
||||
pub mod views;
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -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
8
rear_auth/src/utils.rs
Normal 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()
|
||||
}
|
||||
@@ -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(),
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user