use crate::config::AppSettings;
use crate::context::RequestExtensions;
use crate::utils::constante::{
SESSION_USER_ID_KEY, SESSION_USER_IS_STAFF_KEY, SESSION_USER_IS_SUPERUSER_KEY,
SESSION_USER_ROLES_KEY, SESSION_USER_USERNAME_KEY,
};
use axum::{
extract::Request,
middleware::Next,
response::{IntoResponse, Redirect, Response},
};
use tower_sessions::Session;
#[derive(Clone, Debug, serde::Serialize)]
pub struct CurrentUser {
pub id: i32,
pub username: String,
pub is_staff: bool,
pub is_superuser: bool,
pub roles: Vec<String>,
}
impl CurrentUser {
pub fn has_role(&self, role: &str) -> bool {
self.roles.iter().any(|r| r == role)
}
pub fn has_any_role(&self, roles: &[&str]) -> bool {
roles.iter().any(|role| self.has_role(role))
}
pub fn can_access_admin(&self) -> bool {
self.is_staff || self.is_superuser
}
pub fn can_admin(&self, required_roles: &[&str]) -> bool {
if self.is_superuser {
return true;
}
self.has_any_role(required_roles)
}
}
pub async fn is_authenticated(session: &Session) -> bool {
session
.get::<i32>(SESSION_USER_ID_KEY)
.await
.ok()
.flatten()
.is_some()
}
pub async fn get_user_id(session: &Session) -> Option<i32> {
session.get::<i32>(SESSION_USER_ID_KEY).await.ok().flatten()
}
pub async fn get_username(session: &Session) -> Option<String> {
session
.get::<String>(SESSION_USER_USERNAME_KEY)
.await
.ok()
.flatten()
}
pub async fn login_user(
session: &Session,
user_id: i32,
username: &str,
) -> Result<(), tower_sessions::session::Error> {
session.insert(SESSION_USER_ID_KEY, user_id).await?;
session
.insert(SESSION_USER_USERNAME_KEY, username.to_string())
.await?;
Ok(())
}
pub async fn login_user_full(
session: &Session,
user_id: i32,
username: &str,
is_staff: bool,
is_superuser: bool,
roles: Vec<String>,
) -> Result<(), tower_sessions::session::Error> {
session.insert(SESSION_USER_ID_KEY, user_id).await?;
session
.insert(SESSION_USER_USERNAME_KEY, username.to_string())
.await?;
session.insert(SESSION_USER_IS_STAFF_KEY, is_staff).await?;
session
.insert(SESSION_USER_IS_SUPERUSER_KEY, is_superuser)
.await?;
session.insert(SESSION_USER_ROLES_KEY, roles).await?;
Ok(())
}
pub async fn logout(session: &Session) -> Result<(), tower_sessions::session::Error> {
session.delete().await
}
pub async fn has_permission(session: &Session, _permission: &str) -> bool {
is_authenticated(session).await
}
pub async fn login_required(session: Session, request: Request, next: Next) -> Response {
if is_authenticated(&session).await {
next.run(request).await
} else {
let redirect_anonymous = AppSettings::default().redirect_anonymous;
Redirect::to(&redirect_anonymous).into_response()
}
}
pub async fn redirect_if_authenticated(session: Session, request: Request, next: Next) -> Response {
if is_authenticated(&session).await {
let redirect_url = AppSettings::default().user_connected;
Redirect::to(&redirect_url).into_response()
} else {
next.run(request).await
}
}
pub async fn load_user_middleware(session: Session, mut request: Request, next: Next) -> Response {
if let (Some(user_id), Some(username)) =
(get_user_id(&session).await, get_username(&session).await)
{
let is_staff = session
.get::<bool>(SESSION_USER_IS_STAFF_KEY)
.await
.ok()
.flatten()
.unwrap_or(false);
let is_superuser = session
.get::<bool>(SESSION_USER_IS_SUPERUSER_KEY)
.await
.ok()
.flatten()
.unwrap_or(false);
let roles = session
.get::<Vec<String>>(SESSION_USER_ROLES_KEY)
.await
.ok()
.flatten()
.unwrap_or_default();
let current_user = CurrentUser {
id: user_id,
username,
is_staff,
is_superuser,
roles,
};
let extensions = RequestExtensions::new().with_current_user(current_user);
extensions.inject_request(&mut request);
}
next.run(request).await
}