mockforge_http/handlers/
privileged_access.rs

1//! HTTP handlers for privileged access management
2//!
3//! This module provides REST API endpoints for managing privileged access requests,
4//! monitoring privileged actions, and managing privileged sessions.
5
6use 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/// State for privileged access handlers
25#[derive(Clone)]
26pub struct PrivilegedAccessState {
27    /// Privileged access manager
28    pub manager: Arc<RwLock<PrivilegedAccessManager>>,
29}
30
31/// Request to create a privileged access request
32#[derive(Debug, Deserialize)]
33pub struct CreatePrivilegedAccessRequest {
34    /// Requested role
35    pub requested_role: PrivilegedRole,
36    /// Justification
37    pub justification: String,
38    /// Business need
39    pub business_need: Option<String>,
40    /// Manager approval (optional)
41    pub manager_approval: Option<Uuid>,
42}
43
44/// Request to approve a privileged access request
45#[derive(Debug, Deserialize)]
46pub struct ApproveRequest {
47    /// Whether to approve
48    pub approved: bool,
49    /// Justification for approval/denial
50    pub justification: Option<String>,
51    /// Expiration days (if approved)
52    pub expiration_days: Option<u64>,
53}
54
55/// Response for privileged access request
56#[derive(Debug, Serialize)]
57pub struct PrivilegedAccessRequestResponse {
58    /// Request ID
59    pub request_id: Uuid,
60    /// Status
61    pub status: RequestStatus,
62    /// Created at
63    pub created_at: chrono::DateTime<chrono::Utc>,
64    /// Expires at
65    pub expires_at: Option<chrono::DateTime<chrono::Utc>>,
66}
67
68/// Response for privileged actions list
69#[derive(Debug, Serialize)]
70pub struct PrivilegedActionsResponse {
71    /// Actions
72    pub actions: Vec<PrivilegedActionSummary>,
73}
74
75/// Summary of a privileged action
76#[derive(Debug, Serialize)]
77pub struct PrivilegedActionSummary {
78    /// Action ID
79    pub action_id: Uuid,
80    /// Action type
81    pub action_type: PrivilegedActionType,
82    /// Resource
83    pub resource: Option<String>,
84    /// Timestamp
85    pub timestamp: chrono::DateTime<chrono::Utc>,
86}
87
88/// Request privileged access
89///
90/// POST /api/v1/security/privileged-access/request
91pub 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    // Extract user ID from authentication claims, or use default for mock server
97    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    // Emit security event
117    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
143/// Approve privileged access request (manager)
144///
145/// POST /api/v1/security/privileged-access/{request_id}/approve-manager
146pub 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    // Extract approver ID from authentication claims, or use default for mock server
152    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    // Emit security event
166    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
190/// Approve privileged access request (security)
191///
192/// POST /api/v1/security/privileged-access/{request_id}/approve-security
193pub 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    // Extract approver ID from authentication claims, or use default for mock server
204    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    // Emit security event
220    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
244/// Get privileged actions for a user
245///
246/// GET /api/v1/security/privileged-access/actions/{user_id}
247pub 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
275/// Get active privileged sessions
276///
277/// GET /api/v1/security/privileged-access/sessions
278pub 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
295/// Create privileged access router
296pub 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}