1use 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#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
25#[serde(rename_all = "lowercase")]
26pub enum PrivilegedRole {
27 Admin,
29 Owner,
31 ServiceAccount,
33}
34
35#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
37#[serde(rename_all = "lowercase")]
38pub enum RequestStatus {
39 PendingManager,
41 PendingSecurity,
43 Approved,
45 Denied,
47 Cancelled,
49}
50
51#[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 UserCreate,
58 UserDelete,
60 UserModify,
62 RoleAssign,
64 RoleRevoke,
66 RoleEscalate,
68 PermissionGrant,
70 PermissionRevoke,
72 ConfigModify,
74 SecurityPolicyChange,
76 SecuritySettingChange,
78 AuditLogAccess,
80 DataExport,
82 DataDelete,
84 Other,
86}
87
88#[derive(Debug, Clone, Serialize, Deserialize)]
90pub struct PrivilegedAccessRequest {
91 pub request_id: Uuid,
93 pub user_id: Uuid,
95 pub requested_role: PrivilegedRole,
97 pub justification: String,
99 pub business_need: Option<String>,
101 pub manager_approval: Option<Uuid>,
103 pub security_approval: Option<Uuid>,
105 pub status: RequestStatus,
107 pub created_at: DateTime<Utc>,
109 pub updated_at: DateTime<Utc>,
111 pub expires_at: Option<DateTime<Utc>>,
113}
114
115impl PrivilegedAccessRequest {
116 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 pub fn is_approved(&self) -> bool {
142 self.status == RequestStatus::Approved
143 }
144
145 pub fn is_expired(&self) -> bool {
147 self.expires_at.map(|exp| Utc::now() > exp).unwrap_or(false)
148 }
149}
150
151#[derive(Debug, Clone, Serialize, Deserialize)]
153pub struct PrivilegedAction {
154 pub action_id: Uuid,
156 pub user_id: Uuid,
158 pub action_type: PrivilegedActionType,
160 pub resource: Option<String>,
162 pub details: Option<String>,
164 pub ip_address: Option<String>,
166 pub user_agent: Option<String>,
168 pub session_id: Option<String>,
170 pub timestamp: DateTime<Utc>,
172}
173
174#[derive(Debug, Clone, Serialize, Deserialize)]
176pub struct PrivilegedSession {
177 pub session_id: String,
179 pub user_id: Uuid,
181 pub role: PrivilegedRole,
183 pub started_at: DateTime<Utc>,
185 pub last_activity: DateTime<Utc>,
187 pub ip_address: Option<String>,
189 pub user_agent: Option<String>,
191 pub is_active: bool,
193}
194
195#[derive(Debug, Clone, Serialize, Deserialize)]
197#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
198pub struct PrivilegedAccessConfig {
199 pub require_mfa: bool,
201 pub mfa_grace_period_days: u64,
203 pub auto_suspend_no_mfa: bool,
205 pub session_timeout_minutes: u64,
207 pub max_concurrent_sessions: u32,
209 pub record_sensitive_actions: bool,
211 pub monitor_activity: bool,
213 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
238pub struct PrivilegedAccessManager {
242 config: PrivilegedAccessConfig,
243 mfa_storage: Option<Arc<dyn MfaStorage>>,
244 justification_storage: Option<Arc<dyn JustificationStorage>>,
245 sessions: Arc<RwLock<HashMap<String, PrivilegedSession>>>,
247 actions: Arc<RwLock<Vec<PrivilegedAction>>>,
249 requests: Arc<RwLock<HashMap<Uuid, PrivilegedAccessRequest>>>,
251}
252
253impl PrivilegedAccessManager {
254 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 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 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 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 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 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 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 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 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 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 Ok(true)
452 }
453 }
454
455 #[allow(clippy::too_many_arguments)]
457 pub async fn record_action(
458 &self,
459 user_id: Uuid,
460 action_type: PrivilegedActionType,
461 resource: Option<String>,
462 details: Option<String>,
463 ip_address: Option<String>,
464 user_agent: Option<String>,
465 session_id: Option<String>,
466 ) -> Result<Uuid, Error> {
467 let action = PrivilegedAction {
468 action_id: Uuid::new_v4(),
469 user_id,
470 action_type,
471 resource,
472 details,
473 ip_address,
474 user_agent,
475 session_id,
476 timestamp: Utc::now(),
477 };
478
479 let mut actions = self.actions.write().await;
480 actions.push(action.clone());
481
482 let event_type = if self.config.sensitive_actions.contains(&action_type) {
485 SecurityEventType::AuthzPrivilegeEscalation
486 } else {
487 SecurityEventType::AuthzAccessGranted
488 };
489
490 let event = SecurityEvent::new(event_type, None, None)
491 .with_actor(EventActor {
492 user_id: Some(action.user_id.to_string()),
493 username: None,
494 ip_address: action.ip_address.clone(),
495 user_agent: action.user_agent.clone(),
496 })
497 .with_target(EventTarget {
498 resource_type: Some(format!("privileged_action_{:?}", action_type)),
499 resource_id: Some(action.action_id.to_string()),
500 method: action.resource.clone(),
501 })
502 .with_outcome(EventOutcome {
503 success: true,
504 reason: action.details.clone(),
505 })
506 .with_metadata(
507 "action_type".to_string(),
508 serde_json::json!(format!("{:?}", action_type)),
509 )
510 .with_metadata(
511 "session_id".to_string(),
512 serde_json::json!(action.session_id.clone().unwrap_or_default()),
513 );
514
515 emit_security_event_async(event);
517
518 Ok(action.action_id)
519 }
520
521 pub async fn start_session(
523 &self,
524 session_id: String,
525 user_id: Uuid,
526 role: PrivilegedRole,
527 ip_address: Option<String>,
528 user_agent: Option<String>,
529 ) -> Result<(), Error> {
530 if !self.check_mfa_compliance(user_id).await? && self.config.auto_suspend_no_mfa {
532 return Err(Error::Generic("MFA not enabled for privileged user".to_string()));
533 }
534
535 let sessions = self.sessions.read().await;
537 let active_sessions =
538 sessions.values().filter(|s| s.user_id == user_id && s.is_active).count();
539
540 if active_sessions >= self.config.max_concurrent_sessions as usize {
541 return Err(Error::Generic("Maximum concurrent sessions reached".to_string()));
542 }
543 drop(sessions);
544
545 let ip_address_clone = ip_address.clone();
547 let user_agent_clone = user_agent.clone();
548
549 let session = PrivilegedSession {
550 session_id: session_id.clone(),
551 user_id,
552 role,
553 started_at: Utc::now(),
554 last_activity: Utc::now(),
555 ip_address,
556 user_agent,
557 is_active: true,
558 };
559
560 let mut sessions = self.sessions.write().await;
561 sessions.insert(session_id.clone(), session);
562
563 let event = SecurityEvent::new(SecurityEventType::AuthzPrivilegeEscalation, None, None)
565 .with_actor(EventActor {
566 user_id: Some(user_id.to_string()),
567 username: None,
568 ip_address: ip_address_clone,
569 user_agent: user_agent_clone,
570 })
571 .with_target(EventTarget {
572 resource_type: Some("privileged_session".to_string()),
573 resource_id: Some(session_id.clone()),
574 method: Some(format!("{:?}", role)),
575 })
576 .with_outcome(EventOutcome {
577 success: true,
578 reason: Some("Privileged session started".to_string()),
579 })
580 .with_metadata("role".to_string(), serde_json::json!(format!("{:?}", role)));
581
582 emit_security_event_async(event);
583
584 Ok(())
585 }
586
587 pub async fn update_session_activity(&self, session_id: &str) -> Result<(), Error> {
589 let mut sessions = self.sessions.write().await;
590 if let Some(session) = sessions.get_mut(session_id) {
591 session.last_activity = Utc::now();
592 }
593 Ok(())
594 }
595
596 pub async fn end_session(&self, session_id: &str) -> Result<(), Error> {
598 let mut sessions = self.sessions.write().await;
599 if let Some(session) = sessions.get_mut(session_id) {
600 let user_id = session.user_id;
601 let role = session.role;
602 session.is_active = false;
603
604 let event = SecurityEvent::new(SecurityEventType::AuthzAccessGranted, None, None)
606 .with_actor(EventActor {
607 user_id: Some(user_id.to_string()),
608 username: None,
609 ip_address: session.ip_address.clone(),
610 user_agent: session.user_agent.clone(),
611 })
612 .with_target(EventTarget {
613 resource_type: Some("privileged_session".to_string()),
614 resource_id: Some(session_id.to_string()),
615 method: Some(format!("{:?}", role)),
616 })
617 .with_outcome(EventOutcome {
618 success: true,
619 reason: Some("Privileged session ended".to_string()),
620 })
621 .with_metadata("role".to_string(), serde_json::json!(format!("{:?}", role)))
622 .with_metadata(
623 "duration_seconds".to_string(),
624 serde_json::json!((Utc::now() - session.started_at).num_seconds()),
625 );
626
627 emit_security_event_async(event);
628 }
629 Ok(())
630 }
631
632 pub async fn cleanup_expired_sessions(&self) -> Result<Vec<String>, Error> {
634 let timeout = Duration::minutes(self.config.session_timeout_minutes as i64);
635 let now = Utc::now();
636 let mut expired = Vec::new();
637
638 let mut sessions = self.sessions.write().await;
639 for (session_id, session) in sessions.iter_mut() {
640 if session.is_active && (now - session.last_activity) > timeout {
641 session.is_active = false;
642 expired.push(session_id.clone());
643 }
644 }
645
646 Ok(expired)
647 }
648
649 pub async fn get_user_actions(&self, user_id: Uuid) -> Result<Vec<PrivilegedAction>, Error> {
651 let actions = self.actions.read().await;
652 Ok(actions.iter().filter(|a| a.user_id == user_id).cloned().collect())
653 }
654
655 pub async fn get_active_sessions(&self) -> Result<Vec<PrivilegedSession>, Error> {
657 let sessions = self.sessions.read().await;
658 Ok(sessions.values().filter(|s| s.is_active).cloned().collect())
659 }
660
661 pub async fn get_request(
663 &self,
664 request_id: Uuid,
665 ) -> Result<Option<PrivilegedAccessRequest>, Error> {
666 let requests = self.requests.read().await;
667 Ok(requests.get(&request_id).cloned())
668 }
669
670 pub async fn get_user_requests(
672 &self,
673 user_id: Uuid,
674 ) -> Result<Vec<PrivilegedAccessRequest>, Error> {
675 let requests = self.requests.read().await;
676 Ok(requests.values().filter(|r| r.user_id == user_id).cloned().collect())
677 }
678}
679
680use std::sync::Arc;
682use tokio::sync::RwLock;
683
684#[cfg(test)]
685mod tests {
686 use super::*;
687
688 #[tokio::test]
689 async fn test_privileged_access_request() {
690 let manager = PrivilegedAccessManager::new(PrivilegedAccessConfig::default(), None, None);
691
692 let request = manager
693 .request_privileged_access(
694 Uuid::new_v4(),
695 PrivilegedRole::Admin,
696 "Required for system administration".to_string(),
697 Some("Manage production infrastructure".to_string()),
698 Some(Uuid::new_v4()),
699 )
700 .await
701 .unwrap();
702
703 assert_eq!(request.status, RequestStatus::PendingManager);
704 assert!(request.manager_approval.is_some());
705 }
706}