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::{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/// State for privileged access handlers
24#[derive(Clone)]
25pub struct PrivilegedAccessState {
26    /// Privileged access manager
27    pub manager: Arc<RwLock<PrivilegedAccessManager>>,
28}
29
30/// Request to create a privileged access request
31#[derive(Debug, Deserialize)]
32pub struct CreatePrivilegedAccessRequest {
33    /// Requested role
34    pub requested_role: PrivilegedRole,
35    /// Justification
36    pub justification: String,
37    /// Business need
38    pub business_need: Option<String>,
39    /// Manager approval (optional)
40    pub manager_approval: Option<Uuid>,
41}
42
43/// Request to approve a privileged access request
44#[derive(Debug, Deserialize)]
45pub struct ApproveRequest {
46    /// Whether to approve
47    pub approved: bool,
48    /// Justification for approval/denial
49    pub justification: Option<String>,
50    /// Expiration days (if approved)
51    pub expiration_days: Option<u64>,
52}
53
54/// Response for privileged access request
55#[derive(Debug, Serialize)]
56pub struct PrivilegedAccessRequestResponse {
57    /// Request ID
58    pub request_id: Uuid,
59    /// Status
60    pub status: RequestStatus,
61    /// Created at
62    pub created_at: chrono::DateTime<chrono::Utc>,
63    /// Expires at
64    pub expires_at: Option<chrono::DateTime<chrono::Utc>>,
65}
66
67/// Response for privileged actions list
68#[derive(Debug, Serialize)]
69pub struct PrivilegedActionsResponse {
70    /// Actions
71    pub actions: Vec<PrivilegedActionSummary>,
72}
73
74/// Summary of a privileged action
75#[derive(Debug, Serialize)]
76pub struct PrivilegedActionSummary {
77    /// Action ID
78    pub action_id: Uuid,
79    /// Action type
80    pub action_type: PrivilegedActionType,
81    /// Resource
82    pub resource: Option<String>,
83    /// Timestamp
84    pub timestamp: chrono::DateTime<chrono::Utc>,
85}
86
87/// Request privileged access
88///
89/// POST /api/v1/security/privileged-access/request
90pub async fn request_privileged_access(
91    State(state): State<PrivilegedAccessState>,
92    claims: OptionalAuthClaims,
93    Json(request): Json<CreatePrivilegedAccessRequest>,
94) -> Result<Json<PrivilegedAccessRequestResponse>, StatusCode> {
95    // Extract user ID from authentication claims, or use default for mock server
96    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    // Emit security event
116    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
142/// Approve privileged access request (manager)
143///
144/// POST /api/v1/security/privileged-access/{request_id}/approve-manager
145pub 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    // Extract approver ID from authentication claims, or use default for mock server
151    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    // Emit security event
162    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
186/// Approve privileged access request (security)
187///
188/// POST /api/v1/security/privileged-access/{request_id}/approve-security
189pub 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    // Extract approver ID from authentication claims, or use default for mock server
200    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    // Emit security event
216    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
240/// Get privileged actions for a user
241///
242/// GET /api/v1/security/privileged-access/actions/{user_id}
243pub 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
266/// Get active privileged sessions
267///
268/// GET /api/v1/security/privileged-access/sessions
269pub 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
283/// Create privileged access router
284pub 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}