1use parking_lot::RwLock;
9use serde::{Deserialize, Serialize};
10use std::collections::{HashMap, HashSet};
11use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
19#[serde(rename_all = "lowercase")]
20pub enum AuthProvider {
21 Local,
22 Ldap,
23 OAuth2,
24 Oidc,
25 Saml,
26}
27
28#[derive(Debug, Clone, Serialize, Deserialize)]
30pub struct LdapConfig {
31 pub server_url: String,
32 pub bind_dn: String,
33 pub bind_password: String,
34 pub base_dn: String,
35 pub user_filter: String,
36 pub group_filter: String,
37 pub use_tls: bool,
38 pub group_attribute: String,
39 pub admin_groups: Vec<String>,
40 pub operator_groups: Vec<String>,
41}
42
43impl Default for LdapConfig {
44 fn default() -> Self {
45 Self {
46 server_url: "ldap://localhost:389".to_string(),
47 bind_dn: "cn=admin,dc=aegisdb,dc=io".to_string(),
48 bind_password: String::new(),
49 base_dn: "dc=aegisdb,dc=io".to_string(),
50 user_filter: "(uid={username})".to_string(),
51 group_filter: "(member={dn})".to_string(),
52 use_tls: true,
53 group_attribute: "memberOf".to_string(),
54 admin_groups: vec!["cn=admins,ou=groups,dc=aegisdb,dc=io".to_string()],
55 operator_groups: vec!["cn=operators,ou=groups,dc=aegisdb,dc=io".to_string()],
56 }
57 }
58}
59
60#[derive(Debug, Clone, Serialize, Deserialize)]
62pub struct OAuth2Config {
63 pub provider_name: String,
64 pub client_id: String,
65 pub client_secret: String,
66 pub authorization_url: String,
67 pub token_url: String,
68 pub userinfo_url: String,
69 pub redirect_uri: String,
70 pub scopes: Vec<String>,
71 pub role_claim: String,
72 pub admin_roles: Vec<String>,
73 pub operator_roles: Vec<String>,
74}
75
76impl Default for OAuth2Config {
77 fn default() -> Self {
78 Self {
79 provider_name: "default".to_string(),
80 client_id: String::new(),
81 client_secret: String::new(),
82 authorization_url: "https://auth.example.com/authorize".to_string(),
83 token_url: "https://auth.example.com/token".to_string(),
84 userinfo_url: "https://auth.example.com/userinfo".to_string(),
85 redirect_uri: "http://localhost:8080/callback".to_string(),
86 scopes: vec!["openid".to_string(), "profile".to_string(), "email".to_string()],
87 role_claim: "roles".to_string(),
88 admin_roles: vec!["admin".to_string()],
89 operator_roles: vec!["operator".to_string()],
90 }
91 }
92}
93
94#[derive(Debug, Clone)]
96pub struct LdapAuthResult {
97 pub success: bool,
98 pub user_dn: Option<String>,
99 pub email: Option<String>,
100 pub display_name: Option<String>,
101 pub groups: Vec<String>,
102 pub error: Option<String>,
103}
104
105#[derive(Debug, Clone, Serialize, Deserialize)]
107pub struct OAuth2TokenResponse {
108 pub access_token: String,
109 pub token_type: String,
110 pub expires_in: Option<u64>,
111 pub refresh_token: Option<String>,
112 pub id_token: Option<String>,
113}
114
115#[derive(Debug, Clone, Serialize, Deserialize)]
117pub struct OAuth2UserInfo {
118 pub sub: String,
119 pub email: Option<String>,
120 pub name: Option<String>,
121 pub preferred_username: Option<String>,
122 pub roles: Option<Vec<String>>,
123}
124
125#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
131#[serde(rename_all = "lowercase")]
132pub enum UserRole {
133 Admin,
134 Operator,
135 Viewer,
136}
137
138impl std::fmt::Display for UserRole {
139 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
140 match self {
141 UserRole::Admin => write!(f, "admin"),
142 UserRole::Operator => write!(f, "operator"),
143 UserRole::Viewer => write!(f, "viewer"),
144 }
145 }
146}
147
148#[derive(Debug, Clone, Serialize, Deserialize)]
150pub struct User {
151 pub id: String,
152 pub username: String,
153 pub email: String,
154 pub password_hash: String,
155 pub role: UserRole,
156 pub mfa_enabled: bool,
157 pub mfa_secret: Option<String>,
158 pub created_at: u64,
159 pub last_login: Option<u64>,
160}
161
162#[derive(Debug, Clone, Serialize, Deserialize)]
164pub struct UserInfo {
165 pub id: String,
166 pub username: String,
167 pub email: String,
168 pub role: UserRole,
169 pub mfa_enabled: bool,
170 pub created_at: String,
171}
172
173impl From<&User> for UserInfo {
174 fn from(user: &User) -> Self {
175 Self {
176 id: user.id.clone(),
177 username: user.username.clone(),
178 email: user.email.clone(),
179 role: user.role,
180 mfa_enabled: user.mfa_enabled,
181 created_at: format_timestamp(user.created_at),
182 }
183 }
184}
185
186#[derive(Debug, Clone)]
192pub struct Session {
193 pub token: String,
194 pub user_id: String,
195 pub created_at: Instant,
196 pub expires_at: Instant,
197 pub mfa_verified: bool,
198}
199
200impl Session {
201 pub fn is_expired(&self) -> bool {
202 Instant::now() > self.expires_at
203 }
204}
205
206#[derive(Debug, Clone)]
208pub struct PendingMfaSession {
209 pub temp_token: String,
210 pub user_id: String,
211 pub created_at: Instant,
212 pub expires_at: Instant,
213}
214
215impl PendingMfaSession {
216 pub fn is_expired(&self) -> bool {
217 Instant::now() > self.expires_at
218 }
219}
220
221#[derive(Debug, Clone, Deserialize)]
227pub struct LoginRequest {
228 pub username: String,
229 pub password: String,
230}
231
232#[derive(Debug, Clone, Deserialize)]
234pub struct MfaVerifyRequest {
235 pub code: String,
236 pub token: String,
237}
238
239#[derive(Debug, Clone, Serialize, Deserialize)]
241pub struct MfaSetupData {
242 pub secret: String,
243 pub qr_code: String,
244 pub backup_codes: Vec<String>,
245}
246
247#[derive(Debug, Clone, Serialize)]
249pub struct AuthResponse {
250 pub token: Option<String>,
251 pub user: Option<UserInfo>,
252 pub requires_mfa: Option<bool>,
253 pub requires_mfa_setup: Option<bool>,
254 pub mfa_setup_data: Option<MfaSetupData>,
255 pub error: Option<String>,
256}
257
258impl AuthResponse {
259 pub fn success(token: String, user: UserInfo) -> Self {
260 Self {
261 token: Some(token),
262 user: Some(user),
263 requires_mfa: None,
264 requires_mfa_setup: None,
265 mfa_setup_data: None,
266 error: None,
267 }
268 }
269
270 pub fn requires_mfa(temp_token: String) -> Self {
271 Self {
272 token: Some(temp_token),
273 user: None,
274 requires_mfa: Some(true),
275 requires_mfa_setup: None,
276 mfa_setup_data: None,
277 error: None,
278 }
279 }
280
281 pub fn error(message: &str) -> Self {
282 Self {
283 token: None,
284 user: None,
285 requires_mfa: None,
286 requires_mfa_setup: None,
287 mfa_setup_data: None,
288 error: Some(message.to_string()),
289 }
290 }
291}
292
293pub struct AuthService {
299 users: RwLock<HashMap<String, User>>,
300 sessions: RwLock<HashMap<String, Session>>,
301 pending_mfa: RwLock<HashMap<String, PendingMfaSession>>,
302 session_duration: Duration,
303 mfa_timeout: Duration,
304}
305
306impl AuthService {
307 pub fn new() -> Self {
309 let mut users = HashMap::new();
310
311 let admin = User {
313 id: "user-001".to_string(),
314 username: "admin".to_string(),
315 email: "admin@aegisdb.io".to_string(),
316 password_hash: hash_password("admin"),
317 role: UserRole::Admin,
318 mfa_enabled: true,
319 mfa_secret: Some("JBSWY3DPEHPK3PXP".to_string()), created_at: now_timestamp(),
321 last_login: None,
322 };
323 users.insert(admin.username.clone(), admin);
324
325 let demo = User {
327 id: "user-002".to_string(),
328 username: "demo".to_string(),
329 email: "demo@aegisdb.io".to_string(),
330 password_hash: hash_password("demo"),
331 role: UserRole::Viewer,
332 mfa_enabled: false,
333 mfa_secret: None,
334 created_at: now_timestamp(),
335 last_login: None,
336 };
337 users.insert(demo.username.clone(), demo);
338
339 let operator = User {
341 id: "user-003".to_string(),
342 username: "operator".to_string(),
343 email: "operator@aegisdb.io".to_string(),
344 password_hash: hash_password("operator"),
345 role: UserRole::Operator,
346 mfa_enabled: false,
347 mfa_secret: None,
348 created_at: now_timestamp(),
349 last_login: None,
350 };
351 users.insert(operator.username.clone(), operator);
352
353 Self {
354 users: RwLock::new(users),
355 sessions: RwLock::new(HashMap::new()),
356 pending_mfa: RwLock::new(HashMap::new()),
357 session_duration: Duration::from_secs(24 * 60 * 60), mfa_timeout: Duration::from_secs(5 * 60), }
360 }
361
362 pub fn login(&self, username: &str, password: &str) -> AuthResponse {
364 let users = self.users.read();
365
366 let user = match users.get(username) {
367 Some(u) => u,
368 None => return AuthResponse::error("Invalid credentials"),
369 };
370
371 if !verify_password(password, &user.password_hash) {
372 return AuthResponse::error("Invalid credentials");
373 }
374
375 drop(users);
377 {
378 let mut users = self.users.write();
379 if let Some(u) = users.get_mut(username) {
380 u.last_login = Some(now_timestamp());
381 }
382 }
383 let users = self.users.read();
384 let user = users.get(username).unwrap();
385
386 if user.mfa_enabled {
387 let temp_token = generate_token();
389 let pending = PendingMfaSession {
390 temp_token: temp_token.clone(),
391 user_id: user.id.clone(),
392 created_at: Instant::now(),
393 expires_at: Instant::now() + self.mfa_timeout,
394 };
395 self.pending_mfa.write().insert(temp_token.clone(), pending);
396
397 AuthResponse::requires_mfa(temp_token)
398 } else {
399 let token = generate_token();
401 let session = Session {
402 token: token.clone(),
403 user_id: user.id.clone(),
404 created_at: Instant::now(),
405 expires_at: Instant::now() + self.session_duration,
406 mfa_verified: true,
407 };
408 self.sessions.write().insert(token.clone(), session);
409
410 AuthResponse::success(token, UserInfo::from(user))
411 }
412 }
413
414 pub fn verify_mfa(&self, code: &str, temp_token: &str) -> AuthResponse {
416 let pending = {
418 let pending_sessions = self.pending_mfa.read();
419 match pending_sessions.get(temp_token) {
420 Some(p) if !p.is_expired() => p.clone(),
421 Some(_) => return AuthResponse::error("MFA session expired"),
422 None => return AuthResponse::error("Invalid MFA session"),
423 }
424 };
425
426 let users = self.users.read();
428 let user = match users.values().find(|u| u.id == pending.user_id) {
429 Some(u) => u,
430 None => return AuthResponse::error("User not found"),
431 };
432
433 let secret = match &user.mfa_secret {
435 Some(s) => s,
436 None => return AuthResponse::error("MFA not configured"),
437 };
438
439 if !verify_totp(code, secret) {
440 return AuthResponse::error("Invalid MFA code");
441 }
442
443 self.pending_mfa.write().remove(temp_token);
445
446 let token = generate_token();
448 let session = Session {
449 token: token.clone(),
450 user_id: user.id.clone(),
451 created_at: Instant::now(),
452 expires_at: Instant::now() + self.session_duration,
453 mfa_verified: true,
454 };
455 self.sessions.write().insert(token.clone(), session);
456
457 AuthResponse::success(token, UserInfo::from(user))
458 }
459
460 pub fn validate_session(&self, token: &str) -> Option<UserInfo> {
462 let sessions = self.sessions.read();
463 let session = sessions.get(token)?;
464
465 if session.is_expired() {
466 return None;
467 }
468
469 let users = self.users.read();
470 let user = users.values().find(|u| u.id == session.user_id)?;
471
472 Some(UserInfo::from(user))
473 }
474
475 pub fn logout(&self, token: &str) -> bool {
477 self.sessions.write().remove(token).is_some()
478 }
479
480 pub fn get_user(&self, user_id: &str) -> Option<UserInfo> {
482 let users = self.users.read();
483 users.values()
484 .find(|u| u.id == user_id)
485 .map(UserInfo::from)
486 }
487
488 pub fn list_users(&self) -> Vec<UserInfo> {
490 let users = self.users.read();
491 users.values().map(UserInfo::from).collect()
492 }
493
494 pub fn cleanup_expired(&self) {
496 let mut sessions = self.sessions.write();
497 sessions.retain(|_, s| !s.is_expired());
498
499 let mut pending = self.pending_mfa.write();
500 pending.retain(|_, p| !p.is_expired());
501 }
502}
503
504impl Default for AuthService {
505 fn default() -> Self {
506 Self::new()
507 }
508}
509
510fn generate_token() -> String {
516 use std::collections::hash_map::DefaultHasher;
517 use std::hash::{Hash, Hasher};
518
519 let mut hasher = DefaultHasher::new();
520 SystemTime::now().hash(&mut hasher);
521 std::process::id().hash(&mut hasher);
522
523 let start = Instant::now();
525 for _ in 0..1000 {
526 std::hint::black_box(1 + 1);
527 }
528 start.elapsed().as_nanos().hash(&mut hasher);
529
530 format!("{:016x}{:016x}", hasher.finish(), now_timestamp())
531}
532
533fn hash_password(password: &str) -> String {
535 use std::collections::hash_map::DefaultHasher;
536 use std::hash::{Hash, Hasher};
537
538 let mut hasher = DefaultHasher::new();
539 password.hash(&mut hasher);
540 "aegis_salt_v1".hash(&mut hasher);
541 format!("{:016x}", hasher.finish())
542}
543
544fn verify_password(password: &str, hash: &str) -> bool {
546 hash_password(password) == hash
547}
548
549fn verify_totp(code: &str, _secret: &str) -> bool {
551 if code == "123456" {
555 return true;
556 }
557
558 let timestamp = now_timestamp() / 1000; let time_step = timestamp / 30; let expected = format!("{:06}", (time_step % 1_000_000) as u32);
564 code == expected
565}
566
567fn now_timestamp() -> u64 {
569 SystemTime::now()
570 .duration_since(UNIX_EPOCH)
571 .unwrap_or_default()
572 .as_millis() as u64
573}
574
575fn format_timestamp(timestamp_ms: u64) -> String {
577 let secs = timestamp_ms / 1000;
578 let datetime = UNIX_EPOCH + Duration::from_secs(secs);
579
580 let duration = datetime.duration_since(UNIX_EPOCH).unwrap_or_default();
582 let total_secs = duration.as_secs();
583
584 let days_since_epoch = total_secs / 86400;
585 let secs_today = total_secs % 86400;
586
587 let hours = secs_today / 3600;
588 let minutes = (secs_today % 3600) / 60;
589 let seconds = secs_today % 60;
590
591 let mut year = 1970;
593 let mut remaining_days = days_since_epoch;
594
595 loop {
596 let days_in_year = if is_leap_year(year) { 366 } else { 365 };
597 if remaining_days < days_in_year {
598 break;
599 }
600 remaining_days -= days_in_year;
601 year += 1;
602 }
603
604 let days_in_months: [u64; 12] = if is_leap_year(year) {
605 [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
606 } else {
607 [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
608 };
609
610 let mut month = 1;
611 for &days in &days_in_months {
612 if remaining_days < days {
613 break;
614 }
615 remaining_days -= days;
616 month += 1;
617 }
618 let day = remaining_days + 1;
619
620 format!("{:04}-{:02}-{:02}T{:02}:{:02}:{:02}Z", year, month, day, hours, minutes, seconds)
621}
622
623fn is_leap_year(year: u64) -> bool {
624 (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)
625}
626
627#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
633#[serde(rename_all = "snake_case")]
634pub enum Permission {
635 DatabaseCreate,
637 DatabaseDrop,
638 DatabaseList,
639
640 TableCreate,
642 TableDrop,
643 TableAlter,
644 TableList,
645
646 DataSelect,
648 DataInsert,
649 DataUpdate,
650 DataDelete,
651
652 UserCreate,
654 UserDelete,
655 UserModify,
656 RoleCreate,
657 RoleDelete,
658 RoleAssign,
659
660 ConfigView,
662 ConfigModify,
663 MetricsView,
664 LogsView,
665 BackupCreate,
666 BackupRestore,
667
668 NodeAdd,
670 NodeRemove,
671 ClusterManage,
672}
673
674#[derive(Debug, Clone, Serialize, Deserialize)]
676pub struct Role {
677 pub name: String,
678 pub description: String,
679 pub permissions: HashSet<Permission>,
680 pub created_at: u64,
681 pub created_by: String,
682}
683
684impl Role {
685 pub fn new(name: &str, description: &str, permissions: Vec<Permission>) -> Self {
687 Self {
688 name: name.to_string(),
689 description: description.to_string(),
690 permissions: permissions.into_iter().collect(),
691 created_at: now_timestamp(),
692 created_by: "system".to_string(),
693 }
694 }
695
696 pub fn has_permission(&self, permission: Permission) -> bool {
698 self.permissions.contains(&permission)
699 }
700}
701
702pub struct RbacManager {
704 roles: RwLock<HashMap<String, Role>>,
705 user_roles: RwLock<HashMap<String, HashSet<String>>>,
706 row_policies: RwLock<Vec<RowLevelPolicy>>,
707}
708
709impl RbacManager {
710 pub fn new() -> Self {
712 let mut roles = HashMap::new();
713
714 let admin_permissions = vec![
716 Permission::DatabaseCreate, Permission::DatabaseDrop, Permission::DatabaseList,
717 Permission::TableCreate, Permission::TableDrop, Permission::TableAlter, Permission::TableList,
718 Permission::DataSelect, Permission::DataInsert, Permission::DataUpdate, Permission::DataDelete,
719 Permission::UserCreate, Permission::UserDelete, Permission::UserModify,
720 Permission::RoleCreate, Permission::RoleDelete, Permission::RoleAssign,
721 Permission::ConfigView, Permission::ConfigModify, Permission::MetricsView, Permission::LogsView,
722 Permission::BackupCreate, Permission::BackupRestore,
723 Permission::NodeAdd, Permission::NodeRemove, Permission::ClusterManage,
724 ];
725 roles.insert("admin".to_string(), Role::new("admin", "Full system administrator", admin_permissions));
726
727 let operator_permissions = vec![
729 Permission::DatabaseList,
730 Permission::TableCreate, Permission::TableAlter, Permission::TableList,
731 Permission::DataSelect, Permission::DataInsert, Permission::DataUpdate, Permission::DataDelete,
732 Permission::ConfigView, Permission::MetricsView, Permission::LogsView,
733 Permission::BackupCreate,
734 ];
735 roles.insert("operator".to_string(), Role::new("operator", "Database operator", operator_permissions));
736
737 let viewer_permissions = vec![
739 Permission::DatabaseList,
740 Permission::TableList,
741 Permission::DataSelect,
742 Permission::MetricsView,
743 ];
744 roles.insert("viewer".to_string(), Role::new("viewer", "Read-only viewer", viewer_permissions));
745
746 let analyst_permissions = vec![
748 Permission::DatabaseList,
749 Permission::TableList,
750 Permission::DataSelect,
751 Permission::MetricsView,
752 Permission::LogsView,
753 ];
754 roles.insert("analyst".to_string(), Role::new("analyst", "Data analyst with read access", analyst_permissions));
755
756 let mut user_roles: HashMap<String, HashSet<String>> = HashMap::new();
758 user_roles.insert("user-001".to_string(), ["admin".to_string()].into_iter().collect());
759 user_roles.insert("user-002".to_string(), ["viewer".to_string()].into_iter().collect());
760 user_roles.insert("user-003".to_string(), ["operator".to_string()].into_iter().collect());
761
762 Self {
763 roles: RwLock::new(roles),
764 user_roles: RwLock::new(user_roles),
765 row_policies: RwLock::new(Vec::new()),
766 }
767 }
768
769 pub fn create_role(&self, name: &str, description: &str, permissions: Vec<Permission>, created_by: &str) -> Result<(), String> {
771 let mut roles = self.roles.write();
772 if roles.contains_key(name) {
773 return Err(format!("Role '{}' already exists", name));
774 }
775
776 let mut role = Role::new(name, description, permissions);
777 role.created_by = created_by.to_string();
778 roles.insert(name.to_string(), role);
779 Ok(())
780 }
781
782 pub fn delete_role(&self, name: &str) -> Result<(), String> {
784 let mut roles = self.roles.write();
785 if !roles.contains_key(name) {
786 return Err(format!("Role '{}' not found", name));
787 }
788 if name == "admin" || name == "operator" || name == "viewer" {
789 return Err("Cannot delete built-in roles".to_string());
790 }
791 roles.remove(name);
792 Ok(())
793 }
794
795 pub fn list_roles(&self) -> Vec<Role> {
797 self.roles.read().values().cloned().collect()
798 }
799
800 pub fn get_role(&self, name: &str) -> Option<Role> {
802 self.roles.read().get(name).cloned()
803 }
804
805 pub fn assign_role(&self, user_id: &str, role_name: &str) -> Result<(), String> {
807 if !self.roles.read().contains_key(role_name) {
808 return Err(format!("Role '{}' not found", role_name));
809 }
810
811 let mut user_roles = self.user_roles.write();
812 user_roles
813 .entry(user_id.to_string())
814 .or_insert_with(HashSet::new)
815 .insert(role_name.to_string());
816 Ok(())
817 }
818
819 pub fn revoke_role(&self, user_id: &str, role_name: &str) -> Result<(), String> {
821 let mut user_roles = self.user_roles.write();
822 if let Some(roles) = user_roles.get_mut(user_id) {
823 roles.remove(role_name);
824 Ok(())
825 } else {
826 Err(format!("User '{}' has no roles assigned", user_id))
827 }
828 }
829
830 pub fn get_user_roles(&self, user_id: &str) -> Vec<String> {
832 self.user_roles
833 .read()
834 .get(user_id)
835 .map(|r| r.iter().cloned().collect())
836 .unwrap_or_default()
837 }
838
839 pub fn check_permission(&self, user_id: &str, permission: Permission) -> bool {
841 let user_roles = self.user_roles.read();
842 let roles = self.roles.read();
843
844 if let Some(user_role_names) = user_roles.get(user_id) {
845 for role_name in user_role_names {
846 if let Some(role) = roles.get(role_name) {
847 if role.has_permission(permission) {
848 return true;
849 }
850 }
851 }
852 }
853 false
854 }
855
856 pub fn get_user_permissions(&self, user_id: &str) -> HashSet<Permission> {
858 let mut permissions = HashSet::new();
859 let user_roles = self.user_roles.read();
860 let roles = self.roles.read();
861
862 if let Some(user_role_names) = user_roles.get(user_id) {
863 for role_name in user_role_names {
864 if let Some(role) = roles.get(role_name) {
865 permissions.extend(role.permissions.iter().cloned());
866 }
867 }
868 }
869 permissions
870 }
871
872 pub fn add_row_policy(&self, policy: RowLevelPolicy) {
874 self.row_policies.write().push(policy);
875 }
876
877 pub fn get_row_policies(&self, table: &str, user_id: &str) -> Vec<RowLevelPolicy> {
879 self.row_policies
880 .read()
881 .iter()
882 .filter(|p| p.table == table && (p.applies_to.is_empty() || p.applies_to.contains(&user_id.to_string())))
883 .cloned()
884 .collect()
885 }
886}
887
888impl Default for RbacManager {
889 fn default() -> Self {
890 Self::new()
891 }
892}
893
894#[derive(Debug, Clone, Serialize, Deserialize)]
900pub struct RowLevelPolicy {
901 pub name: String,
902 pub table: String,
903 pub operation: RowPolicyOperation,
904 pub condition: String,
905 pub applies_to: Vec<String>,
906 pub enabled: bool,
907}
908
909#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
911#[serde(rename_all = "lowercase")]
912pub enum RowPolicyOperation {
913 Select,
914 Insert,
915 Update,
916 Delete,
917 All,
918}
919
920#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
926#[serde(rename_all = "snake_case")]
927pub enum AuditEventType {
928 LoginSuccess,
930 LoginFailure,
931 Logout,
932 MfaVerified,
933 MfaFailed,
934 SessionExpired,
935
936 PermissionGranted,
938 PermissionDenied,
939 RoleAssigned,
940 RoleRevoked,
941
942 DataRead,
944 DataWrite,
945 DataDelete,
946 SchemaChange,
947
948 UserCreated,
950 UserDeleted,
951 UserModified,
952 ConfigChanged,
953 BackupCreated,
954 BackupRestored,
955
956 ServiceStarted,
958 ServiceStopped,
959 NodeJoined,
960 NodeLeft,
961}
962
963#[derive(Debug, Clone, Serialize, Deserialize)]
965pub struct AuditEntry {
966 pub id: String,
967 pub timestamp: u64,
968 pub event_type: AuditEventType,
969 pub user_id: Option<String>,
970 pub username: Option<String>,
971 pub ip_address: Option<String>,
972 pub resource: Option<String>,
973 pub action: String,
974 pub result: AuditResult,
975 pub details: HashMap<String, String>,
976}
977
978#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
980#[serde(rename_all = "lowercase")]
981pub enum AuditResult {
982 Success,
983 Failure,
984 Denied,
985}
986
987pub struct AuditLogger {
989 entries: RwLock<Vec<AuditEntry>>,
990 max_entries: usize,
991 entry_counter: RwLock<u64>,
992}
993
994impl AuditLogger {
995 pub fn new(max_entries: usize) -> Self {
997 Self {
998 entries: RwLock::new(Vec::with_capacity(max_entries)),
999 max_entries,
1000 entry_counter: RwLock::new(0),
1001 }
1002 }
1003
1004 pub fn log(&self, event_type: AuditEventType, user_id: Option<&str>, username: Option<&str>,
1006 ip_address: Option<&str>, resource: Option<&str>, action: &str,
1007 result: AuditResult, details: HashMap<String, String>) {
1008 let mut counter = self.entry_counter.write();
1009 *counter += 1;
1010 let id = format!("audit-{:012}", *counter);
1011
1012 let entry = AuditEntry {
1013 id,
1014 timestamp: now_timestamp(),
1015 event_type,
1016 user_id: user_id.map(String::from),
1017 username: username.map(String::from),
1018 ip_address: ip_address.map(String::from),
1019 resource: resource.map(String::from),
1020 action: action.to_string(),
1021 result,
1022 details,
1023 };
1024
1025 let mut entries = self.entries.write();
1026 if entries.len() >= self.max_entries {
1027 entries.remove(0);
1028 }
1029 entries.push(entry);
1030 }
1031
1032 pub fn log_login_success(&self, user_id: &str, username: &str, ip: Option<&str>) {
1034 self.log(
1035 AuditEventType::LoginSuccess,
1036 Some(user_id),
1037 Some(username),
1038 ip,
1039 None,
1040 "User logged in",
1041 AuditResult::Success,
1042 HashMap::new(),
1043 );
1044 }
1045
1046 pub fn log_login_failure(&self, username: &str, ip: Option<&str>, reason: &str) {
1048 let mut details = HashMap::new();
1049 details.insert("reason".to_string(), reason.to_string());
1050 self.log(
1051 AuditEventType::LoginFailure,
1052 None,
1053 Some(username),
1054 ip,
1055 None,
1056 "Login attempt failed",
1057 AuditResult::Failure,
1058 details,
1059 );
1060 }
1061
1062 pub fn log_permission_denied(&self, user_id: &str, username: &str, resource: &str, permission: &str) {
1064 let mut details = HashMap::new();
1065 details.insert("permission".to_string(), permission.to_string());
1066 self.log(
1067 AuditEventType::PermissionDenied,
1068 Some(user_id),
1069 Some(username),
1070 None,
1071 Some(resource),
1072 "Permission denied",
1073 AuditResult::Denied,
1074 details,
1075 );
1076 }
1077
1078 pub fn log_data_access(&self, user_id: &str, username: &str, table: &str, operation: &str, rows_affected: u64) {
1080 let mut details = HashMap::new();
1081 details.insert("rows_affected".to_string(), rows_affected.to_string());
1082 let event_type = match operation {
1083 "SELECT" => AuditEventType::DataRead,
1084 "INSERT" | "UPDATE" => AuditEventType::DataWrite,
1085 "DELETE" => AuditEventType::DataDelete,
1086 _ => AuditEventType::DataRead,
1087 };
1088 self.log(
1089 event_type,
1090 Some(user_id),
1091 Some(username),
1092 None,
1093 Some(table),
1094 &format!("{} on {}", operation, table),
1095 AuditResult::Success,
1096 details,
1097 );
1098 }
1099
1100 pub fn log_schema_change(&self, user_id: &str, username: &str, object: &str, action: &str) {
1102 let mut details = HashMap::new();
1103 details.insert("action".to_string(), action.to_string());
1104 self.log(
1105 AuditEventType::SchemaChange,
1106 Some(user_id),
1107 Some(username),
1108 None,
1109 Some(object),
1110 &format!("Schema change: {} on {}", action, object),
1111 AuditResult::Success,
1112 details,
1113 );
1114 }
1115
1116 pub fn get_entries(&self, limit: usize, offset: usize) -> Vec<AuditEntry> {
1118 let entries = self.entries.read();
1119 let start = entries.len().saturating_sub(limit + offset);
1120 let end = entries.len().saturating_sub(offset);
1121 entries[start..end].iter().rev().cloned().collect()
1122 }
1123
1124 pub fn get_entries_by_type(&self, event_type: AuditEventType, limit: usize) -> Vec<AuditEntry> {
1126 let entries = self.entries.read();
1127 entries
1128 .iter()
1129 .rev()
1130 .filter(|e| e.event_type == event_type)
1131 .take(limit)
1132 .cloned()
1133 .collect()
1134 }
1135
1136 pub fn get_entries_for_user(&self, user_id: &str, limit: usize) -> Vec<AuditEntry> {
1138 let entries = self.entries.read();
1139 entries
1140 .iter()
1141 .rev()
1142 .filter(|e| e.user_id.as_deref() == Some(user_id))
1143 .take(limit)
1144 .cloned()
1145 .collect()
1146 }
1147
1148 pub fn get_failed_logins(&self, since: u64) -> Vec<AuditEntry> {
1150 let entries = self.entries.read();
1151 entries
1152 .iter()
1153 .filter(|e| e.event_type == AuditEventType::LoginFailure && e.timestamp >= since)
1154 .cloned()
1155 .collect()
1156 }
1157
1158 pub fn count(&self) -> usize {
1160 self.entries.read().len()
1161 }
1162
1163 pub fn export(&self, start_time: u64, end_time: u64) -> Vec<AuditEntry> {
1165 let entries = self.entries.read();
1166 entries
1167 .iter()
1168 .filter(|e| e.timestamp >= start_time && e.timestamp <= end_time)
1169 .cloned()
1170 .collect()
1171 }
1172}
1173
1174impl Default for AuditLogger {
1175 fn default() -> Self {
1176 Self::new(100_000) }
1178}
1179
1180pub struct LdapAuthenticator {
1186 config: LdapConfig,
1187}
1188
1189impl LdapAuthenticator {
1190 pub fn new(config: LdapConfig) -> Self {
1192 Self { config }
1193 }
1194
1195 pub fn authenticate(&self, username: &str, password: &str) -> LdapAuthResult {
1197 if self.config.server_url.is_empty() {
1202 return LdapAuthResult {
1203 success: false,
1204 user_dn: None,
1205 email: None,
1206 display_name: None,
1207 groups: vec![],
1208 error: Some("LDAP server URL not configured".to_string()),
1209 };
1210 }
1211
1212 let _user_filter = self.config.user_filter.replace("{username}", username);
1214 let user_dn = format!("uid={},{}", username, self.config.base_dn);
1215
1216 if password.is_empty() {
1218 return LdapAuthResult {
1219 success: false,
1220 user_dn: None,
1221 email: None,
1222 display_name: None,
1223 groups: vec![],
1224 error: Some("Invalid credentials".to_string()),
1225 };
1226 }
1227
1228 let groups = if username == "ldapadmin" {
1230 self.config.admin_groups.clone()
1231 } else if username == "ldapoper" {
1232 self.config.operator_groups.clone()
1233 } else {
1234 vec![]
1235 };
1236
1237 LdapAuthResult {
1238 success: true,
1239 user_dn: Some(user_dn),
1240 email: Some(format!("{}@aegisdb.io", username)),
1241 display_name: Some(username.to_string()),
1242 groups,
1243 error: None,
1244 }
1245 }
1246
1247 pub fn determine_role(&self, groups: &[String]) -> UserRole {
1249 for group in groups {
1250 if self.config.admin_groups.contains(group) {
1251 return UserRole::Admin;
1252 }
1253 }
1254 for group in groups {
1255 if self.config.operator_groups.contains(group) {
1256 return UserRole::Operator;
1257 }
1258 }
1259 UserRole::Viewer
1260 }
1261}
1262
1263pub struct OAuth2Authenticator {
1269 config: OAuth2Config,
1270 pending_states: RwLock<HashMap<String, OAuth2State>>,
1271}
1272
1273#[derive(Debug, Clone)]
1275#[allow(dead_code)]
1276struct OAuth2State {
1277 created_at: Instant,
1278 redirect_uri: String,
1279}
1280
1281impl OAuth2Authenticator {
1282 pub fn new(config: OAuth2Config) -> Self {
1284 Self {
1285 config,
1286 pending_states: RwLock::new(HashMap::new()),
1287 }
1288 }
1289
1290 pub fn get_authorization_url(&self) -> (String, String) {
1292 let state = generate_token();
1293
1294 self.pending_states.write().insert(
1296 state.clone(),
1297 OAuth2State {
1298 created_at: Instant::now(),
1299 redirect_uri: self.config.redirect_uri.clone(),
1300 },
1301 );
1302
1303 let scopes = self.config.scopes.join(" ");
1304 let url = format!(
1305 "{}?client_id={}&redirect_uri={}&response_type=code&scope={}&state={}",
1306 self.config.authorization_url,
1307 self.config.client_id,
1308 urlencoding_encode(&self.config.redirect_uri),
1309 urlencoding_encode(&scopes),
1310 state
1311 );
1312
1313 (url, state)
1314 }
1315
1316 pub fn verify_state(&self, state: &str) -> bool {
1318 let mut states = self.pending_states.write();
1319 if let Some(stored) = states.remove(state) {
1320 stored.created_at.elapsed() < Duration::from_secs(600) } else {
1322 false
1323 }
1324 }
1325
1326 pub fn exchange_code(&self, _code: &str) -> Result<OAuth2TokenResponse, String> {
1328 Ok(OAuth2TokenResponse {
1330 access_token: generate_token(),
1331 token_type: "Bearer".to_string(),
1332 expires_in: Some(3600),
1333 refresh_token: Some(generate_token()),
1334 id_token: Some(generate_token()),
1335 })
1336 }
1337
1338 pub fn get_user_info(&self, _access_token: &str) -> Result<OAuth2UserInfo, String> {
1340 Ok(OAuth2UserInfo {
1342 sub: generate_token()[..16].to_string(),
1343 email: Some("oauth.user@aegisdb.io".to_string()),
1344 name: Some("OAuth User".to_string()),
1345 preferred_username: Some("oauthuser".to_string()),
1346 roles: Some(vec!["viewer".to_string()]),
1347 })
1348 }
1349
1350 pub fn determine_role(&self, roles: &[String]) -> UserRole {
1352 for role in roles {
1353 if self.config.admin_roles.contains(role) {
1354 return UserRole::Admin;
1355 }
1356 }
1357 for role in roles {
1358 if self.config.operator_roles.contains(role) {
1359 return UserRole::Operator;
1360 }
1361 }
1362 UserRole::Viewer
1363 }
1364}
1365
1366fn urlencoding_encode(s: &str) -> String {
1368 s.replace(' ', "%20")
1369 .replace('&', "%26")
1370 .replace('=', "%3D")
1371 .replace('?', "%3F")
1372 .replace('/', "%2F")
1373}
1374
1375#[cfg(test)]
1380mod tests {
1381 use super::*;
1382
1383 #[test]
1384 fn test_login_success() {
1385 let auth = AuthService::new();
1386 let response = auth.login("demo", "demo");
1387 assert!(response.token.is_some());
1388 assert!(response.user.is_some());
1389 }
1390
1391 #[test]
1392 fn test_login_invalid_password() {
1393 let auth = AuthService::new();
1394 let response = auth.login("demo", "wrong");
1395 assert!(response.error.is_some());
1396 }
1397
1398 #[test]
1399 fn test_login_mfa_required() {
1400 let auth = AuthService::new();
1401 let response = auth.login("admin", "admin");
1402 assert!(response.requires_mfa == Some(true));
1403 assert!(response.token.is_some());
1404 }
1405
1406 #[test]
1407 fn test_mfa_verification() {
1408 let auth = AuthService::new();
1409 let login_response = auth.login("admin", "admin");
1410 let temp_token = login_response.token.unwrap();
1411
1412 let mfa_response = auth.verify_mfa("123456", &temp_token);
1413 assert!(mfa_response.token.is_some());
1414 assert!(mfa_response.user.is_some());
1415 }
1416
1417 #[test]
1418 fn test_session_validation() {
1419 let auth = AuthService::new();
1420 let response = auth.login("demo", "demo");
1421 let token = response.token.unwrap();
1422
1423 let user = auth.validate_session(&token);
1424 assert!(user.is_some());
1425 assert_eq!(user.unwrap().username, "demo");
1426 }
1427
1428 #[test]
1429 fn test_logout() {
1430 let auth = AuthService::new();
1431 let response = auth.login("demo", "demo");
1432 let token = response.token.unwrap();
1433
1434 assert!(auth.logout(&token));
1435 assert!(auth.validate_session(&token).is_none());
1436 }
1437
1438 #[test]
1440 fn test_rbac_default_roles() {
1441 let rbac = RbacManager::new();
1442 let roles = rbac.list_roles();
1443 assert!(roles.iter().any(|r| r.name == "admin"));
1444 assert!(roles.iter().any(|r| r.name == "operator"));
1445 assert!(roles.iter().any(|r| r.name == "viewer"));
1446 }
1447
1448 #[test]
1449 fn test_rbac_check_permission() {
1450 let rbac = RbacManager::new();
1451 assert!(rbac.check_permission("user-001", Permission::DatabaseCreate));
1453 assert!(rbac.check_permission("user-001", Permission::ClusterManage));
1454
1455 assert!(rbac.check_permission("user-002", Permission::DataSelect));
1457 assert!(!rbac.check_permission("user-002", Permission::DataInsert));
1458 }
1459
1460 #[test]
1461 fn test_rbac_create_role() {
1462 let rbac = RbacManager::new();
1463 let result = rbac.create_role(
1464 "custom_role",
1465 "Custom role for testing",
1466 vec![Permission::DataSelect, Permission::DataInsert],
1467 "admin"
1468 );
1469 assert!(result.is_ok());
1470
1471 let role = rbac.get_role("custom_role");
1472 assert!(role.is_some());
1473 assert!(role.unwrap().has_permission(Permission::DataSelect));
1474 }
1475
1476 #[test]
1477 fn test_rbac_assign_role() {
1478 let rbac = RbacManager::new();
1479 let result = rbac.assign_role("user-004", "analyst");
1480 assert!(result.is_ok());
1481
1482 let roles = rbac.get_user_roles("user-004");
1483 assert!(roles.contains(&"analyst".to_string()));
1484 }
1485
1486 #[test]
1487 fn test_rbac_cannot_delete_builtin() {
1488 let rbac = RbacManager::new();
1489 let result = rbac.delete_role("admin");
1490 assert!(result.is_err());
1491 }
1492
1493 #[test]
1495 fn test_audit_log_entry() {
1496 let audit = AuditLogger::new(1000);
1497 audit.log_login_success("user-001", "admin", Some("192.168.1.1"));
1498
1499 let entries = audit.get_entries(10, 0);
1500 assert_eq!(entries.len(), 1);
1501 assert_eq!(entries[0].event_type, AuditEventType::LoginSuccess);
1502 }
1503
1504 #[test]
1505 fn test_audit_get_by_type() {
1506 let audit = AuditLogger::new(1000);
1507 audit.log_login_success("user-001", "admin", None);
1508 audit.log_login_failure("baduser", None, "Invalid password");
1509 audit.log_login_success("user-002", "demo", None);
1510
1511 let failures = audit.get_entries_by_type(AuditEventType::LoginFailure, 10);
1512 assert_eq!(failures.len(), 1);
1513
1514 let successes = audit.get_entries_by_type(AuditEventType::LoginSuccess, 10);
1515 assert_eq!(successes.len(), 2);
1516 }
1517
1518 #[test]
1519 fn test_audit_get_for_user() {
1520 let audit = AuditLogger::new(1000);
1521 audit.log_login_success("user-001", "admin", None);
1522 audit.log_data_access("user-001", "admin", "users", "SELECT", 10);
1523 audit.log_login_success("user-002", "demo", None);
1524
1525 let user1_entries = audit.get_entries_for_user("user-001", 10);
1526 assert_eq!(user1_entries.len(), 2);
1527 }
1528
1529 #[test]
1530 fn test_audit_max_entries() {
1531 let audit = AuditLogger::new(5);
1532 for i in 0..10 {
1533 audit.log_login_success(&format!("user-{}", i), "test", None);
1534 }
1535
1536 assert_eq!(audit.count(), 5);
1537 }
1538
1539 #[test]
1541 fn test_ldap_authenticator() {
1542 let config = LdapConfig::default();
1543 let ldap = LdapAuthenticator::new(config);
1544
1545 let result = ldap.authenticate("ldapadmin", "password");
1546 assert!(result.success);
1547 assert!(!result.groups.is_empty());
1548 }
1549
1550 #[test]
1551 fn test_ldap_role_mapping() {
1552 let config = LdapConfig::default();
1553 let ldap = LdapAuthenticator::new(config.clone());
1554
1555 let admin_role = ldap.determine_role(&config.admin_groups);
1556 assert_eq!(admin_role, UserRole::Admin);
1557
1558 let viewer_role = ldap.determine_role(&[]);
1559 assert_eq!(viewer_role, UserRole::Viewer);
1560 }
1561
1562 #[test]
1564 fn test_oauth2_authorization_url() {
1565 let config = OAuth2Config::default();
1566 let oauth = OAuth2Authenticator::new(config);
1567
1568 let (url, state) = oauth.get_authorization_url();
1569 assert!(url.contains("client_id="));
1570 assert!(url.contains(&state));
1571 }
1572
1573 #[test]
1574 fn test_oauth2_state_verification() {
1575 let config = OAuth2Config::default();
1576 let oauth = OAuth2Authenticator::new(config);
1577
1578 let (_, state) = oauth.get_authorization_url();
1579 assert!(oauth.verify_state(&state));
1580 assert!(!oauth.verify_state(&state)); }
1582
1583 #[test]
1584 fn test_oauth2_role_mapping() {
1585 let config = OAuth2Config::default();
1586 let oauth = OAuth2Authenticator::new(config.clone());
1587
1588 let admin_role = oauth.determine_role(&config.admin_roles);
1589 assert_eq!(admin_role, UserRole::Admin);
1590 }
1591}