1use axum::{
7 extract::{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::handlers::auth_helpers::{extract_user_id_with_fallback, OptionalAuthClaims};
22
23#[derive(Clone)]
25pub struct PrivilegedAccessState {
26 pub manager: Arc<RwLock<PrivilegedAccessManager>>,
28}
29
30#[derive(Debug, Deserialize)]
32pub struct CreatePrivilegedAccessRequest {
33 pub requested_role: PrivilegedRole,
35 pub justification: String,
37 pub business_need: Option<String>,
39 pub manager_approval: Option<Uuid>,
41}
42
43#[derive(Debug, Deserialize)]
45pub struct ApproveRequest {
46 pub approved: bool,
48 pub justification: Option<String>,
50 pub expiration_days: Option<u64>,
52}
53
54#[derive(Debug, Serialize)]
56pub struct PrivilegedAccessRequestResponse {
57 pub request_id: Uuid,
59 pub status: RequestStatus,
61 pub created_at: chrono::DateTime<chrono::Utc>,
63 pub expires_at: Option<chrono::DateTime<chrono::Utc>>,
65}
66
67#[derive(Debug, Serialize)]
69pub struct PrivilegedActionsResponse {
70 pub actions: Vec<PrivilegedActionSummary>,
72}
73
74#[derive(Debug, Serialize)]
76pub struct PrivilegedActionSummary {
77 pub action_id: Uuid,
79 pub action_type: PrivilegedActionType,
81 pub resource: Option<String>,
83 pub timestamp: chrono::DateTime<chrono::Utc>,
85}
86
87pub async fn request_privileged_access(
91 State(state): State<PrivilegedAccessState>,
92 claims: OptionalAuthClaims,
93 Json(request): Json<CreatePrivilegedAccessRequest>,
94) -> Result<Json<PrivilegedAccessRequestResponse>, StatusCode> {
95 let user_id = extract_user_id_with_fallback(&claims);
97
98 let manager = state.manager.read().await;
99 let access_request = manager
100 .request_privileged_access(
101 user_id,
102 request.requested_role,
103 request.justification,
104 request.business_need,
105 request.manager_approval,
106 )
107 .await
108 .map_err(|e| {
109 error!("Failed to create privileged access request: {}", e);
110 StatusCode::INTERNAL_SERVER_ERROR
111 })?;
112
113 info!("Privileged access request created: {}", access_request.request_id);
114
115 let event = SecurityEvent::new(SecurityEventType::AuthzPrivilegeEscalation, None, None)
117 .with_actor(EventActor {
118 user_id: Some(user_id.to_string()),
119 username: None,
120 ip_address: None,
121 user_agent: None,
122 })
123 .with_target(EventTarget {
124 resource_type: Some("privileged_access_request".to_string()),
125 resource_id: Some(access_request.request_id.to_string()),
126 method: None,
127 })
128 .with_outcome(EventOutcome {
129 success: true,
130 reason: Some("Privileged access request created".to_string()),
131 });
132 emit_security_event(event).await;
133
134 Ok(Json(PrivilegedAccessRequestResponse {
135 request_id: access_request.request_id,
136 status: access_request.status,
137 created_at: access_request.created_at,
138 expires_at: access_request.expires_at,
139 }))
140}
141
142pub async fn approve_manager(
146 State(state): State<PrivilegedAccessState>,
147 Path(request_id): Path<Uuid>,
148 claims: OptionalAuthClaims,
149) -> Result<Json<serde_json::Value>, StatusCode> {
150 let approver_id = extract_user_id_with_fallback(&claims);
152
153 let manager = state.manager.write().await;
154 manager.approve_manager(request_id, approver_id).await.map_err(|e| {
155 error!("Failed to approve privileged access request: {}", e);
156 StatusCode::BAD_REQUEST
157 })?;
158
159 info!("Privileged access request approved by manager: {}", request_id);
160
161 let event = SecurityEvent::new(SecurityEventType::AuthzPrivilegeEscalation, None, None)
163 .with_actor(EventActor {
164 user_id: Some(approver_id.to_string()),
165 username: None,
166 ip_address: None,
167 user_agent: None,
168 })
169 .with_target(EventTarget {
170 resource_type: Some("privileged_access_request".to_string()),
171 resource_id: Some(request_id.to_string()),
172 method: None,
173 })
174 .with_outcome(EventOutcome {
175 success: true,
176 reason: Some("Manager approval granted".to_string()),
177 });
178 emit_security_event(event).await;
179
180 Ok(Json(serde_json::json!({
181 "status": "approved",
182 "request_id": request_id
183 })))
184}
185
186pub async fn approve_security(
190 State(state): State<PrivilegedAccessState>,
191 Path(request_id): Path<Uuid>,
192 claims: OptionalAuthClaims,
193 Json(request): Json<ApproveRequest>,
194) -> Result<Json<serde_json::Value>, StatusCode> {
195 if !request.approved {
196 return Err(StatusCode::BAD_REQUEST);
197 }
198
199 let approver_id = extract_user_id_with_fallback(&claims);
201
202 let expiration_days = request.expiration_days.unwrap_or(365);
203
204 let manager = state.manager.write().await;
205 manager
206 .approve_security(request_id, approver_id, expiration_days)
207 .await
208 .map_err(|e| {
209 error!("Failed to approve privileged access request: {}", e);
210 StatusCode::BAD_REQUEST
211 })?;
212
213 info!("Privileged access request approved by security: {}", request_id);
214
215 let event = SecurityEvent::new(SecurityEventType::AuthzPrivilegeEscalation, None, None)
217 .with_actor(EventActor {
218 user_id: Some(approver_id.to_string()),
219 username: None,
220 ip_address: None,
221 user_agent: None,
222 })
223 .with_target(EventTarget {
224 resource_type: Some("privileged_access_request".to_string()),
225 resource_id: Some(request_id.to_string()),
226 method: None,
227 })
228 .with_outcome(EventOutcome {
229 success: true,
230 reason: Some("Security approval granted".to_string()),
231 });
232 emit_security_event(event).await;
233
234 Ok(Json(serde_json::json!({
235 "status": "approved",
236 "request_id": request_id
237 })))
238}
239
240pub async fn get_user_actions(
244 State(state): State<PrivilegedAccessState>,
245 Path(user_id): Path<Uuid>,
246) -> Result<Json<PrivilegedActionsResponse>, StatusCode> {
247 let manager = state.manager.read().await;
248 let actions = manager.get_user_actions(user_id).await.map_err(|e| {
249 error!("Failed to get user actions: {}", e);
250 StatusCode::INTERNAL_SERVER_ERROR
251 })?;
252
253 let summaries: Vec<PrivilegedActionSummary> = actions
254 .into_iter()
255 .map(|a| PrivilegedActionSummary {
256 action_id: a.action_id,
257 action_type: a.action_type,
258 resource: a.resource,
259 timestamp: a.timestamp,
260 })
261 .collect();
262
263 Ok(Json(PrivilegedActionsResponse { actions: summaries }))
264}
265
266pub async fn get_active_sessions(
270 State(state): State<PrivilegedAccessState>,
271) -> Result<Json<serde_json::Value>, StatusCode> {
272 let manager = state.manager.read().await;
273 let sessions = manager.get_active_sessions().await.map_err(|e| {
274 error!("Failed to get active sessions: {}", e);
275 StatusCode::INTERNAL_SERVER_ERROR
276 })?;
277
278 Ok(Json(serde_json::json!({
279 "sessions": sessions
280 })))
281}
282
283pub fn privileged_access_router(state: PrivilegedAccessState) -> axum::Router {
285 use axum::routing::{get, post};
286
287 axum::Router::new()
288 .route("/request", post(request_privileged_access))
289 .route("/{request_id}/approve-manager", post(approve_manager))
290 .route("/{request_id}/approve-security", post(approve_security))
291 .route("/actions/{user_id}", get(get_user_actions))
292 .route("/sessions", get(get_active_sessions))
293 .with_state(state)
294}