1use axum::{
11 extract::{Path, State},
12 http::StatusCode,
13 response::Json,
14};
15use mockforge_core::security::{
16 emit_security_event, EventActor, EventOutcome, EventTarget, PrivilegedAccessManager,
17 PrivilegedActionType, PrivilegedRole, RequestStatus, SecurityEvent, SecurityEventType,
18};
19use serde::{Deserialize, Serialize};
20use std::sync::Arc;
21use tokio::sync::RwLock;
22use tracing::{error, info};
23use uuid::Uuid;
24
25use crate::handlers::auth_helpers::{extract_user_id_with_fallback, OptionalAuthClaims};
26
27#[derive(Clone)]
29pub struct PrivilegedAccessState {
30 pub manager: Arc<RwLock<PrivilegedAccessManager>>,
32}
33
34#[derive(Debug, Deserialize)]
36pub struct CreatePrivilegedAccessRequest {
37 pub requested_role: PrivilegedRole,
39 pub justification: String,
41 pub business_need: Option<String>,
43 pub manager_approval: Option<Uuid>,
45}
46
47#[derive(Debug, Deserialize)]
49pub struct ApproveRequest {
50 pub approved: bool,
52 pub justification: Option<String>,
54 pub expiration_days: Option<u64>,
56}
57
58#[derive(Debug, Serialize)]
60pub struct PrivilegedAccessRequestResponse {
61 pub request_id: Uuid,
63 pub status: RequestStatus,
65 pub created_at: chrono::DateTime<chrono::Utc>,
67 pub expires_at: Option<chrono::DateTime<chrono::Utc>>,
69}
70
71#[derive(Debug, Serialize)]
73pub struct PrivilegedActionsResponse {
74 pub actions: Vec<PrivilegedActionSummary>,
76}
77
78#[derive(Debug, Serialize)]
80pub struct PrivilegedActionSummary {
81 pub action_id: Uuid,
83 pub action_type: PrivilegedActionType,
85 pub resource: Option<String>,
87 pub timestamp: chrono::DateTime<chrono::Utc>,
89}
90
91pub async fn request_privileged_access(
95 State(state): State<PrivilegedAccessState>,
96 claims: OptionalAuthClaims,
97 Json(request): Json<CreatePrivilegedAccessRequest>,
98) -> Result<Json<PrivilegedAccessRequestResponse>, StatusCode> {
99 let user_id = extract_user_id_with_fallback(&claims);
101
102 let manager = state.manager.read().await;
103 let access_request = manager
104 .request_privileged_access(
105 user_id,
106 request.requested_role,
107 request.justification,
108 request.business_need,
109 request.manager_approval,
110 )
111 .await
112 .map_err(|e| {
113 error!("Failed to create privileged access request: {}", e);
114 StatusCode::INTERNAL_SERVER_ERROR
115 })?;
116
117 info!("Privileged access request created: {}", access_request.request_id);
118
119 let event = SecurityEvent::new(SecurityEventType::AuthzPrivilegeEscalation, None, None)
121 .with_actor(EventActor {
122 user_id: Some(user_id.to_string()),
123 username: None,
124 ip_address: None,
125 user_agent: None,
126 })
127 .with_target(EventTarget {
128 resource_type: Some("privileged_access_request".to_string()),
129 resource_id: Some(access_request.request_id.to_string()),
130 method: None,
131 })
132 .with_outcome(EventOutcome {
133 success: true,
134 reason: Some("Privileged access request created".to_string()),
135 });
136 emit_security_event(event).await;
137
138 Ok(Json(PrivilegedAccessRequestResponse {
139 request_id: access_request.request_id,
140 status: access_request.status,
141 created_at: access_request.created_at,
142 expires_at: access_request.expires_at,
143 }))
144}
145
146pub async fn approve_manager(
150 State(state): State<PrivilegedAccessState>,
151 Path(request_id): Path<Uuid>,
152 claims: OptionalAuthClaims,
153) -> Result<Json<serde_json::Value>, StatusCode> {
154 let approver_id = extract_user_id_with_fallback(&claims);
156
157 let manager = state.manager.write().await;
158 manager.approve_manager(request_id, approver_id).await.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 claims: OptionalAuthClaims,
197 Json(request): Json<ApproveRequest>,
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.get_user_actions(user_id).await.map_err(|e| {
253 error!("Failed to get user actions: {}", e);
254 StatusCode::INTERNAL_SERVER_ERROR
255 })?;
256
257 let summaries: Vec<PrivilegedActionSummary> = actions
258 .into_iter()
259 .map(|a| PrivilegedActionSummary {
260 action_id: a.action_id,
261 action_type: a.action_type,
262 resource: a.resource,
263 timestamp: a.timestamp,
264 })
265 .collect();
266
267 Ok(Json(PrivilegedActionsResponse { actions: summaries }))
268}
269
270pub async fn get_active_sessions(
274 State(state): State<PrivilegedAccessState>,
275) -> Result<Json<serde_json::Value>, StatusCode> {
276 let manager = state.manager.read().await;
277 let sessions = manager.get_active_sessions().await.map_err(|e| {
278 error!("Failed to get active sessions: {}", e);
279 StatusCode::INTERNAL_SERVER_ERROR
280 })?;
281
282 Ok(Json(serde_json::json!({
283 "sessions": sessions
284 })))
285}
286
287pub fn privileged_access_router(state: PrivilegedAccessState) -> axum::Router {
289 use axum::routing::{get, post};
290
291 axum::Router::new()
292 .route("/request", post(request_privileged_access))
293 .route("/{request_id}/approve-manager", post(approve_manager))
294 .route("/{request_id}/approve-security", post(approve_security))
295 .route("/actions/{user_id}", get(get_user_actions))
296 .route("/sessions", get(get_active_sessions))
297 .with_state(state)
298}