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)]
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)]
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)]
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)]
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)]
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)]
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)]
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)]
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)]
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)]
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)]
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)]
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.active_sessions.get_mut(session_id).unwrap();
471 session.last_activity = SystemTime::now();
472
473 Ok(session)
474 }
475
476 pub fn terminate_session(&mut self, session_id: &str) -> DeviceResult<()> {
477 if let Some(session) = self.active_sessions.remove(session_id) {
478 if let Some(token) = &session.auth_token {
480 let _ = self.auth_provider.logout(token);
481 }
482 }
483
484 Ok(())
485 }
486
487 pub fn cleanup_expired_sessions(&mut self) -> DeviceResult<usize> {
488 let now = SystemTime::now();
489 let expired_sessions: Vec<String> = self
490 .active_sessions
491 .iter()
492 .filter(|(_, session)| now > session.expires_at)
493 .map(|(id, _)| id.clone())
494 .collect();
495
496 let count = expired_sessions.len();
497 for session_id in expired_sessions {
498 self.active_sessions.remove(&session_id);
499 }
500
501 Ok(count)
502 }
503
504 pub async fn update_user_preferences(
505 &mut self,
506 user_id: &str,
507 preferences: UserPreferences,
508 ) -> DeviceResult<()> {
509 for session in self.active_sessions.values_mut() {
511 if session.user_id == user_id {
512 session.preferences = preferences.clone();
513 }
514 }
515
516 self.save_user_preferences(user_id, &preferences).await?;
518
519 Ok(())
520 }
521
522 pub fn get_session_statistics(&self) -> SessionStatistics {
523 let total_sessions = self.active_sessions.len();
524 let mut sessions_by_user = HashMap::new();
525 let mut recent_activity_count = 0;
526 let recent_threshold = SystemTime::now() - Duration::from_secs(5 * 60);
527
528 for session in self.active_sessions.values() {
529 *sessions_by_user.entry(session.user_id.clone()).or_insert(0) += 1;
530
531 if session.last_activity > recent_threshold {
532 recent_activity_count += 1;
533 }
534 }
535
536 SessionStatistics {
537 total_active_sessions: total_sessions,
538 sessions_by_user,
539 recent_activity_count,
540 average_session_duration: self.calculate_average_session_duration(),
541 }
542 }
543
544 fn generate_session_id(&self) -> String {
545 format!(
547 "session_{}",
548 SystemTime::now()
549 .duration_since(std::time::UNIX_EPOCH)
550 .unwrap()
551 .as_nanos()
552 )
553 }
554
555 fn enforce_session_limits(&mut self, user_id: &str) -> DeviceResult<()> {
556 let user_session_count = self
557 .active_sessions
558 .values()
559 .filter(|s| s.user_id == user_id)
560 .count();
561
562 if user_session_count >= self.session_config.max_concurrent_sessions {
563 if let Some((oldest_session_id, _)) = self
565 .active_sessions
566 .iter()
567 .filter(|(_, s)| s.user_id == user_id)
568 .min_by_key(|(_, s)| s.last_activity)
569 .map(|(id, s)| (id.clone(), s.clone()))
570 {
571 self.active_sessions.remove(&oldest_session_id);
572 }
573 }
574
575 Ok(())
576 }
577
578 async fn load_user_preferences(&self, user_id: &str) -> DeviceResult<UserPreferences> {
579 Ok(UserPreferences::default())
581 }
582
583 async fn save_user_preferences(
584 &self,
585 user_id: &str,
586 preferences: &UserPreferences,
587 ) -> DeviceResult<()> {
588 Ok(())
590 }
591
592 fn calculate_average_session_duration(&self) -> Duration {
593 if self.active_sessions.is_empty() {
594 return Duration::from_secs(0);
595 }
596
597 let now = SystemTime::now();
598 let total_duration: Duration = self
599 .active_sessions
600 .values()
601 .map(|s| {
602 now.duration_since(s.last_activity)
603 .unwrap_or(Duration::from_secs(0))
604 })
605 .sum();
606
607 total_duration / self.active_sessions.len() as u32
608 }
609}
610
611#[derive(Debug, Clone)]
613pub struct SessionStatistics {
614 pub total_active_sessions: usize,
615 pub sessions_by_user: HashMap<String, usize>,
616 pub recent_activity_count: usize,
617 pub average_session_duration: Duration,
618}
619
620impl PermissionManager {
621 pub fn new() -> Self {
622 Self {
623 role_definitions: Self::create_default_roles(),
624 permission_cache: HashMap::new(),
625 access_policies: Vec::new(),
626 }
627 }
628
629 pub fn get_user_permissions(&mut self, user_info: &UserInfo) -> DeviceResult<Vec<Permission>> {
630 if let Some(cached_permissions) = self.permission_cache.get(&user_info.user_id) {
632 return Ok(cached_permissions.clone());
633 }
634
635 let mut permissions = Vec::new();
637 for role in &user_info.roles {
638 if let Some(role_def) = self.role_definitions.get(&role.role_id) {
639 permissions.extend(role_def.permissions.clone());
640 }
641 }
642
643 permissions.sort();
645 permissions.dedup();
646
647 self.permission_cache
649 .insert(user_info.user_id.clone(), permissions.clone());
650
651 Ok(permissions)
652 }
653
654 pub fn check_permission(
655 &self,
656 user_permissions: &[Permission],
657 required_permission: &Permission,
658 ) -> bool {
659 user_permissions.contains(required_permission)
660 || user_permissions.contains(&Permission::SystemAdmin)
661 }
662
663 pub fn evaluate_access_policy(
664 &self,
665 user_info: &UserInfo,
666 resource: &str,
667 action: &str,
668 ) -> DeviceResult<bool> {
669 for policy in &self.access_policies {
670 if policy.resource == resource && policy.action == action {
671 if self.evaluate_conditions(&policy.conditions, user_info)? {
672 return Ok(policy.effect == PolicyEffect::Allow);
673 }
674 }
675 }
676
677 Ok(false)
679 }
680
681 fn create_default_roles() -> HashMap<String, Role> {
682 let mut roles = HashMap::new();
683
684 roles.insert(
685 "admin".to_string(),
686 Role {
687 role_id: "admin".to_string(),
688 role_name: "Administrator".to_string(),
689 permissions: vec![Permission::SystemAdmin],
690 description: "Full system access".to_string(),
691 },
692 );
693
694 roles.insert(
695 "viewer".to_string(),
696 Role {
697 role_id: "viewer".to_string(),
698 role_name: "Viewer".to_string(),
699 permissions: vec![Permission::ReadDashboard, Permission::ViewReports],
700 description: "Read-only access".to_string(),
701 },
702 );
703
704 roles.insert(
705 "operator".to_string(),
706 Role {
707 role_id: "operator".to_string(),
708 role_name: "Operator".to_string(),
709 permissions: vec![
710 Permission::ReadDashboard,
711 Permission::WriteDashboard,
712 Permission::ManageAlerts,
713 Permission::ExportData,
714 Permission::ViewReports,
715 ],
716 description: "Operational access".to_string(),
717 },
718 );
719
720 roles
721 }
722
723 fn evaluate_conditions(
724 &self,
725 conditions: &[AccessCondition],
726 user_info: &UserInfo,
727 ) -> DeviceResult<bool> {
728 for condition in conditions {
729 if !self.evaluate_single_condition(condition, user_info)? {
730 return Ok(false);
731 }
732 }
733 Ok(true)
734 }
735
736 fn evaluate_single_condition(
737 &self,
738 condition: &AccessCondition,
739 user_info: &UserInfo,
740 ) -> DeviceResult<bool> {
741 match &condition.condition_type {
742 ConditionType::UserRole => {
743 let has_role = user_info.roles.iter().any(|r| r.role_id == condition.value);
744 Ok(match condition.operator {
745 ConditionOperator::Equals => has_role,
746 ConditionOperator::NotEquals => !has_role,
747 _ => false,
748 })
749 }
750 ConditionType::UserGroup => {
751 let in_group = user_info.groups.contains(&condition.value);
752 Ok(match condition.operator {
753 ConditionOperator::Equals => in_group,
754 ConditionOperator::NotEquals => !in_group,
755 _ => false,
756 })
757 }
758 _ => Ok(true), }
760 }
761}
762
763impl AuditLogger {
764 pub fn new(config: AuditLogConfig, storage: Box<dyn LogStorage + Send + Sync>) -> Self {
765 Self {
766 log_config: config,
767 log_storage: storage,
768 log_buffer: Vec::new(),
769 }
770 }
771
772 pub fn log_event(&mut self, event: AuditEvent) -> DeviceResult<()> {
773 if !self.log_config.enabled {
774 return Ok(());
775 }
776
777 self.log_buffer.push(event);
779
780 if self.log_buffer.len() >= 100 {
782 self.flush_buffer()?;
783 }
784
785 Ok(())
786 }
787
788 pub fn flush_buffer(&mut self) -> DeviceResult<()> {
789 for event in &self.log_buffer {
790 self.log_storage.store(event)?;
791 }
792 self.log_buffer.clear();
793 Ok(())
794 }
795
796 pub fn query_logs(&self, criteria: &QueryCriteria) -> DeviceResult<Vec<AuditEvent>> {
797 self.log_storage.query(criteria)
798 }
799}
800
801impl Default for UserPreferences {
803 fn default() -> Self {
804 Self {
805 dashboard_layout: "grid".to_string(),
806 default_time_range: "last_hour".to_string(),
807 chart_preferences: ChartPreferences::default(),
808 notification_preferences: NotificationPreferences::default(),
809 display_preferences: DisplayPreferences::default(),
810 custom_preferences: HashMap::new(),
811 }
812 }
813}
814
815impl Default for ChartPreferences {
816 fn default() -> Self {
817 Self {
818 preferred_chart_types: vec!["line".to_string(), "bar".to_string()],
819 color_scheme: "scientific".to_string(),
820 animation_enabled: true,
821 interactive_features: true,
822 default_aggregation: "minute".to_string(),
823 refresh_interval: 30,
824 }
825 }
826}
827
828impl Default for NotificationPreferences {
829 fn default() -> Self {
830 Self {
831 email_notifications: true,
832 browser_notifications: false,
833 slack_notifications: false,
834 notification_frequency: NotificationFrequency::Immediate,
835 alert_thresholds: HashMap::new(),
836 quiet_hours: None,
837 }
838 }
839}
840
841impl Default for DisplayPreferences {
842 fn default() -> Self {
843 Self {
844 theme: "light".to_string(),
845 font_size: "medium".to_string(),
846 density: DisplayDensity::Normal,
847 sidebar_collapsed: false,
848 show_tooltips: true,
849 language: "en".to_string(),
850 timezone: "UTC".to_string(),
851 }
852 }
853}
854
855impl Default for SessionConfig {
856 fn default() -> Self {
857 Self {
858 session_timeout: Duration::from_secs(8 * 3600),
859 max_concurrent_sessions: 5,
860 require_authentication: true,
861 enable_session_persistence: false,
862 cookie_settings: CookieSettings {
863 secure: true,
864 http_only: true,
865 same_site: SameSitePolicy::Strict,
866 domain: None,
867 path: "/".to_string(),
868 max_age: Duration::from_secs(8 * 3600),
869 },
870 security_settings: SecuritySettings {
871 csrf_protection: true,
872 rate_limiting: true,
873 ip_whitelist: None,
874 require_https: true,
875 enable_audit_logging: true,
876 },
877 }
878 }
879}
880
881impl Default for AuditLogConfig {
882 fn default() -> Self {
883 Self {
884 enabled: true,
885 log_level: AuditLogLevel::Info,
886 retention_period: Duration::from_secs(90 * 24 * 3600),
887 include_sensitive_data: false,
888 encryption_enabled: true,
889 }
890 }
891}