use crate::auth::session::CurrentUser;
use crate::utils::trad::t;
use axum::{
http::StatusCode,
response::{IntoResponse, Response},
};
#[derive(Debug, Clone, Default)]
pub struct GuardRules {
pub login_required: bool,
pub staff: bool,
pub superuser: bool,
pub roles: Vec<String>,
}
impl GuardRules {
pub fn login_required() -> Self {
Self {
login_required: true,
..Self::default()
}
}
pub fn staff() -> Self {
Self {
login_required: true,
staff: true,
..Self::default()
}
}
pub fn superuser() -> Self {
Self {
login_required: true,
superuser: true,
..Self::default()
}
}
pub fn roles<R, S>(roles: R) -> Self
where
R: IntoIterator<Item = S>,
S: Into<String>,
{
Self {
login_required: true,
roles: roles.into_iter().map(Into::into).collect(),
..Self::default()
}
}
fn is_empty(&self) -> bool {
!self.login_required && !self.staff && !self.superuser && self.roles.is_empty()
}
}
pub fn evaluate_rules(
rules: &GuardRules,
user: Option<&CurrentUser>,
groupes: &[String],
) -> Result<(), Box<Response>> {
if rules.is_empty() {
return Ok(());
}
let Some(user) = user else {
return Err(refusal(StatusCode::UNAUTHORIZED, "forms.auth_required"));
};
let allowed = (!rules.staff || user.is_staff || user.is_superuser)
&& (!rules.superuser || user.is_superuser)
&& (rules.roles.is_empty()
|| user.is_superuser
|| rules.roles.iter().any(|role| groupes.contains(role)));
if allowed {
Ok(())
} else {
Err(refusal(StatusCode::FORBIDDEN, "forms.role_insufficient"))
}
}
fn refusal(status: StatusCode, key: &str) -> Box<Response> {
Box::new((status, t(key).into_owned()).into_response())
}