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