1use axum::{
7 extract::{Extension, Path, State},
8 http::StatusCode,
9 response::Json,
10};
11use mockforge_core::security::{
12 emit_security_event, EventActor, EventOutcome, EventTarget, PrivilegedAccessManager,
13 PrivilegedActionType, PrivilegedRole, RequestStatus, SecurityEvent, SecurityEventType,
14};
15use serde::{Deserialize, Serialize};
16use std::sync::Arc;
17use tokio::sync::RwLock;
18use tracing::{error, info};
19use uuid::Uuid;
20
21use crate::auth::types::AuthClaims;
22use crate::handlers::auth_helpers::extract_user_id_with_fallback;
23
24#[derive(Clone)]
26pub struct PrivilegedAccessState {
27 pub manager: Arc<RwLock<PrivilegedAccessManager>>,
29}
30
31#[derive(Debug, Deserialize)]
33pub struct CreatePrivilegedAccessRequest {
34 pub requested_role: PrivilegedRole,
36 pub justification: String,
38 pub business_need: Option<String>,
40 pub manager_approval: Option<Uuid>,
42}
43
44#[derive(Debug, Deserialize)]
46pub struct ApproveRequest {
47 pub approved: bool,
49 pub justification: Option<String>,
51 pub expiration_days: Option<u64>,
53}
54
55#[derive(Debug, Serialize)]
57pub struct PrivilegedAccessRequestResponse {
58 pub request_id: Uuid,
60 pub status: RequestStatus,
62 pub created_at: chrono::DateTime<chrono::Utc>,
64 pub expires_at: Option<chrono::DateTime<chrono::Utc>>,
66}
67
68#[derive(Debug, Serialize)]
70pub struct PrivilegedActionsResponse {
71 pub actions: Vec<PrivilegedActionSummary>,
73}
74
75#[derive(Debug, Serialize)]
77pub struct PrivilegedActionSummary {
78 pub action_id: Uuid,
80 pub action_type: PrivilegedActionType,
82 pub resource: Option<String>,
84 pub timestamp: chrono::DateTime<chrono::Utc>,
86}
87
88pub async fn request_privileged_access(
92 State(state): State<PrivilegedAccessState>,
93 Json(request): Json<CreatePrivilegedAccessRequest>,
94 claims: Option<Extension<AuthClaims>>,
95) -> Result<Json<PrivilegedAccessRequestResponse>, StatusCode> {
96 let user_id = extract_user_id_with_fallback(claims);
98
99 let manager = state.manager.read().await;
100 let access_request = manager
101 .request_privileged_access(
102 user_id,
103 request.requested_role,
104 request.justification,
105 request.business_need,
106 request.manager_approval,
107 )
108 .await
109 .map_err(|e| {
110 error!("Failed to create privileged access request: {}", e);
111 StatusCode::INTERNAL_SERVER_ERROR
112 })?;
113
114 info!("Privileged access request created: {}", access_request.request_id);
115
116 let event = SecurityEvent::new(SecurityEventType::AuthzPrivilegeEscalation, None, None)
118 .with_actor(EventActor {
119 user_id: Some(user_id.to_string()),
120 username: None,
121 ip_address: None,
122 user_agent: None,
123 })
124 .with_target(EventTarget {
125 resource_type: Some("privileged_access_request".to_string()),
126 resource_id: Some(access_request.request_id.to_string()),
127 method: None,
128 })
129 .with_outcome(EventOutcome {
130 success: true,
131 reason: Some("Privileged access request created".to_string()),
132 });
133 emit_security_event(event).await;
134
135 Ok(Json(PrivilegedAccessRequestResponse {
136 request_id: access_request.request_id,
137 status: access_request.status,
138 created_at: access_request.created_at,
139 expires_at: access_request.expires_at,
140 }))
141}
142
143pub async fn approve_manager(
147 State(state): State<PrivilegedAccessState>,
148 Path(request_id): Path<Uuid>,
149 claims: Option<Extension<AuthClaims>>,
150) -> Result<Json<serde_json::Value>, StatusCode> {
151 let approver_id = extract_user_id_with_fallback(claims);
153
154 let manager = state.manager.write().await;
155 manager
156 .approve_manager(request_id, approver_id)
157 .await
158 .map_err(|e| {
159 error!("Failed to approve privileged access request: {}", e);
160 StatusCode::BAD_REQUEST
161 })?;
162
163 info!("Privileged access request approved by manager: {}", request_id);
164
165 let event = SecurityEvent::new(SecurityEventType::AuthzPrivilegeEscalation, None, None)
167 .with_actor(EventActor {
168 user_id: Some(approver_id.to_string()),
169 username: None,
170 ip_address: None,
171 user_agent: None,
172 })
173 .with_target(EventTarget {
174 resource_type: Some("privileged_access_request".to_string()),
175 resource_id: Some(request_id.to_string()),
176 method: None,
177 })
178 .with_outcome(EventOutcome {
179 success: true,
180 reason: Some("Manager approval granted".to_string()),
181 });
182 emit_security_event(event).await;
183
184 Ok(Json(serde_json::json!({
185 "status": "approved",
186 "request_id": request_id
187 })))
188}
189
190pub async fn approve_security(
194 State(state): State<PrivilegedAccessState>,
195 Path(request_id): Path<Uuid>,
196 Json(request): Json<ApproveRequest>,
197 claims: Option<Extension<AuthClaims>>,
198) -> Result<Json<serde_json::Value>, StatusCode> {
199 if !request.approved {
200 return Err(StatusCode::BAD_REQUEST);
201 }
202
203 let approver_id = extract_user_id_with_fallback(claims);
205
206 let expiration_days = request.expiration_days.unwrap_or(365);
207
208 let manager = state.manager.write().await;
209 manager
210 .approve_security(request_id, approver_id, expiration_days)
211 .await
212 .map_err(|e| {
213 error!("Failed to approve privileged access request: {}", e);
214 StatusCode::BAD_REQUEST
215 })?;
216
217 info!("Privileged access request approved by security: {}", request_id);
218
219 let event = SecurityEvent::new(SecurityEventType::AuthzPrivilegeEscalation, None, None)
221 .with_actor(EventActor {
222 user_id: Some(approver_id.to_string()),
223 username: None,
224 ip_address: None,
225 user_agent: None,
226 })
227 .with_target(EventTarget {
228 resource_type: Some("privileged_access_request".to_string()),
229 resource_id: Some(request_id.to_string()),
230 method: None,
231 })
232 .with_outcome(EventOutcome {
233 success: true,
234 reason: Some("Security approval granted".to_string()),
235 });
236 emit_security_event(event).await;
237
238 Ok(Json(serde_json::json!({
239 "status": "approved",
240 "request_id": request_id
241 })))
242}
243
244pub async fn get_user_actions(
248 State(state): State<PrivilegedAccessState>,
249 Path(user_id): Path<Uuid>,
250) -> Result<Json<PrivilegedActionsResponse>, StatusCode> {
251 let manager = state.manager.read().await;
252 let actions = manager
253 .get_user_actions(user_id)
254 .await
255 .map_err(|e| {
256 error!("Failed to get user actions: {}", e);
257 StatusCode::INTERNAL_SERVER_ERROR
258 })?;
259
260 let summaries: Vec<PrivilegedActionSummary> = actions
261 .into_iter()
262 .map(|a| PrivilegedActionSummary {
263 action_id: a.action_id,
264 action_type: a.action_type,
265 resource: a.resource,
266 timestamp: a.timestamp,
267 })
268 .collect();
269
270 Ok(Json(PrivilegedActionsResponse {
271 actions: summaries,
272 }))
273}
274
275pub async fn get_active_sessions(
279 State(state): State<PrivilegedAccessState>,
280) -> Result<Json<serde_json::Value>, StatusCode> {
281 let manager = state.manager.read().await;
282 let sessions = manager
283 .get_active_sessions()
284 .await
285 .map_err(|e| {
286 error!("Failed to get active sessions: {}", e);
287 StatusCode::INTERNAL_SERVER_ERROR
288 })?;
289
290 Ok(Json(serde_json::json!({
291 "sessions": sessions
292 })))
293}
294
295pub fn privileged_access_router(state: PrivilegedAccessState) -> axum::Router {
297 use axum::routing::{get, post};
298
299 axum::Router::new()
300 .route("/request", post(request_privileged_access))
301 .route("/{request_id}/approve-manager", post(approve_manager))
302 .route("/{request_id}/approve-security", post(approve_security))
303 .route("/actions/{user_id}", get(get_user_actions))
304 .route("/sessions", get(get_active_sessions))
305 .with_state(state)
306}