1use crate::DeviceResult;
7use serde::{Deserialize, Serialize};
8use std::collections::HashMap;
9use std::time::{Duration, SystemTime};
10
11pub struct SessionManager {
13 active_sessions: HashMap<String, UserSession>,
14 session_config: SessionConfig,
15 auth_provider: Box<dyn AuthProvider + Send + Sync>,
16 permission_manager: PermissionManager,
17}
18
19#[derive(Debug, Clone)]
21pub struct UserSession {
22 pub session_id: String,
23 pub user_id: String,
24 pub permissions: Vec<Permission>,
25 pub preferences: UserPreferences,
26 pub last_activity: SystemTime,
27 pub session_data: HashMap<String, String>,
28 pub auth_token: Option<String>,
29 pub expires_at: SystemTime,
30}
31
32#[derive(Debug, Clone)]
34pub struct SessionConfig {
35 pub session_timeout: Duration,
36 pub max_concurrent_sessions: usize,
37 pub require_authentication: bool,
38 pub enable_session_persistence: bool,
39 pub cookie_settings: CookieSettings,
40 pub security_settings: SecuritySettings,
41}
42
43#[derive(Debug, Clone)]
45pub struct CookieSettings {
46 pub secure: bool,
47 pub http_only: bool,
48 pub same_site: SameSitePolicy,
49 pub domain: Option<String>,
50 pub path: String,
51 pub max_age: Duration,
52}
53
54#[derive(Debug, Clone, PartialEq, Eq)]
56pub enum SameSitePolicy {
57 Strict,
58 Lax,
59 None,
60}
61
62#[derive(Debug, Clone)]
64pub struct SecuritySettings {
65 pub csrf_protection: bool,
66 pub rate_limiting: bool,
67 pub ip_whitelist: Option<Vec<String>>,
68 pub require_https: bool,
69 pub enable_audit_logging: bool,
70}
71
72#[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)]
74pub enum Permission {
75 ReadDashboard,
76 WriteDashboard,
77 ManageAlerts,
78 ExportData,
79 ViewReports,
80 ManageUsers,
81 SystemAdmin,
82 Custom(String),
83}
84
85#[derive(Debug, Clone, Serialize, Deserialize)]
87pub struct UserPreferences {
88 pub dashboard_layout: String,
89 pub default_time_range: String,
90 pub chart_preferences: ChartPreferences,
91 pub notification_preferences: NotificationPreferences,
92 pub display_preferences: DisplayPreferences,
93 pub custom_preferences: HashMap<String, String>,
94}
95
96#[derive(Debug, Clone, Serialize, Deserialize)]
98pub struct ChartPreferences {
99 pub preferred_chart_types: Vec<String>,
100 pub color_scheme: String,
101 pub animation_enabled: bool,
102 pub interactive_features: bool,
103 pub default_aggregation: String,
104 pub refresh_interval: u64,
105}
106
107#[derive(Debug, Clone, Serialize, Deserialize)]
109pub struct NotificationPreferences {
110 pub email_notifications: bool,
111 pub browser_notifications: bool,
112 pub slack_notifications: bool,
113 pub notification_frequency: NotificationFrequency,
114 pub alert_thresholds: HashMap<String, f64>,
115 pub quiet_hours: Option<QuietHours>,
116}
117
118#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
120pub enum NotificationFrequency {
121 Immediate,
122 Hourly,
123 Daily,
124 Weekly,
125 Custom(Duration),
126}
127
128#[derive(Debug, Clone, Serialize, Deserialize)]
130pub struct QuietHours {
131 pub start_time: String, pub end_time: String, pub timezone: String,
134 pub days_of_week: Vec<DayOfWeek>,
135}
136
137#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
139pub enum DayOfWeek {
140 Monday,
141 Tuesday,
142 Wednesday,
143 Thursday,
144 Friday,
145 Saturday,
146 Sunday,
147}
148
149#[derive(Debug, Clone, Serialize, Deserialize)]
151pub struct DisplayPreferences {
152 pub theme: String,
153 pub font_size: String,
154 pub density: DisplayDensity,
155 pub sidebar_collapsed: bool,
156 pub show_tooltips: bool,
157 pub language: String,
158 pub timezone: String,
159}
160
161#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
163pub enum DisplayDensity {
164 Compact,
165 Normal,
166 Spacious,
167}
168
169pub trait AuthProvider {
171 fn authenticate(&self, credentials: &Credentials) -> DeviceResult<AuthResult>;
172 fn validate_token(&self, token: &str) -> DeviceResult<TokenValidation>;
173 fn refresh_token(&self, refresh_token: &str) -> DeviceResult<AuthResult>;
174 fn logout(&self, token: &str) -> DeviceResult<()>;
175}
176
177#[derive(Debug, Clone)]
179pub struct Credentials {
180 pub username: Option<String>,
181 pub password: Option<String>,
182 pub token: Option<String>,
183 pub oauth_code: Option<String>,
184 pub provider: AuthProviderType,
185}
186
187#[derive(Debug, Clone, PartialEq, Eq)]
189pub enum AuthProviderType {
190 Local,
191 LDAP,
192 OAuth2,
193 SAML,
194 JWT,
195 Custom(String),
196}
197
198#[derive(Debug, Clone)]
200pub struct AuthResult {
201 pub success: bool,
202 pub user_info: Option<UserInfo>,
203 pub access_token: Option<String>,
204 pub refresh_token: Option<String>,
205 pub expires_at: Option<SystemTime>,
206 pub error_message: Option<String>,
207}
208
209#[derive(Debug, Clone)]
211pub struct UserInfo {
212 pub user_id: String,
213 pub username: String,
214 pub email: String,
215 pub full_name: String,
216 pub roles: Vec<Role>,
217 pub groups: Vec<String>,
218 pub metadata: HashMap<String, String>,
219}
220
221#[derive(Debug, Clone)]
223pub struct Role {
224 pub role_id: String,
225 pub role_name: String,
226 pub permissions: Vec<Permission>,
227 pub description: String,
228}
229
230#[derive(Debug, Clone)]
232pub struct TokenValidation {
233 pub valid: bool,
234 pub user_id: Option<String>,
235 pub expires_at: Option<SystemTime>,
236 pub scopes: Vec<String>,
237}
238
239pub struct PermissionManager {
241 role_definitions: HashMap<String, Role>,
242 permission_cache: HashMap<String, Vec<Permission>>,
243 access_policies: Vec<AccessPolicy>,
244}
245
246#[derive(Debug, Clone)]
248pub struct AccessPolicy {
249 pub policy_id: String,
250 pub resource: String,
251 pub action: String,
252 pub conditions: Vec<AccessCondition>,
253 pub effect: PolicyEffect,
254}
255
256#[derive(Debug, Clone, PartialEq, Eq)]
258pub enum PolicyEffect {
259 Allow,
260 Deny,
261}
262
263#[derive(Debug, Clone)]
265pub struct AccessCondition {
266 pub condition_type: ConditionType,
267 pub operator: ConditionOperator,
268 pub value: String,
269}
270
271#[derive(Debug, Clone, PartialEq, Eq)]
273pub enum ConditionType {
274 UserRole,
275 UserGroup,
276 TimeOfDay,
277 DayOfWeek,
278 IpAddress,
279 ResourceOwner,
280 Custom(String),
281}
282
283#[derive(Debug, Clone, PartialEq, Eq)]
285pub enum ConditionOperator {
286 Equals,
287 NotEquals,
288 Contains,
289 StartsWith,
290 EndsWith,
291 GreaterThan,
292 LessThan,
293 InRange,
294 Custom(String),
295}
296
297#[derive(Debug, Clone)]
299pub struct SessionActivity {
300 pub session_id: String,
301 pub user_id: String,
302 pub activity_type: ActivityType,
303 pub timestamp: SystemTime,
304 pub ip_address: String,
305 pub user_agent: String,
306 pub resource_accessed: Option<String>,
307 pub metadata: HashMap<String, String>,
308}
309
310#[derive(Debug, Clone, PartialEq, Eq)]
312pub enum ActivityType {
313 Login,
314 Logout,
315 DashboardView,
316 ChartInteraction,
317 DataExport,
318 ConfigurationChange,
319 AlertAcknowledgement,
320 Custom(String),
321}
322
323pub struct AuditLogger {
325 log_config: AuditLogConfig,
326 log_storage: Box<dyn LogStorage + Send + Sync>,
327 log_buffer: Vec<AuditEvent>,
328}
329
330#[derive(Debug, Clone)]
332pub struct AuditLogConfig {
333 pub enabled: bool,
334 pub log_level: AuditLogLevel,
335 pub retention_period: Duration,
336 pub include_sensitive_data: bool,
337 pub encryption_enabled: bool,
338}
339
340#[derive(Debug, Clone, PartialEq, Eq)]
342pub enum AuditLogLevel {
343 Debug,
344 Info,
345 Warning,
346 Error,
347 Critical,
348}
349
350#[derive(Debug, Clone)]
352pub struct AuditEvent {
353 pub event_id: String,
354 pub timestamp: SystemTime,
355 pub user_id: Option<String>,
356 pub session_id: Option<String>,
357 pub event_type: AuditEventType,
358 pub resource: String,
359 pub action: String,
360 pub result: AuditResult,
361 pub ip_address: String,
362 pub user_agent: String,
363 pub details: HashMap<String, String>,
364}
365
366#[derive(Debug, Clone, PartialEq, Eq)]
368pub enum AuditEventType {
369 Authentication,
370 Authorization,
371 DataAccess,
372 DataModification,
373 SystemChange,
374 SecurityEvent,
375 Custom(String),
376}
377
378#[derive(Debug, Clone, PartialEq, Eq)]
380pub enum AuditResult {
381 Success,
382 Failure,
383 Unauthorized,
384 Forbidden,
385 Error,
386}
387
388pub trait LogStorage {
390 fn store(&self, event: &AuditEvent) -> DeviceResult<()>;
391 fn query(&self, criteria: &QueryCriteria) -> DeviceResult<Vec<AuditEvent>>;
392 fn purge_old_logs(&self, before: SystemTime) -> DeviceResult<usize>;
393}
394
395#[derive(Debug, Clone)]
397pub struct QueryCriteria {
398 pub start_time: Option<SystemTime>,
399 pub end_time: Option<SystemTime>,
400 pub user_id: Option<String>,
401 pub event_type: Option<AuditEventType>,
402 pub resource: Option<String>,
403 pub result: Option<AuditResult>,
404 pub limit: Option<usize>,
405}
406
407impl SessionManager {
408 pub fn new(config: SessionConfig, auth_provider: Box<dyn AuthProvider + Send + Sync>) -> Self {
409 Self {
410 active_sessions: HashMap::new(),
411 session_config: config,
412 auth_provider,
413 permission_manager: PermissionManager::new(),
414 }
415 }
416
417 pub async fn create_session(&mut self, credentials: &Credentials) -> DeviceResult<UserSession> {
418 let auth_result = self.auth_provider.authenticate(credentials)?;
420
421 if !auth_result.success {
422 return Err(crate::DeviceError::APIError(
423 "Authentication failed".to_string(),
424 ));
425 }
426
427 let user_info = auth_result
428 .user_info
429 .ok_or_else(|| crate::DeviceError::APIError("Missing user information".to_string()))?;
430
431 let session_id = self.generate_session_id();
433 let expires_at = SystemTime::now() + self.session_config.session_timeout;
434
435 let permissions = self.permission_manager.get_user_permissions(&user_info)?;
436 let preferences = self.load_user_preferences(&user_info.user_id).await?;
437
438 let session = UserSession {
439 session_id: session_id.clone(),
440 user_id: user_info.user_id.clone(),
441 permissions,
442 preferences,
443 last_activity: SystemTime::now(),
444 session_data: HashMap::new(),
445 auth_token: auth_result.access_token,
446 expires_at,
447 };
448
449 self.enforce_session_limits(&user_info.user_id)?;
451
452 self.active_sessions
453 .insert(session_id.clone(), session.clone());
454
455 Ok(session)
456 }
457
458 pub fn validate_session(&mut self, session_id: &str) -> DeviceResult<&mut UserSession> {
459 if let Some(session) = self.active_sessions.get(session_id) {
461 if SystemTime::now() > session.expires_at {
462 self.active_sessions.remove(session_id);
463 return Err(crate::DeviceError::APIError("Session expired".to_string()));
464 }
465 } else {
466 return Err(crate::DeviceError::APIError("Invalid session".to_string()));
467 }
468
469 let session = self
472 .active_sessions
473 .get_mut(session_id)
474 .expect("Session was verified to exist");
475 session.last_activity = SystemTime::now();
476
477 Ok(session)
478 }
479
480 pub fn terminate_session(&mut self, session_id: &str) -> DeviceResult<()> {
481 if let Some(session) = self.active_sessions.remove(session_id) {
482 if let Some(token) = &session.auth_token {
484 let _ = self.auth_provider.logout(token);
485 }
486 }
487
488 Ok(())
489 }
490
491 pub fn cleanup_expired_sessions(&mut self) -> DeviceResult<usize> {
492 let now = SystemTime::now();
493 let expired_sessions: Vec<String> = self
494 .active_sessions
495 .iter()
496 .filter(|(_, session)| now > session.expires_at)
497 .map(|(id, _)| id.clone())
498 .collect();
499
500 let count = expired_sessions.len();
501 for session_id in expired_sessions {
502 self.active_sessions.remove(&session_id);
503 }
504
505 Ok(count)
506 }
507
508 pub async fn update_user_preferences(
509 &mut self,
510 user_id: &str,
511 preferences: UserPreferences,
512 ) -> DeviceResult<()> {
513 for session in self.active_sessions.values_mut() {
515 if session.user_id == user_id {
516 session.preferences = preferences.clone();
517 }
518 }
519
520 self.save_user_preferences(user_id, &preferences).await?;
522
523 Ok(())
524 }
525
526 pub fn get_session_statistics(&self) -> SessionStatistics {
527 let total_sessions = self.active_sessions.len();
528 let mut sessions_by_user = HashMap::new();
529 let mut recent_activity_count = 0;
530 let recent_threshold = SystemTime::now() - Duration::from_secs(5 * 60);
531
532 for session in self.active_sessions.values() {
533 *sessions_by_user.entry(session.user_id.clone()).or_insert(0) += 1;
534
535 if session.last_activity > recent_threshold {
536 recent_activity_count += 1;
537 }
538 }
539
540 SessionStatistics {
541 total_active_sessions: total_sessions,
542 sessions_by_user,
543 recent_activity_count,
544 average_session_duration: self.calculate_average_session_duration(),
545 }
546 }
547
548 fn generate_session_id(&self) -> String {
549 format!(
552 "session_{}",
553 SystemTime::now()
554 .duration_since(std::time::UNIX_EPOCH)
555 .unwrap_or(Duration::ZERO)
556 .as_nanos()
557 )
558 }
559
560 fn enforce_session_limits(&mut self, user_id: &str) -> DeviceResult<()> {
561 let user_session_count = self
562 .active_sessions
563 .values()
564 .filter(|s| s.user_id == user_id)
565 .count();
566
567 if user_session_count >= self.session_config.max_concurrent_sessions {
568 if let Some((oldest_session_id, _)) = self
570 .active_sessions
571 .iter()
572 .filter(|(_, s)| s.user_id == user_id)
573 .min_by_key(|(_, s)| s.last_activity)
574 .map(|(id, s)| (id.clone(), s.clone()))
575 {
576 self.active_sessions.remove(&oldest_session_id);
577 }
578 }
579
580 Ok(())
581 }
582
583 async fn load_user_preferences(&self, user_id: &str) -> DeviceResult<UserPreferences> {
584 Ok(UserPreferences::default())
586 }
587
588 async fn save_user_preferences(
589 &self,
590 user_id: &str,
591 preferences: &UserPreferences,
592 ) -> DeviceResult<()> {
593 Ok(())
595 }
596
597 fn calculate_average_session_duration(&self) -> Duration {
598 if self.active_sessions.is_empty() {
599 return Duration::from_secs(0);
600 }
601
602 let now = SystemTime::now();
603 let total_duration: Duration = self
604 .active_sessions
605 .values()
606 .map(|s| {
607 now.duration_since(s.last_activity)
608 .unwrap_or(Duration::from_secs(0))
609 })
610 .sum();
611
612 total_duration / self.active_sessions.len() as u32
613 }
614}
615
616#[derive(Debug, Clone)]
618pub struct SessionStatistics {
619 pub total_active_sessions: usize,
620 pub sessions_by_user: HashMap<String, usize>,
621 pub recent_activity_count: usize,
622 pub average_session_duration: Duration,
623}
624
625impl Default for PermissionManager {
626 fn default() -> Self {
627 Self::new()
628 }
629}
630
631impl PermissionManager {
632 pub fn new() -> Self {
633 Self {
634 role_definitions: Self::create_default_roles(),
635 permission_cache: HashMap::new(),
636 access_policies: Vec::new(),
637 }
638 }
639
640 pub fn get_user_permissions(&mut self, user_info: &UserInfo) -> DeviceResult<Vec<Permission>> {
641 if let Some(cached_permissions) = self.permission_cache.get(&user_info.user_id) {
643 return Ok(cached_permissions.clone());
644 }
645
646 let mut permissions = Vec::new();
648 for role in &user_info.roles {
649 if let Some(role_def) = self.role_definitions.get(&role.role_id) {
650 permissions.extend(role_def.permissions.clone());
651 }
652 }
653
654 permissions.sort();
656 permissions.dedup();
657
658 self.permission_cache
660 .insert(user_info.user_id.clone(), permissions.clone());
661
662 Ok(permissions)
663 }
664
665 pub fn check_permission(
666 &self,
667 user_permissions: &[Permission],
668 required_permission: &Permission,
669 ) -> bool {
670 user_permissions.contains(required_permission)
671 || user_permissions.contains(&Permission::SystemAdmin)
672 }
673
674 pub fn evaluate_access_policy(
675 &self,
676 user_info: &UserInfo,
677 resource: &str,
678 action: &str,
679 ) -> DeviceResult<bool> {
680 for policy in &self.access_policies {
681 if policy.resource == resource
682 && policy.action == action
683 && self.evaluate_conditions(&policy.conditions, user_info)?
684 {
685 return Ok(policy.effect == PolicyEffect::Allow);
686 }
687 }
688
689 Ok(false)
691 }
692
693 fn create_default_roles() -> HashMap<String, Role> {
694 let mut roles = HashMap::new();
695
696 roles.insert(
697 "admin".to_string(),
698 Role {
699 role_id: "admin".to_string(),
700 role_name: "Administrator".to_string(),
701 permissions: vec![Permission::SystemAdmin],
702 description: "Full system access".to_string(),
703 },
704 );
705
706 roles.insert(
707 "viewer".to_string(),
708 Role {
709 role_id: "viewer".to_string(),
710 role_name: "Viewer".to_string(),
711 permissions: vec![Permission::ReadDashboard, Permission::ViewReports],
712 description: "Read-only access".to_string(),
713 },
714 );
715
716 roles.insert(
717 "operator".to_string(),
718 Role {
719 role_id: "operator".to_string(),
720 role_name: "Operator".to_string(),
721 permissions: vec![
722 Permission::ReadDashboard,
723 Permission::WriteDashboard,
724 Permission::ManageAlerts,
725 Permission::ExportData,
726 Permission::ViewReports,
727 ],
728 description: "Operational access".to_string(),
729 },
730 );
731
732 roles
733 }
734
735 fn evaluate_conditions(
736 &self,
737 conditions: &[AccessCondition],
738 user_info: &UserInfo,
739 ) -> DeviceResult<bool> {
740 for condition in conditions {
741 if !self.evaluate_single_condition(condition, user_info)? {
742 return Ok(false);
743 }
744 }
745 Ok(true)
746 }
747
748 fn evaluate_single_condition(
749 &self,
750 condition: &AccessCondition,
751 user_info: &UserInfo,
752 ) -> DeviceResult<bool> {
753 match &condition.condition_type {
754 ConditionType::UserRole => {
755 let has_role = user_info.roles.iter().any(|r| r.role_id == condition.value);
756 Ok(match condition.operator {
757 ConditionOperator::Equals => has_role,
758 ConditionOperator::NotEquals => !has_role,
759 _ => false,
760 })
761 }
762 ConditionType::UserGroup => {
763 let in_group = user_info.groups.contains(&condition.value);
764 Ok(match condition.operator {
765 ConditionOperator::Equals => in_group,
766 ConditionOperator::NotEquals => !in_group,
767 _ => false,
768 })
769 }
770 _ => Ok(true), }
772 }
773}
774
775impl AuditLogger {
776 pub fn new(config: AuditLogConfig, storage: Box<dyn LogStorage + Send + Sync>) -> Self {
777 Self {
778 log_config: config,
779 log_storage: storage,
780 log_buffer: Vec::new(),
781 }
782 }
783
784 pub fn log_event(&mut self, event: AuditEvent) -> DeviceResult<()> {
785 if !self.log_config.enabled {
786 return Ok(());
787 }
788
789 self.log_buffer.push(event);
791
792 if self.log_buffer.len() >= 100 {
794 self.flush_buffer()?;
795 }
796
797 Ok(())
798 }
799
800 pub fn flush_buffer(&mut self) -> DeviceResult<()> {
801 for event in &self.log_buffer {
802 self.log_storage.store(event)?;
803 }
804 self.log_buffer.clear();
805 Ok(())
806 }
807
808 pub fn query_logs(&self, criteria: &QueryCriteria) -> DeviceResult<Vec<AuditEvent>> {
809 self.log_storage.query(criteria)
810 }
811}
812
813impl Default for UserPreferences {
815 fn default() -> Self {
816 Self {
817 dashboard_layout: "grid".to_string(),
818 default_time_range: "last_hour".to_string(),
819 chart_preferences: ChartPreferences::default(),
820 notification_preferences: NotificationPreferences::default(),
821 display_preferences: DisplayPreferences::default(),
822 custom_preferences: HashMap::new(),
823 }
824 }
825}
826
827impl Default for ChartPreferences {
828 fn default() -> Self {
829 Self {
830 preferred_chart_types: vec!["line".to_string(), "bar".to_string()],
831 color_scheme: "scientific".to_string(),
832 animation_enabled: true,
833 interactive_features: true,
834 default_aggregation: "minute".to_string(),
835 refresh_interval: 30,
836 }
837 }
838}
839
840impl Default for NotificationPreferences {
841 fn default() -> Self {
842 Self {
843 email_notifications: true,
844 browser_notifications: false,
845 slack_notifications: false,
846 notification_frequency: NotificationFrequency::Immediate,
847 alert_thresholds: HashMap::new(),
848 quiet_hours: None,
849 }
850 }
851}
852
853impl Default for DisplayPreferences {
854 fn default() -> Self {
855 Self {
856 theme: "light".to_string(),
857 font_size: "medium".to_string(),
858 density: DisplayDensity::Normal,
859 sidebar_collapsed: false,
860 show_tooltips: true,
861 language: "en".to_string(),
862 timezone: "UTC".to_string(),
863 }
864 }
865}
866
867impl Default for SessionConfig {
868 fn default() -> Self {
869 Self {
870 session_timeout: Duration::from_secs(8 * 3600),
871 max_concurrent_sessions: 5,
872 require_authentication: true,
873 enable_session_persistence: false,
874 cookie_settings: CookieSettings {
875 secure: true,
876 http_only: true,
877 same_site: SameSitePolicy::Strict,
878 domain: None,
879 path: "/".to_string(),
880 max_age: Duration::from_secs(8 * 3600),
881 },
882 security_settings: SecuritySettings {
883 csrf_protection: true,
884 rate_limiting: true,
885 ip_whitelist: None,
886 require_https: true,
887 enable_audit_logging: true,
888 },
889 }
890 }
891}
892
893impl Default for AuditLogConfig {
894 fn default() -> Self {
895 Self {
896 enabled: true,
897 log_level: AuditLogLevel::Info,
898 retention_period: Duration::from_secs(90 * 24 * 3600),
899 include_sensitive_data: false,
900 encryption_enabled: true,
901 }
902 }
903}