pulseengine_mcp_auth/monitoring/
mod.rs

1//! Security Monitoring and Dashboard Module
2//!
3//! This module provides comprehensive security monitoring capabilities including
4//! real-time metrics, alerting, and dashboard functionality.
5
6pub mod dashboard_server;
7pub mod security_monitor;
8
9pub use security_monitor::{
10    create_default_alert_rules, AlertAction, AlertRule, AlertThreshold, MonitoringError,
11    SecurityAlert, SecurityDashboard, SecurityEvent, SecurityEventType, SecurityMetrics,
12    SecurityMonitor, SecurityMonitorConfig, SystemHealth,
13};
14
15#[cfg(test)]
16mod tests {
17    use super::*;
18    use crate::security::SecuritySeverity;
19
20    #[test]
21    fn test_monitoring_module_exports() {
22        // Test that all monitoring types are accessible
23
24        let config = SecurityMonitorConfig::default();
25        assert!(config.max_events_in_memory > 0); // Should be accessible
26
27        let event = SecurityEvent::new(
28            SecurityEventType::AuthSuccess,
29            SecuritySeverity::Low,
30            "Test event".to_string(),
31        );
32
33        assert_eq!(event.event_type, SecurityEventType::AuthSuccess);
34        assert_eq!(event.severity, SecuritySeverity::Low);
35        assert_eq!(event.description, "Test event");
36        assert!(!event.event_id.is_empty());
37
38        let threshold = AlertThreshold::Count(10);
39        assert!(matches!(threshold, AlertThreshold::Count(10)));
40
41        let action = AlertAction::Log {
42            level: "info".to_string(),
43        };
44        assert!(matches!(action, AlertAction::Log { level: _ }));
45    }
46
47    #[test]
48    fn test_security_event_types() {
49        let event_types = vec![
50            SecurityEventType::AuthSuccess,
51            SecurityEventType::AuthFailure,
52            SecurityEventType::PermissionDenied,
53            SecurityEventType::RateLimit,
54            SecurityEventType::SessionCreated,
55            SecurityEventType::SessionExpired,
56            SecurityEventType::InjectionAttempt,
57            SecurityEventType::ConfigChange,
58        ];
59
60        for event_type in event_types {
61            let event = SecurityEvent::new(
62                event_type.clone(),
63                SecuritySeverity::Medium,
64                format!("Test {:?}", event_type),
65            );
66
67            assert_eq!(event.event_type, event_type);
68            assert!(!event.description.is_empty());
69            assert!(event.timestamp <= chrono::Utc::now());
70        }
71    }
72
73    #[test]
74    fn test_alert_thresholds() {
75        let thresholds = vec![
76            AlertThreshold::Count(5),
77            AlertThreshold::Rate {
78                count: 10,
79                duration: chrono::Duration::minutes(5),
80            },
81            AlertThreshold::Percentage {
82                numerator_events: vec![SecurityEventType::AuthFailure],
83                denominator_events: vec![
84                    SecurityEventType::AuthSuccess,
85                    SecurityEventType::AuthFailure,
86                ],
87                threshold: 50.0,
88            },
89        ];
90
91        for threshold in thresholds {
92            match threshold {
93                AlertThreshold::Count(count) => assert!(count > 0),
94                AlertThreshold::Rate { count, duration } => {
95                    assert!(count > 0);
96                    assert!(duration > chrono::Duration::zero());
97                }
98                AlertThreshold::Percentage {
99                    threshold: percentage,
100                    ..
101                } => {
102                    assert!((0.0..=100.0).contains(&percentage));
103                }
104            }
105        }
106    }
107
108    #[test]
109    fn test_alert_actions() {
110        let actions = vec![
111            AlertAction::Log {
112                level: "warn".to_string(),
113            },
114            AlertAction::Email {
115                recipients: vec!["admin@example.com".to_string()],
116            },
117            AlertAction::Webhook {
118                url: "https://example.com/webhook".to_string(),
119                payload_template: "{}".to_string(),
120            },
121            AlertAction::BlockIp {
122                duration: chrono::Duration::hours(1),
123            },
124        ];
125
126        for action in actions {
127            match action {
128                AlertAction::Log { level } => assert!(!level.is_empty()),
129                AlertAction::Email { recipients } => assert!(!recipients.is_empty()),
130                AlertAction::Webhook {
131                    url,
132                    payload_template,
133                } => {
134                    assert!(!url.is_empty());
135                    assert!(!payload_template.is_empty());
136                }
137                AlertAction::BlockIp { duration } => assert!(duration > chrono::Duration::zero()),
138                _ => {} // Other variants are valid
139            }
140        }
141    }
142
143    #[tokio::test]
144    async fn test_security_monitor_creation() {
145        let config = SecurityMonitorConfig {
146            max_events_in_memory: 1000,
147            enable_realtime: true,
148            enable_alerts: false,
149            ..Default::default()
150        };
151
152        let monitor = SecurityMonitor::new(config);
153
154        // Test basic event recording
155        let event = SecurityEvent::new(
156            SecurityEventType::AuthSuccess,
157            SecuritySeverity::Low,
158            "Test authentication success".to_string(),
159        );
160
161        monitor.record_event(event).await;
162        // Should not panic or error
163    }
164
165    #[test]
166    fn test_default_alert_rules() {
167        let rules = create_default_alert_rules();
168        assert!(!rules.is_empty());
169
170        for rule in rules {
171            assert!(!rule.name.is_empty());
172            assert!(!rule.description.is_empty());
173            assert!(!rule.actions.is_empty());
174
175            // Verify threshold is reasonable
176            match rule.threshold {
177                AlertThreshold::Count(count) => assert!(count > 0 && count < 1000),
178                AlertThreshold::Rate { count, duration } => {
179                    assert!(count > 0 && count < 1000);
180                    assert!(duration >= chrono::Duration::minutes(1));
181                    assert!(duration <= chrono::Duration::hours(24));
182                }
183                AlertThreshold::Percentage {
184                    threshold: percentage,
185                    ..
186                } => {
187                    assert!((0.0..=100.0).contains(&percentage));
188                }
189            }
190        }
191    }
192
193    #[test]
194    fn test_security_metrics() {
195        let now = chrono::Utc::now();
196        let metrics = SecurityMetrics {
197            period_start: now - chrono::Duration::hours(1),
198            period_end: now,
199            auth_success_count: 80,
200            auth_failure_count: 20,
201            invalid_api_key_count: 5,
202            expired_token_count: 3,
203            sessions_created: 15,
204            sessions_expired: 2,
205            sessions_terminated: 1,
206            active_sessions: 25,
207            injection_attempts: 0,
208            size_limit_violations: 1,
209            rate_limit_violations: 5,
210            unauthorized_access_attempts: 2,
211            permission_denied_count: 3,
212            role_escalation_attempts: 0,
213            top_source_ips: vec![("192.168.1.1".to_string(), 50)],
214            top_user_agents: vec![("Mozilla/5.0".to_string(), 40)],
215            top_methods: vec![("POST".to_string(), 60)],
216            country_distribution: std::collections::HashMap::new(),
217        };
218
219        assert_eq!(metrics.auth_success_count, 80);
220        assert_eq!(metrics.auth_failure_count, 20);
221        assert_eq!(metrics.active_sessions, 25);
222        assert_eq!(metrics.sessions_created, 15);
223    }
224
225    #[test]
226    fn test_system_health() {
227        let health = SystemHealth {
228            events_in_memory: 1500,
229            active_alerts: 2,
230            last_event_time: Some(chrono::Utc::now()),
231            memory_usage_mb: 512,
232        };
233
234        assert_eq!(health.events_in_memory, 1500);
235        assert_eq!(health.active_alerts, 2);
236        assert!(health.last_event_time.is_some());
237        assert_eq!(health.memory_usage_mb, 512);
238    }
239
240    #[test]
241    fn test_monitoring_error_types() {
242        let errors = vec![
243            MonitoringError::AlertNotFound {
244                alert_id: "test-alert".to_string(),
245            },
246            MonitoringError::MetricNotFound {
247                metric_name: "test-metric".to_string(),
248            },
249            MonitoringError::ConfigError {
250                reason: "test config error".to_string(),
251            },
252            MonitoringError::StorageError("test storage error".to_string()),
253            MonitoringError::SerializationError("test serialization error".to_string()),
254        ];
255
256        for error in errors {
257            let error_string = error.to_string();
258            assert!(!error_string.is_empty());
259            assert!(error_string.len() > 5);
260        }
261    }
262
263    #[tokio::test]
264    async fn test_security_dashboard_integration() {
265        let config = SecurityMonitorConfig {
266            max_events_in_memory: 1000,
267            enable_realtime: true,
268            enable_alerts: true,
269            ..Default::default()
270        };
271
272        let monitor = SecurityMonitor::new(config);
273
274        // Record some events
275        let events = vec![
276            SecurityEvent::new(
277                SecurityEventType::AuthSuccess,
278                SecuritySeverity::Low,
279                "Auth 1".to_string(),
280            ),
281            SecurityEvent::new(
282                SecurityEventType::AuthSuccess,
283                SecuritySeverity::Low,
284                "Auth 2".to_string(),
285            ),
286            SecurityEvent::new(
287                SecurityEventType::AuthFailure,
288                SecuritySeverity::Medium,
289                "Failed auth".to_string(),
290            ),
291            SecurityEvent::new(
292                SecurityEventType::RateLimit,
293                SecuritySeverity::High,
294                "Rate limit".to_string(),
295            ),
296        ];
297
298        for event in events {
299            monitor.record_event(event).await;
300        }
301
302        // Get dashboard data
303        let dashboard_data = monitor.get_dashboard_data().await;
304
305        // Verify dashboard contains expected data
306        assert!(dashboard_data.hourly_metrics.auth_success_count >= 2);
307        assert!(dashboard_data.hourly_metrics.auth_failure_count >= 1);
308        assert!(dashboard_data.hourly_metrics.rate_limit_violations >= 1);
309
310        // System health should be populated
311        assert!(dashboard_data.system_health.events_in_memory >= 4);
312        // Memory usage is u64, so always >= 0 - remove redundant check
313    }
314
315    #[test]
316    fn test_monitoring_config_defaults() {
317        let config = SecurityMonitorConfig::default();
318
319        // Defaults should be reasonable
320        assert!(config.max_events_in_memory > 0);
321        assert!(config.max_alerts_in_memory > 0);
322        assert!(config.event_retention > chrono::Duration::zero());
323        assert!(config.alert_retention > chrono::Duration::zero());
324        assert!(config.metrics_interval > chrono::Duration::zero());
325    }
326}