pulseengine_mcp_auth/monitoring/
mod.rs1pub mod dashboard_server;
7pub mod security_monitor;
8
9pub use security_monitor::{
10 AlertAction, AlertRule, AlertThreshold, MonitoringError, SecurityAlert, SecurityDashboard,
11 SecurityEvent, SecurityEventType, SecurityMetrics, SecurityMonitor, SecurityMonitorConfig,
12 SystemHealth, create_default_alert_rules,
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 let config = SecurityMonitorConfig::default();
25 assert!(config.max_events_in_memory > 0); 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 _ => {} }
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 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 }
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 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 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 let dashboard_data = monitor.get_dashboard_data().await;
304
305 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 assert!(dashboard_data.system_health.events_in_memory >= 4);
312 }
314
315 #[test]
316 fn test_monitoring_config_defaults() {
317 let config = SecurityMonitorConfig::default();
318
319 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}