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    emit_security_event_async,
12    events::{EventActor, EventOutcome, EventTarget, SecurityEvent, SecurityEventType},
13    justification_storage::{AccessJustification, JustificationStorage},
14    mfa_tracking::MfaStorage,
15};
16use crate::Error;
17use chrono::{DateTime, Duration, Utc};
18use serde::{Deserialize, Serialize};
19use serde_json;
20use std::collections::HashMap;
21use uuid::Uuid;
22
23/// Privileged role types
24#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
25#[serde(rename_all = "lowercase")]
26pub enum PrivilegedRole {
27    /// Admin role - full system access
28    Admin,
29    /// Owner role - organization ownership
30    Owner,
31    /// Service account - automated system access
32    ServiceAccount,
33}
34
35/// Privileged access request status
36#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
37#[serde(rename_all = "lowercase")]
38pub enum RequestStatus {
39    /// Request pending manager approval
40    PendingManager,
41    /// Request pending security review
42    PendingSecurity,
43    /// Request approved
44    Approved,
45    /// Request denied
46    Denied,
47    /// Request cancelled
48    Cancelled,
49}
50
51/// Privileged action type
52#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
53#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
54#[serde(rename_all = "snake_case")]
55pub enum PrivilegedActionType {
56    /// User management actions
57    UserCreate,
58    /// User deletion
59    UserDelete,
60    /// User modification
61    UserModify,
62    /// Role assignment
63    RoleAssign,
64    /// Role revocation
65    RoleRevoke,
66    /// Role escalation
67    RoleEscalate,
68    /// Permission grant
69    PermissionGrant,
70    /// Permission revocation
71    PermissionRevoke,
72    /// Configuration modification
73    ConfigModify,
74    /// Security policy change
75    SecurityPolicyChange,
76    /// Security setting change
77    SecuritySettingChange,
78    /// Audit log access
79    AuditLogAccess,
80    /// Data export
81    DataExport,
82    /// Data deletion
83    DataDelete,
84    /// Other privileged actions
85    Other,
86}
87
88/// Privileged access request
89#[derive(Debug, Clone, Serialize, Deserialize)]
90pub struct PrivilegedAccessRequest {
91    /// Request ID
92    pub request_id: Uuid,
93    /// User requesting access
94    pub user_id: Uuid,
95    /// Requested role
96    pub requested_role: PrivilegedRole,
97    /// Justification text
98    pub justification: String,
99    /// Business need description
100    pub business_need: Option<String>,
101    /// Manager who approved (if applicable)
102    pub manager_approval: Option<Uuid>,
103    /// Security team approval
104    pub security_approval: Option<Uuid>,
105    /// Request status
106    pub status: RequestStatus,
107    /// Request creation date
108    pub created_at: DateTime<Utc>,
109    /// Last update date
110    pub updated_at: DateTime<Utc>,
111    /// Access expiration date (if approved)
112    pub expires_at: Option<DateTime<Utc>>,
113}
114
115impl PrivilegedAccessRequest {
116    /// Create a new privileged access request
117    pub fn new(
118        user_id: Uuid,
119        requested_role: PrivilegedRole,
120        justification: String,
121        business_need: Option<String>,
122        manager_approval: Option<Uuid>,
123    ) -> Self {
124        let now = Utc::now();
125        Self {
126            request_id: Uuid::new_v4(),
127            user_id,
128            requested_role,
129            justification,
130            business_need,
131            manager_approval,
132            security_approval: None,
133            status: RequestStatus::PendingManager,
134            created_at: now,
135            updated_at: now,
136            expires_at: None,
137        }
138    }
139
140    /// Check if request is approved
141    pub fn is_approved(&self) -> bool {
142        self.status == RequestStatus::Approved
143    }
144
145    /// Check if request is expired
146    pub fn is_expired(&self) -> bool {
147        self.expires_at.map(|exp| Utc::now() > exp).unwrap_or(false)
148    }
149}
150
151/// Privileged action record
152#[derive(Debug, Clone, Serialize, Deserialize)]
153pub struct PrivilegedAction {
154    /// Action ID
155    pub action_id: Uuid,
156    /// User who performed the action
157    pub user_id: Uuid,
158    /// Action type
159    pub action_type: PrivilegedActionType,
160    /// Resource affected
161    pub resource: Option<String>,
162    /// Action details
163    pub details: Option<String>,
164    /// IP address
165    pub ip_address: Option<String>,
166    /// User agent
167    pub user_agent: Option<String>,
168    /// Session ID
169    pub session_id: Option<String>,
170    /// Timestamp
171    pub timestamp: DateTime<Utc>,
172}
173
174/// Privileged session information
175#[derive(Debug, Clone, Serialize, Deserialize)]
176pub struct PrivilegedSession {
177    /// Session ID
178    pub session_id: String,
179    /// User ID
180    pub user_id: Uuid,
181    /// Role
182    pub role: PrivilegedRole,
183    /// Session start time
184    pub started_at: DateTime<Utc>,
185    /// Last activity time
186    pub last_activity: DateTime<Utc>,
187    /// IP address
188    pub ip_address: Option<String>,
189    /// User agent
190    pub user_agent: Option<String>,
191    /// Whether session is active
192    pub is_active: bool,
193}
194
195/// Privileged access configuration
196#[derive(Debug, Clone, Serialize, Deserialize)]
197#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
198pub struct PrivilegedAccessConfig {
199    /// Require MFA for privileged users
200    pub require_mfa: bool,
201    /// MFA grace period in days
202    pub mfa_grace_period_days: u64,
203    /// Auto-suspend if MFA not enabled
204    pub auto_suspend_no_mfa: bool,
205    /// Session timeout in minutes
206    pub session_timeout_minutes: u64,
207    /// Max concurrent sessions
208    pub max_concurrent_sessions: u32,
209    /// Record sensitive actions
210    pub record_sensitive_actions: bool,
211    /// Monitor activity
212    pub monitor_activity: bool,
213    /// Sensitive action types that require alerting
214    pub sensitive_actions: Vec<PrivilegedActionType>,
215}
216
217impl Default for PrivilegedAccessConfig {
218    fn default() -> Self {
219        Self {
220            require_mfa: true,
221            mfa_grace_period_days: 7,
222            auto_suspend_no_mfa: true,
223            session_timeout_minutes: 30,
224            max_concurrent_sessions: 2,
225            record_sensitive_actions: true,
226            monitor_activity: true,
227            sensitive_actions: vec![
228                PrivilegedActionType::UserDelete,
229                PrivilegedActionType::RoleEscalate,
230                PrivilegedActionType::SecurityPolicyChange,
231                PrivilegedActionType::DataExport,
232                PrivilegedActionType::AuditLogAccess,
233            ],
234        }
235    }
236}
237
238/// Privileged access manager
239///
240/// Manages privileged access requests, monitoring, and enforcement
241pub struct PrivilegedAccessManager {
242    config: PrivilegedAccessConfig,
243    mfa_storage: Option<Arc<dyn MfaStorage>>,
244    justification_storage: Option<Arc<dyn JustificationStorage>>,
245    /// Active privileged sessions
246    sessions: Arc<RwLock<HashMap<String, PrivilegedSession>>>,
247    /// Privileged actions log
248    actions: Arc<RwLock<Vec<PrivilegedAction>>>,
249    /// Active access requests
250    requests: Arc<RwLock<HashMap<Uuid, PrivilegedAccessRequest>>>,
251}
252
253impl PrivilegedAccessManager {
254    /// Create a new privileged access manager
255    pub fn new(
256        config: PrivilegedAccessConfig,
257        mfa_storage: Option<Arc<dyn MfaStorage>>,
258        justification_storage: Option<Arc<dyn JustificationStorage>>,
259    ) -> Self {
260        Self {
261            config,
262            mfa_storage,
263            justification_storage,
264            sessions: Arc::new(RwLock::new(HashMap::new())),
265            actions: Arc::new(RwLock::new(Vec::new())),
266            requests: Arc::new(RwLock::new(HashMap::new())),
267        }
268    }
269
270    /// Request privileged access
271    pub async fn request_privileged_access(
272        &self,
273        user_id: Uuid,
274        requested_role: PrivilegedRole,
275        justification: String,
276        business_need: Option<String>,
277        manager_approval: Option<Uuid>,
278    ) -> Result<PrivilegedAccessRequest, Error> {
279        let request = PrivilegedAccessRequest::new(
280            user_id,
281            requested_role,
282            justification,
283            business_need,
284            manager_approval,
285        );
286
287        let mut requests = self.requests.write().await;
288        requests.insert(request.request_id, request.clone());
289
290        Ok(request)
291    }
292
293    /// Approve privileged access request (manager approval)
294    pub async fn approve_manager(&self, request_id: Uuid, approver_id: Uuid) -> Result<(), Error> {
295        let mut requests = self.requests.write().await;
296        let request = requests
297            .get_mut(&request_id)
298            .ok_or_else(|| Error::Generic("Request not found".to_string()))?;
299
300        if request.status != RequestStatus::PendingManager {
301            return Err(Error::Generic("Request is not pending manager approval".to_string()));
302        }
303
304        let user_id = request.user_id;
305        request.manager_approval = Some(approver_id);
306        request.status = RequestStatus::PendingSecurity;
307        request.updated_at = Utc::now();
308
309        // Emit security event for manager approval
310        let event = SecurityEvent::new(SecurityEventType::AuthzPrivilegeEscalation, None, None)
311            .with_actor(EventActor {
312                user_id: Some(approver_id.to_string()),
313                username: None,
314                ip_address: None,
315                user_agent: None,
316            })
317            .with_target(EventTarget {
318                resource_type: Some("privileged_access_request".to_string()),
319                resource_id: Some(request_id.to_string()),
320                method: None,
321            })
322            .with_outcome(EventOutcome {
323                success: true,
324                reason: Some("Manager approval granted".to_string()),
325            })
326            .with_metadata("request_user_id".to_string(), serde_json::json!(user_id.to_string()))
327            .with_metadata(
328                "requested_role".to_string(),
329                serde_json::json!(format!("{:?}", request.requested_role)),
330            );
331
332        emit_security_event_async(event);
333
334        Ok(())
335    }
336
337    /// Approve privileged access request (security approval)
338    pub async fn approve_security(
339        &self,
340        request_id: Uuid,
341        approver_id: Uuid,
342        expiration_days: u64,
343    ) -> Result<(), Error> {
344        let mut requests = self.requests.write().await;
345        let request = requests
346            .get_mut(&request_id)
347            .ok_or_else(|| Error::Generic("Request not found".to_string()))?;
348
349        if request.status != RequestStatus::PendingSecurity {
350            return Err(Error::Generic("Request is not pending security approval".to_string()));
351        }
352
353        request.security_approval = Some(approver_id);
354        request.status = RequestStatus::Approved;
355        request.expires_at = Some(Utc::now() + Duration::days(expiration_days as i64));
356        request.updated_at = Utc::now();
357
358        // Store justification
359        if let Some(ref just_storage) = self.justification_storage {
360            let justification = AccessJustification::new(
361                request.user_id,
362                request.justification.clone(),
363                request.business_need.clone(),
364                request.manager_approval,
365                request.expires_at,
366            );
367            just_storage.set_justification(justification).await?;
368        }
369
370        // Emit security event for security approval
371        let event = SecurityEvent::new(SecurityEventType::AuthzPrivilegeEscalation, None, None)
372            .with_actor(EventActor {
373                user_id: Some(approver_id.to_string()),
374                username: None,
375                ip_address: None,
376                user_agent: None,
377            })
378            .with_target(EventTarget {
379                resource_type: Some("privileged_access_request".to_string()),
380                resource_id: Some(request_id.to_string()),
381                method: None,
382            })
383            .with_outcome(EventOutcome {
384                success: true,
385                reason: Some("Security approval granted".to_string()),
386            })
387            .with_metadata(
388                "request_user_id".to_string(),
389                serde_json::json!(request.user_id.to_string()),
390            )
391            .with_metadata(
392                "requested_role".to_string(),
393                serde_json::json!(format!("{:?}", request.requested_role)),
394            )
395            .with_metadata("expiration_days".to_string(), serde_json::json!(expiration_days));
396
397        emit_security_event_async(event);
398
399        Ok(())
400    }
401
402    /// Deny privileged access request
403    pub async fn deny_request(&self, request_id: Uuid, reason: String) -> Result<(), Error> {
404        let mut requests = self.requests.write().await;
405        let request = requests
406            .get_mut(&request_id)
407            .ok_or_else(|| Error::Generic("Request not found".to_string()))?;
408
409        let user_id = request.user_id;
410        request.status = RequestStatus::Denied;
411        request.updated_at = Utc::now();
412
413        // Emit security event for request denial
414        let event = SecurityEvent::new(SecurityEventType::AuthzAccessDenied, None, None)
415            .with_actor(EventActor {
416                user_id: Some(user_id.to_string()),
417                username: None,
418                ip_address: None,
419                user_agent: None,
420            })
421            .with_target(EventTarget {
422                resource_type: Some("privileged_access_request".to_string()),
423                resource_id: Some(request_id.to_string()),
424                method: None,
425            })
426            .with_outcome(EventOutcome {
427                success: false,
428                reason: Some(reason.clone()),
429            })
430            .with_metadata(
431                "requested_role".to_string(),
432                serde_json::json!(format!("{:?}", request.requested_role)),
433            );
434
435        emit_security_event_async(event);
436
437        Ok(())
438    }
439
440    /// Check MFA compliance for a user
441    pub async fn check_mfa_compliance(&self, user_id: Uuid) -> Result<bool, Error> {
442        if !self.config.require_mfa {
443            return Ok(true);
444        }
445
446        if let Some(ref mfa_storage) = self.mfa_storage {
447            let mfa_status = mfa_storage.get_mfa_status(user_id).await?;
448            Ok(mfa_status.map(|s| s.enabled).unwrap_or(false))
449        } else {
450            // No MFA storage configured, assume compliant
451            Ok(true)
452        }
453    }
454
455    /// Record a privileged action
456    pub async fn record_action(
457        &self,
458        user_id: Uuid,
459        action_type: PrivilegedActionType,
460        resource: Option<String>,
461        details: Option<String>,
462        ip_address: Option<String>,
463        user_agent: Option<String>,
464        session_id: Option<String>,
465    ) -> Result<Uuid, Error> {
466        let action = PrivilegedAction {
467            action_id: Uuid::new_v4(),
468            user_id,
469            action_type,
470            resource,
471            details,
472            ip_address,
473            user_agent,
474            session_id,
475            timestamp: Utc::now(),
476        };
477
478        let mut actions = self.actions.write().await;
479        actions.push(action.clone());
480
481        // Emit security event for all privileged actions
482        // Use higher severity for sensitive actions
483        let event_type = if self.config.sensitive_actions.contains(&action_type) {
484            SecurityEventType::AuthzPrivilegeEscalation
485        } else {
486            SecurityEventType::AuthzAccessGranted
487        };
488
489        let event = SecurityEvent::new(event_type, None, None)
490            .with_actor(EventActor {
491                user_id: Some(action.user_id.to_string()),
492                username: None,
493                ip_address: action.ip_address.clone(),
494                user_agent: action.user_agent.clone(),
495            })
496            .with_target(EventTarget {
497                resource_type: Some(format!("privileged_action_{:?}", action_type)),
498                resource_id: Some(action.action_id.to_string()),
499                method: action.resource.clone(),
500            })
501            .with_outcome(EventOutcome {
502                success: true,
503                reason: action.details.clone(),
504            })
505            .with_metadata(
506                "action_type".to_string(),
507                serde_json::json!(format!("{:?}", action_type)),
508            )
509            .with_metadata(
510                "session_id".to_string(),
511                serde_json::json!(action.session_id.clone().unwrap_or_default()),
512            );
513
514        // Emit asynchronously to avoid blocking
515        emit_security_event_async(event);
516
517        Ok(action.action_id)
518    }
519
520    /// Start a privileged session
521    pub async fn start_session(
522        &self,
523        session_id: String,
524        user_id: Uuid,
525        role: PrivilegedRole,
526        ip_address: Option<String>,
527        user_agent: Option<String>,
528    ) -> Result<(), Error> {
529        // Check MFA compliance
530        if !self.check_mfa_compliance(user_id).await? && self.config.auto_suspend_no_mfa {
531            return Err(Error::Generic("MFA not enabled for privileged user".to_string()));
532        }
533
534        // Check concurrent session limit
535        let sessions = self.sessions.read().await;
536        let active_sessions =
537            sessions.values().filter(|s| s.user_id == user_id && s.is_active).count();
538
539        if active_sessions >= self.config.max_concurrent_sessions as usize {
540            return Err(Error::Generic("Maximum concurrent sessions reached".to_string()));
541        }
542        drop(sessions);
543
544        // Clone values before moving into session
545        let ip_address_clone = ip_address.clone();
546        let user_agent_clone = user_agent.clone();
547
548        let session = PrivilegedSession {
549            session_id: session_id.clone(),
550            user_id,
551            role,
552            started_at: Utc::now(),
553            last_activity: Utc::now(),
554            ip_address,
555            user_agent,
556            is_active: true,
557        };
558
559        let mut sessions = self.sessions.write().await;
560        sessions.insert(session_id.clone(), session);
561
562        // Emit security event for privileged session start
563        let event = SecurityEvent::new(SecurityEventType::AuthzPrivilegeEscalation, None, None)
564            .with_actor(EventActor {
565                user_id: Some(user_id.to_string()),
566                username: None,
567                ip_address: ip_address_clone,
568                user_agent: user_agent_clone,
569            })
570            .with_target(EventTarget {
571                resource_type: Some("privileged_session".to_string()),
572                resource_id: Some(session_id.clone()),
573                method: Some(format!("{:?}", role)),
574            })
575            .with_outcome(EventOutcome {
576                success: true,
577                reason: Some("Privileged session started".to_string()),
578            })
579            .with_metadata("role".to_string(), serde_json::json!(format!("{:?}", role)));
580
581        emit_security_event_async(event);
582
583        Ok(())
584    }
585
586    /// Update session activity
587    pub async fn update_session_activity(&self, session_id: &str) -> Result<(), Error> {
588        let mut sessions = self.sessions.write().await;
589        if let Some(session) = sessions.get_mut(session_id) {
590            session.last_activity = Utc::now();
591        }
592        Ok(())
593    }
594
595    /// End a privileged session
596    pub async fn end_session(&self, session_id: &str) -> Result<(), Error> {
597        let mut sessions = self.sessions.write().await;
598        if let Some(session) = sessions.get_mut(session_id) {
599            let user_id = session.user_id;
600            let role = session.role;
601            session.is_active = false;
602
603            // Emit security event for privileged session end
604            let event = SecurityEvent::new(SecurityEventType::AuthzAccessGranted, None, None)
605                .with_actor(EventActor {
606                    user_id: Some(user_id.to_string()),
607                    username: None,
608                    ip_address: session.ip_address.clone(),
609                    user_agent: session.user_agent.clone(),
610                })
611                .with_target(EventTarget {
612                    resource_type: Some("privileged_session".to_string()),
613                    resource_id: Some(session_id.to_string()),
614                    method: Some(format!("{:?}", role)),
615                })
616                .with_outcome(EventOutcome {
617                    success: true,
618                    reason: Some("Privileged session ended".to_string()),
619                })
620                .with_metadata("role".to_string(), serde_json::json!(format!("{:?}", role)))
621                .with_metadata(
622                    "duration_seconds".to_string(),
623                    serde_json::json!((Utc::now() - session.started_at).num_seconds()),
624                );
625
626            emit_security_event_async(event);
627        }
628        Ok(())
629    }
630
631    /// Check for expired sessions and clean them up
632    pub async fn cleanup_expired_sessions(&self) -> Result<Vec<String>, Error> {
633        let timeout = Duration::minutes(self.config.session_timeout_minutes as i64);
634        let now = Utc::now();
635        let mut expired = Vec::new();
636
637        let mut sessions = self.sessions.write().await;
638        for (session_id, session) in sessions.iter_mut() {
639            if session.is_active && (now - session.last_activity) > timeout {
640                session.is_active = false;
641                expired.push(session_id.clone());
642            }
643        }
644
645        Ok(expired)
646    }
647
648    /// Get all privileged actions for a user
649    pub async fn get_user_actions(&self, user_id: Uuid) -> Result<Vec<PrivilegedAction>, Error> {
650        let actions = self.actions.read().await;
651        Ok(actions.iter().filter(|a| a.user_id == user_id).cloned().collect())
652    }
653
654    /// Get all active privileged sessions
655    pub async fn get_active_sessions(&self) -> Result<Vec<PrivilegedSession>, Error> {
656        let sessions = self.sessions.read().await;
657        Ok(sessions.values().filter(|s| s.is_active).cloned().collect())
658    }
659
660    /// Get access request by ID
661    pub async fn get_request(
662        &self,
663        request_id: Uuid,
664    ) -> Result<Option<PrivilegedAccessRequest>, Error> {
665        let requests = self.requests.read().await;
666        Ok(requests.get(&request_id).cloned())
667    }
668
669    /// Get all requests for a user
670    pub async fn get_user_requests(
671        &self,
672        user_id: Uuid,
673    ) -> Result<Vec<PrivilegedAccessRequest>, Error> {
674        let requests = self.requests.read().await;
675        Ok(requests.values().filter(|r| r.user_id == user_id).cloned().collect())
676    }
677}
678
679// Required for Arc usage
680use std::sync::Arc;
681use tokio::sync::RwLock;
682
683#[cfg(test)]
684mod tests {
685    use super::*;
686
687    #[tokio::test]
688    async fn test_privileged_access_request() {
689        let manager = PrivilegedAccessManager::new(PrivilegedAccessConfig::default(), None, None);
690
691        let request = manager
692            .request_privileged_access(
693                Uuid::new_v4(),
694                PrivilegedRole::Admin,
695                "Required for system administration".to_string(),
696                Some("Manage production infrastructure".to_string()),
697                Some(Uuid::new_v4()),
698            )
699            .await
700            .unwrap();
701
702        assert_eq!(request.status, RequestStatus::PendingManager);
703        assert!(request.manager_approval.is_some());
704    }
705}