mockforge_core/security/
privileged_access.rs

1//! Privileged Access Management
2//!
3//! This module provides comprehensive privileged access management including:
4//! - MFA enforcement
5//! - Access justification tracking
6//! - Privileged action monitoring
7//! - Session management
8//! - Automatic revocation
9
10use crate::security::{
11    justification_storage::{AccessJustification, JustificationStorage},
12    mfa_tracking::{MfaStatus, MfaStorage},
13};
14use crate::Error;
15use chrono::{DateTime, Duration, Utc};
16use serde::{Deserialize, Serialize};
17use std::collections::HashMap;
18use uuid::Uuid;
19
20/// Privileged role types
21#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
22#[serde(rename_all = "lowercase")]
23pub enum PrivilegedRole {
24    /// Admin role - full system access
25    Admin,
26    /// Owner role - organization ownership
27    Owner,
28    /// Service account - automated system access
29    ServiceAccount,
30}
31
32/// Privileged access request status
33#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
34#[serde(rename_all = "lowercase")]
35pub enum RequestStatus {
36    /// Request pending manager approval
37    PendingManager,
38    /// Request pending security review
39    PendingSecurity,
40    /// Request approved
41    Approved,
42    /// Request denied
43    Denied,
44    /// Request cancelled
45    Cancelled,
46}
47
48/// Privileged action type
49#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
50#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
51#[serde(rename_all = "snake_case")]
52pub enum PrivilegedActionType {
53    /// User management actions
54    UserCreate,
55    /// User deletion
56    UserDelete,
57    /// User modification
58    UserModify,
59    /// Role assignment
60    RoleAssign,
61    /// Role revocation
62    RoleRevoke,
63    /// Role escalation
64    RoleEscalate,
65    /// Permission grant
66    PermissionGrant,
67    /// Permission revocation
68    PermissionRevoke,
69    /// Configuration modification
70    ConfigModify,
71    /// Security policy change
72    SecurityPolicyChange,
73    /// Security setting change
74    SecuritySettingChange,
75    /// Audit log access
76    AuditLogAccess,
77    /// Data export
78    DataExport,
79    /// Data deletion
80    DataDelete,
81    /// Other privileged actions
82    Other,
83}
84
85/// Privileged access request
86#[derive(Debug, Clone, Serialize, Deserialize)]
87pub struct PrivilegedAccessRequest {
88    /// Request ID
89    pub request_id: Uuid,
90    /// User requesting access
91    pub user_id: Uuid,
92    /// Requested role
93    pub requested_role: PrivilegedRole,
94    /// Justification text
95    pub justification: String,
96    /// Business need description
97    pub business_need: Option<String>,
98    /// Manager who approved (if applicable)
99    pub manager_approval: Option<Uuid>,
100    /// Security team approval
101    pub security_approval: Option<Uuid>,
102    /// Request status
103    pub status: RequestStatus,
104    /// Request creation date
105    pub created_at: DateTime<Utc>,
106    /// Last update date
107    pub updated_at: DateTime<Utc>,
108    /// Access expiration date (if approved)
109    pub expires_at: Option<DateTime<Utc>>,
110}
111
112impl PrivilegedAccessRequest {
113    /// Create a new privileged access request
114    pub fn new(
115        user_id: Uuid,
116        requested_role: PrivilegedRole,
117        justification: String,
118        business_need: Option<String>,
119        manager_approval: Option<Uuid>,
120    ) -> Self {
121        let now = Utc::now();
122        Self {
123            request_id: Uuid::new_v4(),
124            user_id,
125            requested_role,
126            justification,
127            business_need,
128            manager_approval,
129            security_approval: None,
130            status: RequestStatus::PendingManager,
131            created_at: now,
132            updated_at: now,
133            expires_at: None,
134        }
135    }
136
137    /// Check if request is approved
138    pub fn is_approved(&self) -> bool {
139        self.status == RequestStatus::Approved
140    }
141
142    /// Check if request is expired
143    pub fn is_expired(&self) -> bool {
144        self.expires_at.map(|exp| Utc::now() > exp).unwrap_or(false)
145    }
146}
147
148/// Privileged action record
149#[derive(Debug, Clone, Serialize, Deserialize)]
150pub struct PrivilegedAction {
151    /// Action ID
152    pub action_id: Uuid,
153    /// User who performed the action
154    pub user_id: Uuid,
155    /// Action type
156    pub action_type: PrivilegedActionType,
157    /// Resource affected
158    pub resource: Option<String>,
159    /// Action details
160    pub details: Option<String>,
161    /// IP address
162    pub ip_address: Option<String>,
163    /// User agent
164    pub user_agent: Option<String>,
165    /// Session ID
166    pub session_id: Option<String>,
167    /// Timestamp
168    pub timestamp: DateTime<Utc>,
169}
170
171/// Privileged session information
172#[derive(Debug, Clone, Serialize, Deserialize)]
173pub struct PrivilegedSession {
174    /// Session ID
175    pub session_id: String,
176    /// User ID
177    pub user_id: Uuid,
178    /// Role
179    pub role: PrivilegedRole,
180    /// Session start time
181    pub started_at: DateTime<Utc>,
182    /// Last activity time
183    pub last_activity: DateTime<Utc>,
184    /// IP address
185    pub ip_address: Option<String>,
186    /// User agent
187    pub user_agent: Option<String>,
188    /// Whether session is active
189    pub is_active: bool,
190}
191
192/// Privileged access configuration
193#[derive(Debug, Clone, Serialize, Deserialize)]
194#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
195pub struct PrivilegedAccessConfig {
196    /// Require MFA for privileged users
197    pub require_mfa: bool,
198    /// MFA grace period in days
199    pub mfa_grace_period_days: u64,
200    /// Auto-suspend if MFA not enabled
201    pub auto_suspend_no_mfa: bool,
202    /// Session timeout in minutes
203    pub session_timeout_minutes: u64,
204    /// Max concurrent sessions
205    pub max_concurrent_sessions: u32,
206    /// Record sensitive actions
207    pub record_sensitive_actions: bool,
208    /// Monitor activity
209    pub monitor_activity: bool,
210    /// Sensitive action types that require alerting
211    pub sensitive_actions: Vec<PrivilegedActionType>,
212}
213
214impl Default for PrivilegedAccessConfig {
215    fn default() -> Self {
216        Self {
217            require_mfa: true,
218            mfa_grace_period_days: 7,
219            auto_suspend_no_mfa: true,
220            session_timeout_minutes: 30,
221            max_concurrent_sessions: 2,
222            record_sensitive_actions: true,
223            monitor_activity: true,
224            sensitive_actions: vec![
225                PrivilegedActionType::UserDelete,
226                PrivilegedActionType::RoleEscalate,
227                PrivilegedActionType::SecurityPolicyChange,
228                PrivilegedActionType::DataExport,
229                PrivilegedActionType::AuditLogAccess,
230            ],
231        }
232    }
233}
234
235/// Privileged access manager
236///
237/// Manages privileged access requests, monitoring, and enforcement
238pub struct PrivilegedAccessManager {
239    config: PrivilegedAccessConfig,
240    mfa_storage: Option<Arc<dyn MfaStorage>>,
241    justification_storage: Option<Arc<dyn JustificationStorage>>,
242    /// Active privileged sessions
243    sessions: Arc<RwLock<HashMap<String, PrivilegedSession>>>,
244    /// Privileged actions log
245    actions: Arc<RwLock<Vec<PrivilegedAction>>>,
246    /// Active access requests
247    requests: Arc<RwLock<HashMap<Uuid, PrivilegedAccessRequest>>>,
248}
249
250impl PrivilegedAccessManager {
251    /// Create a new privileged access manager
252    pub fn new(
253        config: PrivilegedAccessConfig,
254        mfa_storage: Option<Arc<dyn MfaStorage>>,
255        justification_storage: Option<Arc<dyn JustificationStorage>>,
256    ) -> Self {
257        Self {
258            config,
259            mfa_storage,
260            justification_storage,
261            sessions: Arc::new(RwLock::new(HashMap::new())),
262            actions: Arc::new(RwLock::new(Vec::new())),
263            requests: Arc::new(RwLock::new(HashMap::new())),
264        }
265    }
266
267    /// Request privileged access
268    pub async fn request_privileged_access(
269        &self,
270        user_id: Uuid,
271        requested_role: PrivilegedRole,
272        justification: String,
273        business_need: Option<String>,
274        manager_approval: Option<Uuid>,
275    ) -> Result<PrivilegedAccessRequest, Error> {
276        let request = PrivilegedAccessRequest::new(
277            user_id,
278            requested_role,
279            justification,
280            business_need,
281            manager_approval,
282        );
283
284        let mut requests = self.requests.write().await;
285        requests.insert(request.request_id, request.clone());
286
287        Ok(request)
288    }
289
290    /// Approve privileged access request (manager approval)
291    pub async fn approve_manager(&self, request_id: Uuid, approver_id: Uuid) -> Result<(), Error> {
292        let mut requests = self.requests.write().await;
293        let request = requests
294            .get_mut(&request_id)
295            .ok_or_else(|| Error::Generic("Request not found".to_string()))?;
296
297        if request.status != RequestStatus::PendingManager {
298            return Err(Error::Generic("Request is not pending manager approval".to_string()));
299        }
300
301        request.manager_approval = Some(approver_id);
302        request.status = RequestStatus::PendingSecurity;
303        request.updated_at = Utc::now();
304
305        Ok(())
306    }
307
308    /// Approve privileged access request (security approval)
309    pub async fn approve_security(
310        &self,
311        request_id: Uuid,
312        approver_id: Uuid,
313        expiration_days: u64,
314    ) -> Result<(), Error> {
315        let mut requests = self.requests.write().await;
316        let request = requests
317            .get_mut(&request_id)
318            .ok_or_else(|| Error::Generic("Request not found".to_string()))?;
319
320        if request.status != RequestStatus::PendingSecurity {
321            return Err(Error::Generic("Request is not pending security approval".to_string()));
322        }
323
324        request.security_approval = Some(approver_id);
325        request.status = RequestStatus::Approved;
326        request.expires_at = Some(Utc::now() + Duration::days(expiration_days as i64));
327        request.updated_at = Utc::now();
328
329        // Store justification
330        if let Some(ref just_storage) = self.justification_storage {
331            let justification = AccessJustification::new(
332                request.user_id,
333                request.justification.clone(),
334                request.business_need.clone(),
335                request.manager_approval,
336                request.expires_at,
337            );
338            just_storage.set_justification(justification).await?;
339        }
340
341        Ok(())
342    }
343
344    /// Deny privileged access request
345    pub async fn deny_request(&self, request_id: Uuid, reason: String) -> Result<(), Error> {
346        let mut requests = self.requests.write().await;
347        let request = requests
348            .get_mut(&request_id)
349            .ok_or_else(|| Error::Generic("Request not found".to_string()))?;
350
351        request.status = RequestStatus::Denied;
352        request.updated_at = Utc::now();
353
354        Ok(())
355    }
356
357    /// Check MFA compliance for a user
358    pub async fn check_mfa_compliance(&self, user_id: Uuid) -> Result<bool, Error> {
359        if !self.config.require_mfa {
360            return Ok(true);
361        }
362
363        if let Some(ref mfa_storage) = self.mfa_storage {
364            let mfa_status = mfa_storage.get_mfa_status(user_id).await?;
365            Ok(mfa_status.map(|s| s.enabled).unwrap_or(false))
366        } else {
367            // No MFA storage configured, assume compliant
368            Ok(true)
369        }
370    }
371
372    /// Record a privileged action
373    pub async fn record_action(
374        &self,
375        user_id: Uuid,
376        action_type: PrivilegedActionType,
377        resource: Option<String>,
378        details: Option<String>,
379        ip_address: Option<String>,
380        user_agent: Option<String>,
381        session_id: Option<String>,
382    ) -> Result<Uuid, Error> {
383        let action = PrivilegedAction {
384            action_id: Uuid::new_v4(),
385            user_id,
386            action_type,
387            resource,
388            details,
389            ip_address,
390            user_agent,
391            session_id,
392            timestamp: Utc::now(),
393        };
394
395        let mut actions = self.actions.write().await;
396        actions.push(action.clone());
397
398        // Check if this is a sensitive action that requires alerting
399        if self.config.sensitive_actions.contains(&action_type) {
400            // TODO: Emit security event for sensitive action
401        }
402
403        Ok(action.action_id)
404    }
405
406    /// Start a privileged session
407    pub async fn start_session(
408        &self,
409        session_id: String,
410        user_id: Uuid,
411        role: PrivilegedRole,
412        ip_address: Option<String>,
413        user_agent: Option<String>,
414    ) -> Result<(), Error> {
415        // Check MFA compliance
416        if !self.check_mfa_compliance(user_id).await? {
417            if self.config.auto_suspend_no_mfa {
418                return Err(Error::Generic("MFA not enabled for privileged user".to_string()));
419            }
420        }
421
422        // Check concurrent session limit
423        let sessions = self.sessions.read().await;
424        let active_sessions =
425            sessions.values().filter(|s| s.user_id == user_id && s.is_active).count();
426
427        if active_sessions >= self.config.max_concurrent_sessions as usize {
428            return Err(Error::Generic("Maximum concurrent sessions reached".to_string()));
429        }
430        drop(sessions);
431
432        let session = PrivilegedSession {
433            session_id: session_id.clone(),
434            user_id,
435            role,
436            started_at: Utc::now(),
437            last_activity: Utc::now(),
438            ip_address,
439            user_agent,
440            is_active: true,
441        };
442
443        let mut sessions = self.sessions.write().await;
444        sessions.insert(session_id, session);
445
446        Ok(())
447    }
448
449    /// Update session activity
450    pub async fn update_session_activity(&self, session_id: &str) -> Result<(), Error> {
451        let mut sessions = self.sessions.write().await;
452        if let Some(session) = sessions.get_mut(session_id) {
453            session.last_activity = Utc::now();
454        }
455        Ok(())
456    }
457
458    /// End a privileged session
459    pub async fn end_session(&self, session_id: &str) -> Result<(), Error> {
460        let mut sessions = self.sessions.write().await;
461        if let Some(session) = sessions.get_mut(session_id) {
462            session.is_active = false;
463        }
464        Ok(())
465    }
466
467    /// Check for expired sessions and clean them up
468    pub async fn cleanup_expired_sessions(&self) -> Result<Vec<String>, Error> {
469        let timeout = Duration::minutes(self.config.session_timeout_minutes as i64);
470        let now = Utc::now();
471        let mut expired = Vec::new();
472
473        let mut sessions = self.sessions.write().await;
474        for (session_id, session) in sessions.iter_mut() {
475            if session.is_active && (now - session.last_activity) > timeout {
476                session.is_active = false;
477                expired.push(session_id.clone());
478            }
479        }
480
481        Ok(expired)
482    }
483
484    /// Get all privileged actions for a user
485    pub async fn get_user_actions(&self, user_id: Uuid) -> Result<Vec<PrivilegedAction>, Error> {
486        let actions = self.actions.read().await;
487        Ok(actions.iter().filter(|a| a.user_id == user_id).cloned().collect())
488    }
489
490    /// Get all active privileged sessions
491    pub async fn get_active_sessions(&self) -> Result<Vec<PrivilegedSession>, Error> {
492        let sessions = self.sessions.read().await;
493        Ok(sessions.values().filter(|s| s.is_active).cloned().collect())
494    }
495
496    /// Get access request by ID
497    pub async fn get_request(
498        &self,
499        request_id: Uuid,
500    ) -> Result<Option<PrivilegedAccessRequest>, Error> {
501        let requests = self.requests.read().await;
502        Ok(requests.get(&request_id).cloned())
503    }
504
505    /// Get all requests for a user
506    pub async fn get_user_requests(
507        &self,
508        user_id: Uuid,
509    ) -> Result<Vec<PrivilegedAccessRequest>, Error> {
510        let requests = self.requests.read().await;
511        Ok(requests.values().filter(|r| r.user_id == user_id).cloned().collect())
512    }
513}
514
515// Required for Arc usage
516use std::sync::Arc;
517use tokio::sync::RwLock;
518
519#[cfg(test)]
520mod tests {
521    use super::*;
522
523    #[tokio::test]
524    async fn test_privileged_access_request() {
525        let manager = PrivilegedAccessManager::new(PrivilegedAccessConfig::default(), None, None);
526
527        let request = manager
528            .request_privileged_access(
529                Uuid::new_v4(),
530                PrivilegedRole::Admin,
531                "Required for system administration".to_string(),
532                Some("Manage production infrastructure".to_string()),
533                Some(Uuid::new_v4()),
534            )
535            .await
536            .unwrap();
537
538        assert_eq!(request.status, RequestStatus::PendingManager);
539        assert!(request.manager_approval.is_some());
540    }
541}