1use crate::error::{ClusterError, Result};
6use dashmap::DashMap;
7use parking_lot::RwLock;
8use serde::{Deserialize, Serialize};
9use std::collections::{HashMap, HashSet};
10use std::sync::Arc;
11use std::time::{Duration, SystemTime};
12
13pub type UserId = String;
15
16pub type RoleId = String;
18
19pub type Permission = String;
21
22pub type Token = String;
24
25pub struct SecurityManager {
27 users: Arc<DashMap<UserId, User>>,
29 roles: Arc<DashMap<RoleId, Role>>,
31 sessions: Arc<DashMap<Token, Session>>,
33 audit_log: Arc<RwLock<Vec<AuditEntry>>>,
35 secrets: Arc<RwLock<HashMap<String, Secret>>>,
37 stats: Arc<RwLock<SecurityStats>>,
39}
40
41#[derive(Debug, Clone, Serialize, Deserialize)]
43pub struct User {
44 pub id: UserId,
46 pub username: String,
48 pub email: Option<String>,
50 pub roles: Vec<RoleId>,
52 pub created_at: SystemTime,
54 pub last_login: Option<SystemTime>,
56 pub enabled: bool,
58}
59
60#[derive(Debug, Clone, Serialize, Deserialize)]
62pub struct Role {
63 pub id: RoleId,
65 pub name: String,
67 pub description: Option<String>,
69 pub permissions: HashSet<Permission>,
71}
72
73#[derive(Debug, Clone)]
75pub struct Session {
76 pub token: Token,
78 pub user_id: UserId,
80 pub created_at: SystemTime,
82 pub expires_at: SystemTime,
84 pub ip_address: Option<String>,
86}
87
88#[derive(Debug, Clone, Serialize, Deserialize)]
90pub struct AuditEntry {
91 pub timestamp: SystemTime,
93 pub user_id: Option<UserId>,
95 pub action: String,
97 pub resource: String,
99 pub result: AuditResult,
101 pub details: Option<String>,
103}
104
105#[derive(Debug, Clone, Serialize, Deserialize)]
107pub enum AuditResult {
108 Success,
110 Failure,
112 Denied,
114}
115
116#[derive(Debug, Clone, Serialize, Deserialize)]
118pub struct Secret {
119 pub key: String,
121 pub value: String,
123 pub created_at: SystemTime,
125 pub expires_at: Option<SystemTime>,
127}
128
129#[derive(Debug, Clone, Default, Serialize, Deserialize)]
131pub struct SecurityStats {
132 pub total_users: usize,
134 pub active_sessions: usize,
136 pub total_logins: u64,
138 pub failed_logins: u64,
140 pub total_audit_entries: u64,
142 pub authorization_denials: u64,
144}
145
146impl SecurityManager {
147 pub fn new() -> Self {
149 let manager = Self {
150 users: Arc::new(DashMap::new()),
151 roles: Arc::new(DashMap::new()),
152 sessions: Arc::new(DashMap::new()),
153 audit_log: Arc::new(RwLock::new(Vec::new())),
154 secrets: Arc::new(RwLock::new(HashMap::new())),
155 stats: Arc::new(RwLock::new(SecurityStats::default())),
156 };
157
158 manager.create_default_roles();
160
161 manager
162 }
163
164 fn create_default_roles(&self) {
165 let admin_permissions: HashSet<Permission> = vec![
167 "cluster:*".to_string(),
168 "task:*".to_string(),
169 "worker:*".to_string(),
170 "user:*".to_string(),
171 ]
172 .into_iter()
173 .collect();
174
175 self.roles.insert(
176 "admin".to_string(),
177 Role {
178 id: "admin".to_string(),
179 name: "Administrator".to_string(),
180 description: Some("Full cluster access".to_string()),
181 permissions: admin_permissions,
182 },
183 );
184
185 let operator_permissions: HashSet<Permission> = vec![
187 "cluster:read".to_string(),
188 "task:*".to_string(),
189 "worker:read".to_string(),
190 ]
191 .into_iter()
192 .collect();
193
194 self.roles.insert(
195 "operator".to_string(),
196 Role {
197 id: "operator".to_string(),
198 name: "Operator".to_string(),
199 description: Some("Task and cluster operations".to_string()),
200 permissions: operator_permissions,
201 },
202 );
203
204 let user_permissions: HashSet<Permission> = vec![
206 "cluster:read".to_string(),
207 "task:create".to_string(),
208 "task:read".to_string(),
209 "task:cancel".to_string(),
210 ]
211 .into_iter()
212 .collect();
213
214 self.roles.insert(
215 "user".to_string(),
216 Role {
217 id: "user".to_string(),
218 name: "User".to_string(),
219 description: Some("Basic user access".to_string()),
220 permissions: user_permissions,
221 },
222 );
223 }
224
225 pub fn create_user(
227 &self,
228 username: String,
229 email: Option<String>,
230 roles: Vec<RoleId>,
231 ) -> Result<UserId> {
232 let user_id = uuid::Uuid::new_v4().to_string();
233
234 let user = User {
235 id: user_id.clone(),
236 username,
237 email,
238 roles,
239 created_at: SystemTime::now(),
240 last_login: None,
241 enabled: true,
242 };
243
244 self.users.insert(user_id.clone(), user);
245
246 {
247 let mut stats = self.stats.write();
248 stats.total_users = self.users.len();
249 } self.audit(
252 "system".to_string(),
253 "user:create".to_string(),
254 user_id.clone(),
255 AuditResult::Success,
256 None,
257 );
258
259 Ok(user_id)
260 }
261
262 pub fn authenticate(&self, user_id: &UserId, _credentials: &str) -> Result<Token> {
264 let mut user = self
267 .users
268 .get_mut(user_id)
269 .ok_or_else(|| ClusterError::AuthenticationFailed("User not found".to_string()))?;
270
271 if !user.enabled {
272 let mut stats = self.stats.write();
273 stats.failed_logins += 1;
274 return Err(ClusterError::AuthenticationFailed(
275 "User disabled".to_string(),
276 ));
277 }
278
279 user.last_login = Some(SystemTime::now());
280
281 let token = uuid::Uuid::new_v4().to_string();
282 let session = Session {
283 token: token.clone(),
284 user_id: user_id.clone(),
285 created_at: SystemTime::now(),
286 expires_at: SystemTime::now() + Duration::from_secs(3600), ip_address: None,
288 };
289
290 self.sessions.insert(token.clone(), session);
291
292 {
293 let mut stats = self.stats.write();
294 stats.total_logins += 1;
295 stats.active_sessions = self.sessions.len();
296 } self.audit(
299 user_id.clone(),
300 "auth:login".to_string(),
301 user_id.clone(),
302 AuditResult::Success,
303 None,
304 );
305
306 Ok(token)
307 }
308
309 pub fn logout(&self, token: &Token) -> Result<()> {
311 if let Some((_, session)) = self.sessions.remove(token) {
312 self.audit(
313 session.user_id.clone(),
314 "auth:logout".to_string(),
315 session.user_id,
316 AuditResult::Success,
317 None,
318 );
319
320 let mut stats = self.stats.write();
321 stats.active_sessions = self.sessions.len();
322 }
323
324 Ok(())
325 }
326
327 pub fn validate_session(&self, token: &Token) -> Result<UserId> {
329 let session = self
330 .sessions
331 .get(token)
332 .ok_or_else(|| ClusterError::AuthenticationFailed("Invalid session".to_string()))?;
333
334 if SystemTime::now() > session.expires_at {
335 self.sessions.remove(token);
336 return Err(ClusterError::AuthenticationFailed(
337 "Session expired".to_string(),
338 ));
339 }
340
341 Ok(session.user_id.clone())
342 }
343
344 pub fn check_permission(&self, user_id: &UserId, permission: &Permission) -> Result<bool> {
346 let user = self
347 .users
348 .get(user_id)
349 .ok_or_else(|| ClusterError::AuthenticationFailed("User not found".to_string()))?;
350
351 for role_id in &user.roles {
353 if let Some(role) = self.roles.get(role_id) {
354 let prefix = permission.split(':').next().unwrap_or("");
357 if role.permissions.contains(&format!("{}:*", prefix)) {
358 return Ok(true);
359 }
360
361 if role.permissions.contains(permission) {
363 return Ok(true);
364 }
365
366 if role.permissions.contains("*") {
368 return Ok(true);
369 }
370 }
371 }
372
373 {
374 let mut stats = self.stats.write();
375 stats.authorization_denials += 1;
376 } self.audit(
379 user_id.clone(),
380 permission.clone(),
381 user_id.clone(),
382 AuditResult::Denied,
383 Some(format!("Missing permission: {}", permission)),
384 );
385
386 Ok(false)
387 }
388
389 pub fn require_permission(&self, user_id: &UserId, permission: &Permission) -> Result<()> {
391 if self.check_permission(user_id, permission)? {
392 Ok(())
393 } else {
394 Err(ClusterError::PermissionDenied(permission.clone()))
395 }
396 }
397
398 pub fn create_role(
400 &self,
401 id: RoleId,
402 name: String,
403 permissions: HashSet<Permission>,
404 ) -> Result<()> {
405 let role = Role {
406 id: id.clone(),
407 name,
408 description: None,
409 permissions,
410 };
411
412 self.roles.insert(id, role);
413
414 Ok(())
415 }
416
417 pub fn assign_role(&self, user_id: &UserId, role_id: &RoleId) -> Result<()> {
419 let mut user = self
420 .users
421 .get_mut(user_id)
422 .ok_or_else(|| ClusterError::AuthenticationFailed("User not found".to_string()))?;
423
424 if !user.roles.contains(role_id) {
425 user.roles.push(role_id.clone());
426 }
427
428 self.audit(
429 user_id.clone(),
430 "user:assign_role".to_string(),
431 user_id.clone(),
432 AuditResult::Success,
433 Some(format!("Role: {}", role_id)),
434 );
435
436 Ok(())
437 }
438
439 pub fn store_secret(
441 &self,
442 key: String,
443 value: String,
444 expires_at: Option<SystemTime>,
445 ) -> Result<()> {
446 let secret = Secret {
447 key: key.clone(),
448 value, created_at: SystemTime::now(),
450 expires_at,
451 };
452
453 self.secrets.write().insert(key, secret);
454
455 Ok(())
456 }
457
458 pub fn get_secret(&self, key: &str) -> Result<String> {
460 let secrets = self.secrets.read();
461 let secret = secrets
462 .get(key)
463 .ok_or_else(|| ClusterError::SecretNotFound(key.to_string()))?;
464
465 if let Some(expires) = secret.expires_at {
467 if SystemTime::now() > expires {
468 return Err(ClusterError::SecretNotFound("Secret expired".to_string()));
469 }
470 }
471
472 Ok(secret.value.clone())
473 }
474
475 pub fn audit(
477 &self,
478 user_id: UserId,
479 action: String,
480 resource: String,
481 result: AuditResult,
482 details: Option<String>,
483 ) {
484 let entry = AuditEntry {
485 timestamp: SystemTime::now(),
486 user_id: Some(user_id),
487 action,
488 resource,
489 result,
490 details,
491 };
492
493 self.audit_log.write().push(entry);
494
495 let mut stats = self.stats.write();
496 stats.total_audit_entries += 1;
497 }
498
499 pub fn get_audit_log(&self, limit: usize) -> Vec<AuditEntry> {
501 let log = self.audit_log.read();
502 log.iter().rev().take(limit).cloned().collect()
503 }
504
505 pub fn get_stats(&self) -> SecurityStats {
507 self.stats.read().clone()
508 }
509}
510
511impl Default for SecurityManager {
512 fn default() -> Self {
513 Self::new()
514 }
515}
516
517#[cfg(test)]
518#[allow(clippy::expect_used, clippy::unwrap_used)]
519mod tests {
520 use super::*;
521
522 #[test]
523 fn test_user_creation() {
524 let manager = SecurityManager::new();
525 let result = manager.create_user(
526 "testuser".to_string(),
527 Some("test@example.com".to_string()),
528 vec!["user".to_string()],
529 );
530
531 assert!(result.is_ok());
532 let stats = manager.get_stats();
533 assert_eq!(stats.total_users, 1);
534 }
535
536 #[test]
537 fn test_authentication() {
538 let manager = SecurityManager::new();
539 let user_id = manager
540 .create_user("testuser".to_string(), None, vec!["user".to_string()])
541 .expect("user creation should succeed");
542
543 let token = manager
544 .authenticate(&user_id, "password")
545 .expect("authentication should succeed");
546 assert!(!token.is_empty());
547
548 let validated_user = manager.validate_session(&token);
549 assert!(validated_user.is_ok());
550 assert_eq!(
551 validated_user.expect("session validation should succeed"),
552 user_id
553 );
554 }
555
556 #[test]
557 fn test_authorization() {
558 let manager = SecurityManager::new();
559 let user_id = manager
560 .create_user("testuser".to_string(), None, vec!["user".to_string()])
561 .expect("user creation should succeed");
562
563 let has_perm = manager
565 .check_permission(&user_id, &"task:create".to_string())
566 .expect("permission check should succeed");
567 assert!(has_perm);
568
569 let has_perm = manager
571 .check_permission(&user_id, &"worker:delete".to_string())
572 .expect("permission check should succeed");
573 assert!(!has_perm);
574 }
575
576 #[test]
577 fn test_secret_management() {
578 let manager = SecurityManager::new();
579
580 manager
581 .store_secret("api_key".to_string(), "secret123".to_string(), None)
582 .ok();
583
584 let secret = manager.get_secret("api_key");
585 assert!(secret.is_ok());
586 assert_eq!(
587 secret.expect("secret retrieval should succeed"),
588 "secret123"
589 );
590 }
591}