mockforge_http/middleware/
security.rs1use axum::body::Body;
7use axum::http::{Request, Response, StatusCode};
8use axum::middleware::Next;
9use mockforge_core::security::{
10 emit_security_event, EventActor, EventOutcome, EventTarget, SecurityEvent, SecurityEventType,
11};
12use tracing::debug;
13
14pub async fn security_middleware(req: Request<Body>, next: Next) -> Response<Body> {
21 let path = req.uri().path().to_string();
22 let method = req.method().clone();
23
24 let ip_address = req
26 .headers()
27 .get("x-forwarded-for")
28 .or_else(|| req.headers().get("x-real-ip"))
29 .and_then(|h| h.to_str().ok())
30 .map(|s| s.to_string())
31 .or_else(|| {
32 req.extensions()
33 .get::<axum::extract::ConnectInfo<std::net::SocketAddr>>()
34 .map(|addr| addr.ip().to_string())
35 });
36
37 let user_agent = req
38 .headers()
39 .get("user-agent")
40 .and_then(|h| h.to_str().ok())
41 .map(|s| s.to_string());
42
43 let user_id: Option<String> = req
46 .extensions()
47 .get::<axum::extract::Extension<crate::auth::types::AuthClaims>>()
48 .and_then(|claims| claims.sub.clone());
49
50 let response = next.run(req).await;
52 let status = response.status();
53
54 let is_success = status.is_success();
56 let is_client_error = status.is_client_error();
57 let is_server_error = status.is_server_error();
58
59 if is_success {
61 let event = SecurityEvent::new(SecurityEventType::AuthzAccessGranted, None, None)
63 .with_actor(EventActor {
64 user_id: user_id.clone(),
65 username: user_id.clone(),
66 ip_address: ip_address.clone(),
67 user_agent: user_agent.clone(),
68 })
69 .with_target(EventTarget {
70 resource_type: Some("api".to_string()),
71 resource_id: Some(path.clone()),
72 method: Some(method.to_string()),
73 })
74 .with_outcome(EventOutcome {
75 success: true,
76 reason: None,
77 })
78 .with_metadata("status_code".to_string(), serde_json::json!(status.as_u16()));
79 emit_security_event(event).await;
80 } else if is_client_error && status == StatusCode::FORBIDDEN {
81 let event = SecurityEvent::new(SecurityEventType::AuthzAccessDenied, None, None)
83 .with_actor(EventActor {
84 user_id: user_id.clone(),
85 username: user_id.clone(),
86 ip_address: ip_address.clone(),
87 user_agent: user_agent.clone(),
88 })
89 .with_target(EventTarget {
90 resource_type: Some("api".to_string()),
91 resource_id: Some(path.clone()),
92 method: Some(method.to_string()),
93 })
94 .with_outcome(EventOutcome {
95 success: false,
96 reason: Some(format!("Access denied: {}", status)),
97 })
98 .with_metadata("status_code".to_string(), serde_json::json!(status.as_u16()));
99 emit_security_event(event).await;
100 } else if is_server_error {
101 debug!("Server error detected: {} for {}", status, path);
103 }
106
107 response
108}