init
This commit is contained in:
@@ -9,11 +9,13 @@ pub struct Claims {
|
||||
pub expire_at: usize, // Expiration time
|
||||
pub created_at: usize, // Issued at
|
||||
pub username: String,
|
||||
pub is_superuser: bool, // Ajoutez ce champ
|
||||
}
|
||||
|
||||
pub fn create_jwt(
|
||||
user_id: Uuid,
|
||||
username: &str,
|
||||
is_superuser: bool, // Ajoutez l'argument ici
|
||||
secret: &str,
|
||||
expiration_seconds: u64,
|
||||
) -> Result<String, jsonwebtoken::errors::Error> {
|
||||
@@ -27,6 +29,7 @@ pub fn create_jwt(
|
||||
expire_at: (now + expiration_seconds) as usize,
|
||||
created_at: now as usize,
|
||||
username: username.to_string(),
|
||||
is_superuser, // Et ici
|
||||
};
|
||||
|
||||
encode(
|
||||
|
||||
+80
-28
@@ -1,28 +1,78 @@
|
||||
use axum::{extract::State, http::Request, middleware::Next, response::Response};
|
||||
use axum::{
|
||||
body::Body,
|
||||
extract::State,
|
||||
http::{header, Request},
|
||||
middleware::Next,
|
||||
response::{IntoResponse, Response},
|
||||
};
|
||||
use std::sync::Arc;
|
||||
use std::time::Instant;
|
||||
use tracing::info;
|
||||
use tracing::{info, Instrument};
|
||||
use uuid::Uuid;
|
||||
|
||||
use super::context::{CurrentUser, RequestContext};
|
||||
use super::error::HTTPError;
|
||||
use super::metrics::HttpMetrics;
|
||||
use crate::auth::token::verify_jwt;
|
||||
use crate::core::AppState;
|
||||
|
||||
pub async fn context_middleware(
|
||||
State(app_state): State<AppState>,
|
||||
mut req: Request<axum::body::Body>,
|
||||
/// Middleware pour collecter les métriques HTTP.
|
||||
pub async fn metrics_middleware(
|
||||
State(metrics): State<Arc<HttpMetrics>>,
|
||||
req: Request<Body>,
|
||||
next: Next,
|
||||
) -> Response {
|
||||
metrics.inc_request();
|
||||
let started = Instant::now();
|
||||
let response = next.run(req).await;
|
||||
let latency = started.elapsed();
|
||||
metrics.record_response(response.status().as_u16(), latency);
|
||||
response
|
||||
}
|
||||
|
||||
/// Middleware d'initialisation du contexte de requête et de traçage de base.
|
||||
pub async fn request_context_middleware(mut req: Request<Body>, next: Next) -> Response {
|
||||
let request_id = Uuid::new_v4();
|
||||
let started_at = Instant::now();
|
||||
|
||||
// Infos "type Django request"
|
||||
let method = req.method().clone();
|
||||
let uri = req.uri().clone();
|
||||
|
||||
// Authentification par JWT
|
||||
// On injecte un contexte initial sans utilisateur
|
||||
req.extensions_mut().insert(RequestContext {
|
||||
request_id,
|
||||
started_at,
|
||||
method: method.clone(),
|
||||
uri: uri.clone(),
|
||||
user: None,
|
||||
});
|
||||
|
||||
let span = tracing::info_span!(
|
||||
"request",
|
||||
request_id = %request_id,
|
||||
method = %method,
|
||||
uri = %uri,
|
||||
);
|
||||
|
||||
async move {
|
||||
info!("Incoming request");
|
||||
next.run(req).await
|
||||
}
|
||||
.instrument(span)
|
||||
.await
|
||||
}
|
||||
|
||||
/// Middleware d'authentification par JWT.
|
||||
/// Tente d'identifier l'utilisateur et met à jour le RequestContext.
|
||||
pub async fn auth_middleware(
|
||||
State(app_state): State<AppState>,
|
||||
mut req: Request<Body>,
|
||||
next: Next,
|
||||
) -> Response {
|
||||
// Extraction du JWT depuis le header Authorization
|
||||
let user: Option<CurrentUser> = match req
|
||||
.headers()
|
||||
.get(axum::http::header::AUTHORIZATION)
|
||||
.get(header::AUTHORIZATION)
|
||||
.and_then(|v| v.to_str().ok())
|
||||
.and_then(|auth_header| {
|
||||
if auth_header.starts_with("Bearer ") {
|
||||
@@ -44,26 +94,28 @@ pub async fn context_middleware(
|
||||
None => None,
|
||||
};
|
||||
|
||||
let user_id = user.as_ref().map(|u| u.id);
|
||||
// Mise à jour du RequestContext existant
|
||||
if let Some(user) = &user {
|
||||
info!(user_id = %user.id, username = %user.username, "User identified");
|
||||
if let Some(ctx) = req.extensions_mut().get_mut::<RequestContext>() {
|
||||
ctx.user = Some(user.clone());
|
||||
}
|
||||
}
|
||||
|
||||
// Injecte le contexte dans la requête (espace de stockage partagé)
|
||||
// C'est ce qui permettra aux extracteurs comme 'CurrentUser' de retrouver ces données plus tard.
|
||||
req.extensions_mut().insert(RequestContext {
|
||||
request_id,
|
||||
started_at,
|
||||
method: method.clone(),
|
||||
uri: uri.clone(),
|
||||
user,
|
||||
});
|
||||
|
||||
info!(
|
||||
request_id = %request_id,
|
||||
user_id = ?user_id,
|
||||
method = %method,
|
||||
uri = %uri,
|
||||
"Incoming request"
|
||||
);
|
||||
|
||||
// Passe la requête au reste de la stack
|
||||
next.run(req).await
|
||||
}
|
||||
|
||||
/// Middleware de sécurité qui impose une authentification.
|
||||
pub async fn require_auth(req: Request<Body>, next: Next) -> Response {
|
||||
let authenticated = req
|
||||
.extensions()
|
||||
.get::<RequestContext>()
|
||||
.and_then(|ctx| ctx.user.as_ref())
|
||||
.is_some();
|
||||
|
||||
if authenticated {
|
||||
next.run(req).await
|
||||
} else {
|
||||
HTTPError::Unauthorized.into_response()
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ use axum::Router;
|
||||
pub mod context;
|
||||
pub mod error;
|
||||
pub mod metrics;
|
||||
mod middleware;
|
||||
pub mod middleware;
|
||||
pub mod server;
|
||||
pub mod validation;
|
||||
|
||||
|
||||
+24
-23
@@ -20,7 +20,7 @@ use crate::core::AppState;
|
||||
use crate::routes;
|
||||
|
||||
use super::metrics::HttpMetrics;
|
||||
use super::middleware::context_middleware;
|
||||
use super::middleware;
|
||||
|
||||
// ── Erreurs ───────────────────────────────────────────────────────────────────
|
||||
|
||||
@@ -44,8 +44,9 @@ pub enum HttpServerError {
|
||||
/// - Injecte [`AppState`] via le middleware (les handlers l'obtiendront via
|
||||
/// `State<AppState>` quand ils seront implémentés ; `.with_state()` sera
|
||||
/// ajouté en conséquence).
|
||||
/// - Applique [`context_middleware`] (authentification JWT + contexte de
|
||||
/// requête) sur l'ensemble du router.
|
||||
/// - Applique [`middleware::request_context_middleware`] (contexte de requête)
|
||||
/// et [`middleware::auth_middleware`] (authentification JWT) sur l'ensemble
|
||||
/// du router.
|
||||
/// - Empile les layers Tower : [`CatchPanicLayer`], [`CorsLayer`] (permissif),
|
||||
/// [`TraceLayer`].
|
||||
/// - Collecte des métriques via [`HttpMetrics`] et les reporte périodiquement.
|
||||
@@ -104,35 +105,35 @@ impl HttpServer {
|
||||
/// La future se résout lorsqu'un signal de shutdown est reçu ou qu'une
|
||||
/// erreur I/O fatale survient.
|
||||
pub async fn run(mut self) -> Result<(), HttpServerError> {
|
||||
let metrics = self.metrics.clone();
|
||||
let app_state = self.app_state.clone();
|
||||
|
||||
// Construit le router avec state + middleware + layers Tower.
|
||||
//
|
||||
// Ordre d'application en axum : chaque `.layer()` enveloppe le service
|
||||
// courant — le dernier appel devient la couche la plus externe.
|
||||
// Ordre d'application en axum (LIFO) : le dernier .layer() ajouté est le premier exécuté.
|
||||
//
|
||||
// CatchPanicLayer (outermost — intercepte tout panic en dessous)
|
||||
// └─ CorsLayer (répond aux preflight avant auth)
|
||||
// └─ TraceLayer (span tracing sur la durée totale)
|
||||
// └─ context_middleware (JWT + RequestContext + métriques)
|
||||
// └─ routes (handlers)
|
||||
// Courant de la requête :
|
||||
// 1. CatchPanicLayer (le plus externe)
|
||||
// 2. CorsLayer
|
||||
// 3. TraceLayer
|
||||
// 4. metrics_middleware (commence le chrono)
|
||||
// 5. request_context_middleware (init ID)
|
||||
// 6. auth_middleware (identifie l'utilisateur)
|
||||
// 7. Routes (handlers)
|
||||
let app: Router = routes::router()
|
||||
.with_state(app_state.clone())
|
||||
// Innermost : auth JWT, injection contexte, métriques
|
||||
// Identification de l'utilisateur (optionnelle ici)
|
||||
.layer(axum_middleware::from_fn_with_state(
|
||||
app_state.clone(),
|
||||
move |state, req, next| {
|
||||
let metrics = metrics.clone();
|
||||
async move {
|
||||
metrics.inc_request();
|
||||
let started = std::time::Instant::now();
|
||||
let response = context_middleware(state, req, next).await;
|
||||
let latency = started.elapsed();
|
||||
metrics.record_response(response.status().as_u16(), latency);
|
||||
response
|
||||
}
|
||||
},
|
||||
middleware::auth_middleware,
|
||||
))
|
||||
// Initialisation du contexte (ID de requête, etc.)
|
||||
.layer(axum_middleware::from_fn(
|
||||
middleware::request_context_middleware,
|
||||
))
|
||||
// Métriques (mesure de la durée totale de la requête)
|
||||
.layer(axum_middleware::from_fn_with_state(
|
||||
app_state.metrics.http.clone(),
|
||||
middleware::metrics_middleware,
|
||||
))
|
||||
// Spans tracing par requête (method, uri, status, latency)
|
||||
.layer(TraceLayer::new_for_http())
|
||||
|
||||
+14
-10
@@ -1,5 +1,6 @@
|
||||
use crate::http::middleware;
|
||||
use crate::http::OxRouter;
|
||||
use axum::Router;
|
||||
use axum::{middleware as axum_middleware, Router};
|
||||
use utoipa::OpenApi;
|
||||
use utoipa_swagger_ui::SwaggerUi;
|
||||
|
||||
@@ -15,19 +16,22 @@ pub mod server;
|
||||
pub mod user;
|
||||
|
||||
pub fn router() -> OxRouter {
|
||||
let api_routes = Router::new()
|
||||
.merge(auth::routes::router())
|
||||
// Routes nécessitant une authentification
|
||||
let secure_routes = Router::new()
|
||||
.merge(server::routes::router())
|
||||
.merge(category::routes::router())
|
||||
.merge(channel::routes::router())
|
||||
.merge(group::routes::router())
|
||||
.merge(message::routes::router())
|
||||
.merge(user::routes::router());
|
||||
.merge(user::routes::router())
|
||||
.layer(axum_middleware::from_fn(middleware::require_auth));
|
||||
|
||||
Router::new()
|
||||
.nest("/api", api_routes)
|
||||
// .merge(attachment::routes::router())
|
||||
.merge(
|
||||
SwaggerUi::new("/swagger-ui").url("/api-docs/openapi.json", openapi::ApiDoc::openapi()),
|
||||
)
|
||||
// Routes publiques (ou gérant leur propre auth)
|
||||
let api_routes = Router::new()
|
||||
.merge(secure_routes)
|
||||
.merge(auth::routes::router());
|
||||
|
||||
Router::new().nest("/api", api_routes).merge(
|
||||
SwaggerUi::new("/swagger-ui").url("/api-docs/openapi.json", openapi::ApiDoc::openapi()),
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user