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> = None; let response = next.run(req).await;
49 let status = response.status();
50
51 let is_success = status.is_success();
53 let is_client_error = status.is_client_error();
54 let is_server_error = status.is_server_error();
55
56 if is_success {
58 let event = SecurityEvent::new(SecurityEventType::AuthzAccessGranted, None, None)
60 .with_actor(EventActor {
61 user_id: user_id.clone(),
62 username: user_id.clone(),
63 ip_address: ip_address.clone(),
64 user_agent: user_agent.clone(),
65 })
66 .with_target(EventTarget {
67 resource_type: Some("api".to_string()),
68 resource_id: Some(path.clone()),
69 method: Some(method.to_string()),
70 })
71 .with_outcome(EventOutcome {
72 success: true,
73 reason: None,
74 })
75 .with_metadata("status_code".to_string(), serde_json::json!(status.as_u16()));
76 emit_security_event(event).await;
77 } else if is_client_error && status == StatusCode::FORBIDDEN {
78 let event = SecurityEvent::new(SecurityEventType::AuthzAccessDenied, None, None)
80 .with_actor(EventActor {
81 user_id: user_id.clone(),
82 username: user_id.clone(),
83 ip_address: ip_address.clone(),
84 user_agent: user_agent.clone(),
85 })
86 .with_target(EventTarget {
87 resource_type: Some("api".to_string()),
88 resource_id: Some(path.clone()),
89 method: Some(method.to_string()),
90 })
91 .with_outcome(EventOutcome {
92 success: false,
93 reason: Some(format!("Access denied: {}", status)),
94 })
95 .with_metadata("status_code".to_string(), serde_json::json!(status.as_u16()));
96 emit_security_event(event).await;
97 } else if is_server_error {
98 debug!("Server error detected: {} for {}", status, path);
100 }
103
104 response
105}