use axum::body::Body;
use axum::http::Request;
use axum::middleware::Next;
use axum::response::Response;
use crate::config::RateLimitConfig;
use crate::state::AppState;
use gradatum_warden::{WardenConfig, WardenLayer};
pub fn build_warden_layer(cfg: &RateLimitConfig) -> Option<WardenLayer> {
if !cfg.enabled {
return None;
}
let warden_cfg = WardenConfig {
enabled: true,
rate_limit_per_minute: cfg.per_minute,
rate_limit_burst: cfg.burst,
bypass_loopback: cfg.exempt_localhost,
ip_allow: vec![],
ip_deny: vec![],
};
Some(WardenLayer::new(warden_cfg).expect(
"config warden invalide — per_minute et burst doivent être > 0, \
garantis par RateLimitConfig::default() (60, 10)",
))
}
pub async fn auth_middleware(
axum::extract::State(state): axum::extract::State<AppState>,
mut request: Request<Body>,
next: Next,
) -> Response {
let trust = extract_trust(&state, &request);
request.extensions_mut().insert(trust);
next.run(request).await
}
fn extract_trust(state: &AppState, request: &Request<Body>) -> gradatum_core::trust::TrustContext {
let header_value = match request.headers().get(axum::http::header::AUTHORIZATION) {
Some(v) => v,
None => return gradatum_core::trust::TrustContext::Unauthenticated,
};
let raw = match header_value.to_str() {
Ok(s) => s,
Err(_) => {
tracing::debug!("Authorization header contient des octets non-UTF-8 — ignoré");
return gradatum_core::trust::TrustContext::Unauthenticated;
}
};
let token = match raw.strip_prefix("Bearer ") {
Some(t) if !t.is_empty() => t,
_ => return gradatum_core::trust::TrustContext::Unauthenticated,
};
match state.jwt.verify(token) {
Ok(claims) => {
tracing::debug!(
sub = %claims.sub,
tenant = %claims.tenant_id,
"JWT vérifié avec succès"
);
gradatum_core::trust::TrustContext::BearerToken {
kid: state.jwt.kid().to_string(),
aud: claims.aud,
sub: claims.sub,
scopes: claims.scopes,
tenant_id: claims.tenant_id,
}
}
Err(e) => {
tracing::debug!(err = %e, "JWT invalide — TrustContext::Unauthenticated");
gradatum_core::trust::TrustContext::Unauthenticated
}
}
}