botserver/src/auth/mod.rs

144 lines
4.3 KiB
Rust
Raw Normal View History

2025-10-06 10:30:17 -03:00
use argon2::{
password_hash::{rand_core::OsRng, PasswordHash, PasswordHasher, PasswordVerifier, SaltString},
Argon2,
};
2025-10-11 12:29:03 -03:00
use diesel::pg::PgConnection;
2025-10-12 13:27:48 -03:00
use diesel::prelude::*;
2025-10-06 10:30:17 -03:00
use redis::Client;
use std::sync::Arc;
use uuid::Uuid;
2025-10-12 13:27:48 -03:00
use crate::shared;
2025-10-06 10:30:17 -03:00
pub struct AuthService {
2025-10-11 12:29:03 -03:00
pub conn: PgConnection,
2025-10-06 10:30:17 -03:00
pub redis: Option<Arc<Client>>,
}
impl AuthService {
2025-10-11 12:29:03 -03:00
pub fn new(conn: PgConnection, redis: Option<Arc<Client>>) -> Self {
Self { conn, redis }
2025-10-06 10:30:17 -03:00
}
2025-10-11 12:29:03 -03:00
pub fn verify_user(
&mut self,
2025-10-06 10:30:17 -03:00
username: &str,
password: &str,
2025-10-06 19:12:13 -03:00
) -> Result<Option<Uuid>, Box<dyn std::error::Error + Send + Sync>> {
2025-10-11 12:29:03 -03:00
use crate::shared::models::users;
2025-10-12 13:27:48 -03:00
2025-10-11 12:29:03 -03:00
let user = users::table
.filter(users::username.eq(username))
.filter(users::is_active.eq(true))
.select((users::id, users::password_hash))
.first::<(Uuid, String)>(&mut self.conn)
.optional()?;
if let Some((user_id, password_hash)) = user {
2025-10-06 10:30:17 -03:00
if let Ok(parsed_hash) = PasswordHash::new(&password_hash) {
if Argon2::default()
.verify_password(password.as_bytes(), &parsed_hash)
.is_ok()
{
return Ok(Some(user_id));
}
}
}
Ok(None)
}
2025-10-11 12:29:03 -03:00
pub fn create_user(
&mut self,
2025-10-06 10:30:17 -03:00
username: &str,
email: &str,
password: &str,
2025-10-06 19:12:13 -03:00
) -> Result<Uuid, Box<dyn std::error::Error + Send + Sync>> {
2025-10-11 12:29:03 -03:00
use crate::shared::models::users;
use diesel::insert_into;
2025-10-12 13:27:48 -03:00
2025-10-06 10:30:17 -03:00
let salt = SaltString::generate(&mut OsRng);
let argon2 = Argon2::default();
2025-10-12 13:27:48 -03:00
let password_hash = argon2
.hash_password(password.as_bytes(), &salt)
2025-10-11 12:29:03 -03:00
.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e.to_string()))?
.to_string();
let user_id = Uuid::new_v4();
2025-10-12 13:27:48 -03:00
2025-10-11 12:29:03 -03:00
insert_into(users::table)
.values((
users::id.eq(user_id),
users::username.eq(username),
users::email.eq(email),
users::password_hash.eq(password_hash),
))
.execute(&mut self.conn)?;
Ok(user_id)
2025-10-06 10:30:17 -03:00
}
pub async fn delete_user_cache(
&self,
username: &str,
2025-10-06 19:12:13 -03:00
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
2025-10-06 10:30:17 -03:00
if let Some(redis_client) = &self.redis {
let mut conn = redis_client.get_multiplexed_async_connection().await?;
let cache_key = format!("auth:user:{}", username);
let _: () = redis::Cmd::del(&cache_key).query_async(&mut conn).await?;
}
Ok(())
}
2025-10-11 12:29:03 -03:00
pub fn update_user_password(
&mut self,
2025-10-06 10:30:17 -03:00
user_id: Uuid,
new_password: &str,
2025-10-06 19:12:13 -03:00
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
2025-10-11 12:29:03 -03:00
use crate::shared::models::users;
use diesel::update;
2025-10-12 13:27:48 -03:00
2025-10-06 10:30:17 -03:00
let salt = SaltString::generate(&mut OsRng);
let argon2 = Argon2::default();
2025-10-12 13:27:48 -03:00
let password_hash = argon2
.hash_password(new_password.as_bytes(), &salt)
2025-10-11 12:29:03 -03:00
.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e.to_string()))?
.to_string();
update(users::table.filter(users::id.eq(user_id)))
.set((
users::password_hash.eq(&password_hash),
users::updated_at.eq(diesel::dsl::now),
))
.execute(&mut self.conn)?;
if let Some(username) = users::table
.filter(users::id.eq(user_id))
.select(users::username)
.first::<String>(&mut self.conn)
.optional()?
2025-10-06 10:30:17 -03:00
{
2025-10-11 12:29:03 -03:00
// Note: This would need to be handled differently in async context
// For now, we'll just log it
log::info!("Would delete cache for user: {}", username);
2025-10-06 10:30:17 -03:00
}
Ok(())
}
2025-10-12 13:27:48 -03:00
pub(crate) fn get_user_by_id(
&mut self,
uid: Uuid,
) -> Result<Option<shared::models::User>, Box<dyn std::error::Error + Send + Sync>> {
use crate::shared::models::users;
let user = users::table
.filter(users::id.eq(uid))
.filter(users::is_active.eq(true))
.first::<shared::models::User>(&mut self.conn)
.optional()?;
Ok(user)
}
2025-10-06 10:30:17 -03:00
}