87 lines
2.3 KiB
Rust
87 lines
2.3 KiB
Rust
use axum::http::StatusCode;
|
|
use sha2::{Digest, Sha256};
|
|
use sqlx::PgPool;
|
|
use uuid::Uuid;
|
|
|
|
use crate::auth::{create_token, Claims};
|
|
use crate::repositories;
|
|
use crate::repositories::session_repo;
|
|
|
|
pub struct LoginResponse {
|
|
pub token: String,
|
|
pub user_id: Uuid,
|
|
pub username: String,
|
|
pub role: String,
|
|
}
|
|
|
|
pub async fn login(
|
|
pool: &PgPool,
|
|
username: &str,
|
|
password: &str,
|
|
ip_address: Option<&str>,
|
|
user_agent: Option<&str>,
|
|
jwt_secret: &str,
|
|
jwt_expiry_hours: i64,
|
|
) -> Result<LoginResponse, StatusCode> {
|
|
let user = repositories::user_repo::find_by_username(pool, username)
|
|
.await
|
|
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?
|
|
.ok_or(StatusCode::UNAUTHORIZED)?;
|
|
|
|
if !user.is_active {
|
|
return Err(StatusCode::FORBIDDEN);
|
|
}
|
|
|
|
if user.is_locked {
|
|
return Err(StatusCode::LOCKED);
|
|
}
|
|
|
|
let password_valid = bcrypt::verify(password, &user.password_hash)
|
|
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
|
|
|
|
if !password_valid {
|
|
let _ = repositories::user_repo::increment_failed_login(pool, username).await;
|
|
return Err(StatusCode::UNAUTHORIZED);
|
|
}
|
|
|
|
let _ = repositories::user_repo::update_last_login(pool, user.id, ip_address.unwrap_or("")).await;
|
|
|
|
let role = "USER";
|
|
|
|
let token = create_token(user.id, &user.username, role, jwt_secret, jwt_expiry_hours)?;
|
|
|
|
let token_hash = hex::encode(Sha256::digest(token.as_bytes()));
|
|
|
|
let expires_at = chrono::Utc::now()
|
|
.checked_add_signed(chrono::Duration::hours(jwt_expiry_hours))
|
|
.ok_or(StatusCode::INTERNAL_SERVER_ERROR)?;
|
|
|
|
let _ = session_repo::create_session(
|
|
pool,
|
|
user.id,
|
|
&token_hash,
|
|
ip_address,
|
|
user_agent,
|
|
&expires_at,
|
|
)
|
|
.await;
|
|
|
|
Ok(LoginResponse {
|
|
token,
|
|
user_id: user.id,
|
|
username: user.username,
|
|
role: role.to_string(),
|
|
})
|
|
}
|
|
|
|
pub async fn logout(pool: &PgPool, token: &str) -> Result<(), StatusCode> {
|
|
let token_hash = hex::encode(Sha256::digest(token.as_bytes()));
|
|
session_repo::invalidate_session(pool, &token_hash)
|
|
.await
|
|
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)
|
|
}
|
|
|
|
pub fn validate_token(token: &str, jwt_secret: &str) -> Result<Claims, StatusCode> {
|
|
crate::auth::validate_token(token, jwt_secret)
|
|
}
|