Skip to main content

aegis_server/
auth.rs

1//! Aegis Authentication Module
2//!
3//! Enterprise authentication with LDAP, OAuth2/OIDC, RBAC, and audit logging.
4//!
5//! @version 0.2.0
6//! @author AutomataNexus Development Team
7
8use argon2::{
9    password_hash::{rand_core::OsRng, PasswordHash, PasswordHasher, PasswordVerifier, SaltString},
10    Argon2,
11};
12use parking_lot::RwLock;
13use rand::Rng;
14use serde::{Deserialize, Serialize};
15use std::collections::{HashMap, HashSet};
16use std::path::PathBuf;
17use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
18
19// =============================================================================
20// Authentication Provider Types
21// =============================================================================
22
23/// Authentication provider type.
24#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
25#[serde(rename_all = "lowercase")]
26pub enum AuthProvider {
27    Local,
28    Ldap,
29    OAuth2,
30    Oidc,
31    Saml,
32}
33
34/// LDAP configuration.
35#[derive(Debug, Clone, Serialize, Deserialize)]
36pub struct LdapConfig {
37    pub server_url: String,
38    pub bind_dn: String,
39    pub bind_password: String,
40    pub base_dn: String,
41    pub user_filter: String,
42    pub group_filter: String,
43    pub use_tls: bool,
44    pub group_attribute: String,
45    pub admin_groups: Vec<String>,
46    pub operator_groups: Vec<String>,
47}
48
49impl Default for LdapConfig {
50    fn default() -> Self {
51        Self {
52            server_url: "ldap://localhost:389".to_string(),
53            bind_dn: "cn=admin,dc=aegisdb,dc=io".to_string(),
54            bind_password: String::new(),
55            base_dn: "dc=aegisdb,dc=io".to_string(),
56            user_filter: "(uid={username})".to_string(),
57            group_filter: "(member={dn})".to_string(),
58            use_tls: true,
59            group_attribute: "memberOf".to_string(),
60            admin_groups: vec!["cn=admins,ou=groups,dc=aegisdb,dc=io".to_string()],
61            operator_groups: vec!["cn=operators,ou=groups,dc=aegisdb,dc=io".to_string()],
62        }
63    }
64}
65
66/// OAuth2/OIDC configuration.
67#[derive(Debug, Clone, Serialize, Deserialize)]
68pub struct OAuth2Config {
69    pub provider_name: String,
70    pub client_id: String,
71    pub client_secret: String,
72    pub authorization_url: String,
73    pub token_url: String,
74    pub userinfo_url: String,
75    pub redirect_uri: String,
76    pub scopes: Vec<String>,
77    pub role_claim: String,
78    pub admin_roles: Vec<String>,
79    pub operator_roles: Vec<String>,
80}
81
82impl Default for OAuth2Config {
83    fn default() -> Self {
84        Self {
85            provider_name: "default".to_string(),
86            client_id: String::new(),
87            client_secret: String::new(),
88            authorization_url: "https://auth.example.com/authorize".to_string(),
89            token_url: "https://auth.example.com/token".to_string(),
90            userinfo_url: "https://auth.example.com/userinfo".to_string(),
91            redirect_uri: "http://localhost:8080/callback".to_string(),
92            scopes: vec![
93                "openid".to_string(),
94                "profile".to_string(),
95                "email".to_string(),
96            ],
97            role_claim: "roles".to_string(),
98            admin_roles: vec!["admin".to_string()],
99            operator_roles: vec!["operator".to_string()],
100        }
101    }
102}
103
104/// LDAP authentication result.
105#[derive(Debug, Clone)]
106pub struct LdapAuthResult {
107    pub success: bool,
108    pub user_dn: Option<String>,
109    pub email: Option<String>,
110    pub display_name: Option<String>,
111    pub groups: Vec<String>,
112    pub error: Option<String>,
113}
114
115/// OAuth2 token response.
116#[derive(Debug, Clone, Serialize, Deserialize)]
117pub struct OAuth2TokenResponse {
118    pub access_token: String,
119    pub token_type: String,
120    pub expires_in: Option<u64>,
121    pub refresh_token: Option<String>,
122    pub id_token: Option<String>,
123}
124
125/// OAuth2 user info.
126#[derive(Debug, Clone, Serialize, Deserialize)]
127pub struct OAuth2UserInfo {
128    pub sub: String,
129    pub email: Option<String>,
130    pub name: Option<String>,
131    pub preferred_username: Option<String>,
132    pub roles: Option<Vec<String>>,
133}
134
135// =============================================================================
136// User Types
137// =============================================================================
138
139/// User role enumeration.
140#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
141#[serde(rename_all = "lowercase")]
142pub enum UserRole {
143    Admin,
144    Operator,
145    Viewer,
146}
147
148impl std::fmt::Display for UserRole {
149    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
150        match self {
151            UserRole::Admin => write!(f, "admin"),
152            UserRole::Operator => write!(f, "operator"),
153            UserRole::Viewer => write!(f, "viewer"),
154        }
155    }
156}
157
158/// User information stored in the system.
159#[derive(Debug, Clone, Serialize, Deserialize)]
160pub struct User {
161    pub id: String,
162    pub username: String,
163    pub email: String,
164    pub password_hash: String,
165    pub role: UserRole,
166    pub mfa_enabled: bool,
167    pub mfa_secret: Option<String>,
168    pub created_at: u64,
169    pub last_login: Option<u64>,
170}
171
172/// User information returned to clients (no sensitive data).
173#[derive(Debug, Clone, Serialize, Deserialize)]
174pub struct UserInfo {
175    pub id: String,
176    pub username: String,
177    pub email: String,
178    pub role: UserRole,
179    pub mfa_enabled: bool,
180    pub created_at: String,
181}
182
183impl From<&User> for UserInfo {
184    fn from(user: &User) -> Self {
185        Self {
186            id: user.id.clone(),
187            username: user.username.clone(),
188            email: user.email.clone(),
189            role: user.role,
190            mfa_enabled: user.mfa_enabled,
191            created_at: format_timestamp(user.created_at),
192        }
193    }
194}
195
196// =============================================================================
197// Session Types
198// =============================================================================
199
200/// Active session information.
201#[derive(Debug, Clone)]
202pub struct Session {
203    pub token: String,
204    pub user_id: String,
205    pub created_at: Instant,
206    pub expires_at: Instant,
207    pub mfa_verified: bool,
208}
209
210impl Session {
211    pub fn is_expired(&self) -> bool {
212        Instant::now() > self.expires_at
213    }
214}
215
216/// Pending MFA verification session.
217#[derive(Debug, Clone)]
218pub struct PendingMfaSession {
219    pub temp_token: String,
220    pub user_id: String,
221    pub created_at: Instant,
222    pub expires_at: Instant,
223}
224
225impl PendingMfaSession {
226    pub fn is_expired(&self) -> bool {
227        Instant::now() > self.expires_at
228    }
229}
230
231// =============================================================================
232// Request/Response Types
233// =============================================================================
234
235/// Login request.
236#[derive(Debug, Clone, Deserialize)]
237pub struct LoginRequest {
238    pub username: String,
239    pub password: String,
240}
241
242/// MFA verification request.
243#[derive(Debug, Clone, Deserialize)]
244pub struct MfaVerifyRequest {
245    pub code: String,
246    pub token: String,
247}
248
249/// MFA setup data for new MFA enrollment.
250#[derive(Debug, Clone, Serialize, Deserialize)]
251pub struct MfaSetupData {
252    pub secret: String,
253    pub qr_code: String,
254    pub backup_codes: Vec<String>,
255}
256
257/// Authentication response.
258#[derive(Debug, Clone, Serialize)]
259pub struct AuthResponse {
260    pub token: Option<String>,
261    pub user: Option<UserInfo>,
262    pub requires_mfa: Option<bool>,
263    pub requires_mfa_setup: Option<bool>,
264    pub mfa_setup_data: Option<MfaSetupData>,
265    pub error: Option<String>,
266}
267
268impl AuthResponse {
269    pub fn success(token: String, user: UserInfo) -> Self {
270        Self {
271            token: Some(token),
272            user: Some(user),
273            requires_mfa: None,
274            requires_mfa_setup: None,
275            mfa_setup_data: None,
276            error: None,
277        }
278    }
279
280    pub fn requires_mfa(temp_token: String) -> Self {
281        Self {
282            token: Some(temp_token),
283            user: None,
284            requires_mfa: Some(true),
285            requires_mfa_setup: None,
286            mfa_setup_data: None,
287            error: None,
288        }
289    }
290
291    pub fn error(message: &str) -> Self {
292        Self {
293            token: None,
294            user: None,
295            requires_mfa: None,
296            requires_mfa_setup: None,
297            mfa_setup_data: None,
298            error: Some(message.to_string()),
299        }
300    }
301}
302
303// =============================================================================
304// Authentication Service
305// =============================================================================
306
307/// Authentication service managing users and sessions.
308pub struct AuthService {
309    users: RwLock<HashMap<String, User>>,
310    sessions: RwLock<HashMap<String, Session>>,
311    pending_mfa: RwLock<HashMap<String, PendingMfaSession>>,
312    session_duration: Duration,
313    mfa_timeout: Duration,
314    data_dir: Option<PathBuf>,
315}
316
317impl AuthService {
318    /// Create a new authentication service.
319    ///
320    /// Uses environment variables as a fallback for admin credentials.
321    pub fn new() -> Self {
322        Self::with_data_dir(None)
323    }
324
325    /// Create an authentication service with optional persistence to disk.
326    /// Admin credentials are read from environment variables only.
327    pub fn with_data_dir(data_dir: Option<PathBuf>) -> Self {
328        Self::with_data_dir_and_secrets(data_dir, None)
329    }
330
331    /// Create an authentication service with persistence and a secrets provider.
332    /// Admin credentials are resolved through the secrets provider chain
333    /// (vault → external vault → env vars) instead of raw environment variables.
334    pub fn with_data_dir_and_secrets(
335        data_dir: Option<PathBuf>,
336        secrets: Option<&dyn crate::secrets::SecretsProvider>,
337    ) -> Self {
338        let mut users = HashMap::new();
339
340        // Load persisted users from disk if data_dir is configured
341        if let Some(ref dir) = data_dir {
342            let users_path = dir.join("users.json");
343            if users_path.exists() {
344                match std::fs::read_to_string(&users_path) {
345                    Ok(data) => match serde_json::from_str::<HashMap<String, User>>(&data) {
346                        Ok(loaded) => {
347                            tracing::info!(
348                                "Loaded {} users from {}",
349                                loaded.len(),
350                                users_path.display()
351                            );
352                            users = loaded;
353                        }
354                        Err(e) => {
355                            tracing::error!(
356                                "Failed to parse users file {}: {}",
357                                users_path.display(),
358                                e
359                            );
360                        }
361                    },
362                    Err(e) => {
363                        tracing::error!(
364                            "Failed to read users file {}: {}",
365                            users_path.display(),
366                            e
367                        );
368                    }
369                }
370            }
371        }
372
373        // Resolve admin credentials through secrets provider (vault-first),
374        // falling back to environment variables if no provider is given.
375        let admin_username = secrets
376            .and_then(|s| s.get(crate::secrets::keys::ADMIN_USERNAME))
377            .or_else(|| std::env::var("AEGIS_ADMIN_USERNAME").ok());
378        let admin_password = secrets
379            .and_then(|s| s.get(crate::secrets::keys::ADMIN_PASSWORD))
380            .or_else(|| std::env::var("AEGIS_ADMIN_PASSWORD").ok());
381        let admin_email = secrets
382            .and_then(|s| s.get(crate::secrets::keys::ADMIN_EMAIL))
383            .or_else(|| std::env::var("AEGIS_ADMIN_EMAIL").ok());
384
385        // Create initial admin user if credentials are available and user doesn't already exist
386        if let (Some(username), Some(password)) = (admin_username, admin_password) {
387            if !users.contains_key(&username) {
388                if password.len() >= 12 {
389                    let email =
390                        admin_email.unwrap_or_else(|| format!("{}@localhost", username));
391
392                    let user_count = users.len() + 1;
393                    let admin = User {
394                        id: format!("user-{:03}", user_count),
395                        username: username.clone(),
396                        email,
397                        password_hash: hash_password(&password),
398                        role: UserRole::Admin,
399                        mfa_enabled: false,
400                        mfa_secret: None,
401                        created_at: now_timestamp(),
402                        last_login: None,
403                    };
404                    tracing::info!("Created initial admin user '{}' from secrets provider", username);
405                    users.insert(admin.username.clone(), admin);
406                } else {
407                    tracing::warn!(
408                        "AEGIS_ADMIN_PASSWORD must be at least 12 characters. Initial admin user not created."
409                    );
410                }
411            }
412        } else if users.is_empty() {
413            tracing::info!(
414                "No initial admin configured. Store credentials in the vault or set AEGIS_ADMIN_USERNAME and AEGIS_ADMIN_PASSWORD."
415            );
416        }
417
418        let service = Self {
419            users: RwLock::new(users),
420            sessions: RwLock::new(HashMap::new()),
421            pending_mfa: RwLock::new(HashMap::new()),
422            session_duration: Duration::from_secs(24 * 60 * 60), // 24 hours
423            mfa_timeout: Duration::from_secs(5 * 60),            // 5 minutes
424            data_dir,
425        };
426
427        // Persist initial state (env-created admin) to disk
428        service.flush_users_to_disk();
429
430        service
431    }
432
433    /// Flush users HashMap to disk as JSON. No-op if data_dir is None.
434    fn flush_users_to_disk(&self) {
435        let Some(ref dir) = self.data_dir else { return };
436        let users_path = dir.join("users.json");
437        let users = self.users.read();
438        match serde_json::to_string_pretty(&*users) {
439            Ok(json) => {
440                if let Err(e) = std::fs::write(&users_path, json) {
441                    tracing::error!("Failed to write users to {}: {}", users_path.display(), e);
442                }
443            }
444            Err(e) => {
445                tracing::error!("Failed to serialize users: {}", e);
446            }
447        }
448    }
449
450    /// Authenticate user with username and password.
451    pub fn login(&self, username: &str, password: &str) -> AuthResponse {
452        let users = self.users.read();
453
454        let user = match users.get(username) {
455            Some(u) => u,
456            None => return AuthResponse::error("Invalid credentials"),
457        };
458
459        if !verify_password(password, &user.password_hash) {
460            return AuthResponse::error("Invalid credentials");
461        }
462
463        // Update last login
464        drop(users);
465        {
466            let mut users = self.users.write();
467            if let Some(u) = users.get_mut(username) {
468                u.last_login = Some(now_timestamp());
469            }
470        }
471        let users = self.users.read();
472        let user = users
473            .get(username)
474            .expect("user should exist after successful credential check");
475
476        if user.mfa_enabled {
477            // Create pending MFA session
478            let temp_token = generate_token();
479            let pending = PendingMfaSession {
480                temp_token: temp_token.clone(),
481                user_id: user.id.clone(),
482                created_at: Instant::now(),
483                expires_at: Instant::now() + self.mfa_timeout,
484            };
485            self.pending_mfa.write().insert(temp_token.clone(), pending);
486
487            AuthResponse::requires_mfa(temp_token)
488        } else {
489            // Create session directly
490            let token = generate_token();
491            let session = Session {
492                token: token.clone(),
493                user_id: user.id.clone(),
494                created_at: Instant::now(),
495                expires_at: Instant::now() + self.session_duration,
496                mfa_verified: true,
497            };
498            self.sessions.write().insert(token.clone(), session);
499
500            AuthResponse::success(token, UserInfo::from(user))
501        }
502    }
503
504    /// Verify MFA code and complete authentication.
505    pub fn verify_mfa(&self, code: &str, temp_token: &str) -> AuthResponse {
506        // Get pending MFA session
507        let pending = {
508            let pending_sessions = self.pending_mfa.read();
509            match pending_sessions.get(temp_token) {
510                Some(p) if !p.is_expired() => p.clone(),
511                Some(_) => return AuthResponse::error("MFA session expired"),
512                None => return AuthResponse::error("Invalid MFA session"),
513            }
514        };
515
516        // Get user
517        let users = self.users.read();
518        let user = match users.values().find(|u| u.id == pending.user_id) {
519            Some(u) => u,
520            None => return AuthResponse::error("User not found"),
521        };
522
523        // Verify TOTP code
524        let secret = match &user.mfa_secret {
525            Some(s) => s,
526            None => return AuthResponse::error("MFA not configured"),
527        };
528
529        if !verify_totp(code, secret) {
530            return AuthResponse::error("Invalid MFA code");
531        }
532
533        // Remove pending session
534        self.pending_mfa.write().remove(temp_token);
535
536        // Create authenticated session
537        let token = generate_token();
538        let session = Session {
539            token: token.clone(),
540            user_id: user.id.clone(),
541            created_at: Instant::now(),
542            expires_at: Instant::now() + self.session_duration,
543            mfa_verified: true,
544        };
545        self.sessions.write().insert(token.clone(), session);
546
547        AuthResponse::success(token, UserInfo::from(user))
548    }
549
550    /// Validate a session token and return user info.
551    pub fn validate_session(&self, token: &str) -> Option<UserInfo> {
552        let sessions = self.sessions.read();
553        let session = sessions.get(token)?;
554
555        if session.is_expired() {
556            return None;
557        }
558
559        let users = self.users.read();
560        let user = users.values().find(|u| u.id == session.user_id)?;
561
562        Some(UserInfo::from(user))
563    }
564
565    /// Logout and invalidate session.
566    pub fn logout(&self, token: &str) -> bool {
567        self.sessions.write().remove(token).is_some()
568    }
569
570    /// Get user by ID.
571    pub fn get_user(&self, user_id: &str) -> Option<UserInfo> {
572        let users = self.users.read();
573        users.values().find(|u| u.id == user_id).map(UserInfo::from)
574    }
575
576    /// List all users.
577    pub fn list_users(&self) -> Vec<UserInfo> {
578        let users = self.users.read();
579        users.values().map(UserInfo::from).collect()
580    }
581
582    /// Create a new user.
583    pub fn create_user(
584        &self,
585        username: &str,
586        email: &str,
587        password: &str,
588        role: &str,
589    ) -> Result<UserInfo, String> {
590        let mut users = self.users.write();
591
592        if users.contains_key(username) {
593            return Err(format!("User '{}' already exists", username));
594        }
595
596        // Validate password strength
597        if password.len() < 8 {
598            return Err("Password must be at least 8 characters".to_string());
599        }
600
601        // Validate username (alphanumeric and underscore only)
602        if !username.chars().all(|c| c.is_alphanumeric() || c == '_') {
603            return Err(
604                "Username must contain only alphanumeric characters and underscores".to_string(),
605            );
606        }
607
608        // Basic email validation
609        if !email.contains('@') || !email.contains('.') {
610            return Err("Invalid email format".to_string());
611        }
612
613        let user_role = match role.to_lowercase().as_str() {
614            "admin" => UserRole::Admin,
615            "operator" => UserRole::Operator,
616            "viewer" | _ => UserRole::Viewer,
617        };
618
619        let id = format!("user-{:03}", users.len() + 1);
620        let user = User {
621            id: id.clone(),
622            username: username.to_string(),
623            email: email.to_string(),
624            password_hash: hash_password(password),
625            role: user_role,
626            mfa_enabled: false,
627            mfa_secret: None,
628            created_at: now_timestamp(),
629            last_login: None,
630        };
631
632        let user_info = UserInfo::from(&user);
633        users.insert(username.to_string(), user);
634
635        // Note: Role assignment should be handled by RbacManager separately
636        tracing::info!("Created user '{}' with role '{}'", username, role);
637        drop(users);
638        self.flush_users_to_disk();
639
640        Ok(user_info)
641    }
642
643    /// Update an existing user.
644    pub fn update_user(
645        &self,
646        username: &str,
647        email: Option<String>,
648        role: Option<String>,
649        password: Option<String>,
650    ) -> Result<UserInfo, String> {
651        let mut users = self.users.write();
652
653        let user = users
654            .get_mut(username)
655            .ok_or_else(|| format!("User '{}' not found", username))?;
656
657        if let Some(new_email) = email {
658            user.email = new_email;
659        }
660
661        if let Some(new_role) = role {
662            user.role = match new_role.to_lowercase().as_str() {
663                "admin" => UserRole::Admin,
664                "operator" => UserRole::Operator,
665                "viewer" | _ => UserRole::Viewer,
666            };
667        }
668
669        let password_changed = if let Some(new_password) = password {
670            user.password_hash = hash_password(&new_password);
671            true
672        } else {
673            false
674        };
675
676        let info = UserInfo::from(user as &User);
677        drop(users);
678
679        // Revoke all sessions for this user on password change
680        if password_changed {
681            let user_id = info.id.clone();
682            let mut sessions = self.sessions.write();
683            let before = sessions.len();
684            sessions.retain(|_, s| s.user_id != user_id);
685            let revoked = before - sessions.len();
686            if revoked > 0 {
687                tracing::info!(
688                    "Revoked {} session(s) for user '{}' due to password change",
689                    revoked,
690                    username
691                );
692            }
693        }
694
695        self.flush_users_to_disk();
696
697        Ok(info)
698    }
699
700    /// Delete a user.
701    pub fn delete_user(&self, username: &str) -> Result<(), String> {
702        let mut users = self.users.write();
703
704        if !users.contains_key(username) {
705            return Err(format!("User '{}' not found", username));
706        }
707
708        // Prevent deleting the admin user
709        if username == "admin" {
710            return Err("Cannot delete the admin user".to_string());
711        }
712
713        users.remove(username);
714        drop(users);
715        self.flush_users_to_disk();
716        Ok(())
717    }
718
719    /// Enable MFA for a user and return the generated secret.
720    /// The secret should be stored by the user in their authenticator app.
721    pub fn enable_mfa(&self, username: &str) -> Result<String, String> {
722        let mut users = self.users.write();
723
724        let user = users
725            .get_mut(username)
726            .ok_or_else(|| format!("User '{}' not found", username))?;
727
728        if user.mfa_enabled {
729            return Err("MFA is already enabled for this user".to_string());
730        }
731
732        // Generate a new MFA secret
733        let secret = generate_mfa_secret();
734        user.mfa_secret = Some(secret.clone());
735        user.mfa_enabled = true;
736
737        tracing::info!("Enabled MFA for user '{}'", username);
738        drop(users);
739        self.flush_users_to_disk();
740
741        Ok(secret)
742    }
743
744    /// Disable MFA for a user.
745    pub fn disable_mfa(&self, username: &str) -> Result<(), String> {
746        let mut users = self.users.write();
747
748        let user = users
749            .get_mut(username)
750            .ok_or_else(|| format!("User '{}' not found", username))?;
751
752        if !user.mfa_enabled {
753            return Err("MFA is not enabled for this user".to_string());
754        }
755
756        user.mfa_secret = None;
757        user.mfa_enabled = false;
758
759        tracing::info!("Disabled MFA for user '{}'", username);
760        drop(users);
761        self.flush_users_to_disk();
762
763        Ok(())
764    }
765
766    /// Clean up expired sessions.
767    pub fn cleanup_expired(&self) {
768        let mut sessions = self.sessions.write();
769        sessions.retain(|_, s| !s.is_expired());
770
771        let mut pending = self.pending_mfa.write();
772        pending.retain(|_, p| !p.is_expired());
773    }
774}
775
776impl Default for AuthService {
777    fn default() -> Self {
778        Self::new()
779    }
780}
781
782// =============================================================================
783// Helper Functions
784// =============================================================================
785
786/// Generate a cryptographically secure random token (256-bit).
787fn generate_token() -> String {
788    let mut rng = rand::thread_rng();
789    let bytes: [u8; 32] = rng.gen();
790    hex::encode(&bytes)
791}
792
793/// Generate a cryptographically secure random token using hex encoding.
794mod hex {
795    const HEX_CHARS: &[u8; 16] = b"0123456789abcdef";
796
797    pub fn encode(bytes: &[u8]) -> String {
798        let mut result = String::with_capacity(bytes.len() * 2);
799        for byte in bytes {
800            result.push(HEX_CHARS[(byte >> 4) as usize] as char);
801            result.push(HEX_CHARS[(byte & 0x0f) as usize] as char);
802        }
803        result
804    }
805}
806
807/// Hash a password using Argon2id with a random salt.
808fn hash_password(password: &str) -> String {
809    let salt = SaltString::generate(&mut OsRng);
810    let argon2 = Argon2::default();
811
812    argon2
813        .hash_password(password.as_bytes(), &salt)
814        .expect("Failed to hash password")
815        .to_string()
816}
817
818/// Verify a password against its Argon2 hash.
819fn verify_password(password: &str, hash: &str) -> bool {
820    let parsed_hash = match PasswordHash::new(hash) {
821        Ok(h) => h,
822        Err(_) => return false,
823    };
824
825    Argon2::default()
826        .verify_password(password.as_bytes(), &parsed_hash)
827        .is_ok()
828}
829
830/// Generate a cryptographically secure MFA secret (Base32 encoded).
831fn generate_mfa_secret() -> String {
832    let mut rng = rand::thread_rng();
833    let bytes: [u8; 20] = rng.gen(); // 160 bits for TOTP
834
835    // Base32 encode (RFC 4648)
836    const BASE32_ALPHABET: &[u8; 32] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
837    let mut result = String::with_capacity(32);
838
839    for chunk in bytes.chunks(5) {
840        let mut buffer = [0u8; 5];
841        buffer[..chunk.len()].copy_from_slice(chunk);
842
843        result.push(BASE32_ALPHABET[(buffer[0] >> 3) as usize] as char);
844        result.push(BASE32_ALPHABET[((buffer[0] & 0x07) << 2 | buffer[1] >> 6) as usize] as char);
845        result.push(BASE32_ALPHABET[((buffer[1] & 0x3E) >> 1) as usize] as char);
846        result.push(BASE32_ALPHABET[((buffer[1] & 0x01) << 4 | buffer[2] >> 4) as usize] as char);
847        result.push(BASE32_ALPHABET[((buffer[2] & 0x0F) << 1 | buffer[3] >> 7) as usize] as char);
848        result.push(BASE32_ALPHABET[((buffer[3] & 0x7C) >> 2) as usize] as char);
849        result.push(BASE32_ALPHABET[((buffer[3] & 0x03) << 3 | buffer[4] >> 5) as usize] as char);
850        result.push(BASE32_ALPHABET[(buffer[4] & 0x1F) as usize] as char);
851    }
852
853    result
854}
855
856/// Verify a TOTP code using RFC 6238 algorithm.
857fn verify_totp(code: &str, secret: &str) -> bool {
858    use data_encoding::BASE32_NOPAD;
859    use hmac::{Hmac, Mac};
860    use sha1::Sha1;
861
862    // Decode the base32 secret
863    let secret_bytes = match BASE32_NOPAD.decode(secret.to_uppercase().as_bytes()) {
864        Ok(bytes) => bytes,
865        Err(_) => {
866            // Try with padding variations
867            let padded = format!(
868                "{}{}",
869                secret.to_uppercase(),
870                &"========"[..((8 - secret.len() % 8) % 8)]
871            );
872            match data_encoding::BASE32.decode(padded.as_bytes()) {
873                Ok(bytes) => bytes,
874                Err(_) => return false,
875            }
876        }
877    };
878
879    // Get current time step (30-second windows)
880    let timestamp = now_timestamp() / 1000; // seconds
881    let time_step = timestamp / 30;
882
883    // Check current time step and one before/after for clock drift tolerance
884    for offset in [-1i64, 0, 1] {
885        let counter = (time_step as i64 + offset) as u64;
886        let counter_bytes = counter.to_be_bytes();
887
888        // Compute HMAC-SHA1
889        let mut mac = match Hmac::<Sha1>::new_from_slice(&secret_bytes) {
890            Ok(m) => m,
891            Err(_) => return false,
892        };
893        mac.update(&counter_bytes);
894        let result = mac.finalize().into_bytes();
895
896        // Dynamic truncation (RFC 4226)
897        let offset_idx = (result[19] & 0x0f) as usize;
898        let binary_code = ((result[offset_idx] & 0x7f) as u32) << 24
899            | (result[offset_idx + 1] as u32) << 16
900            | (result[offset_idx + 2] as u32) << 8
901            | (result[offset_idx + 3] as u32);
902
903        // Generate 6-digit code
904        let otp = binary_code % 1_000_000;
905        let expected = format!("{:06}", otp);
906
907        if code == expected {
908            return true;
909        }
910    }
911
912    false
913}
914
915/// Get current timestamp in milliseconds.
916fn now_timestamp() -> u64 {
917    SystemTime::now()
918        .duration_since(UNIX_EPOCH)
919        .unwrap_or_default()
920        .as_millis() as u64
921}
922
923/// Format a timestamp to RFC3339 string.
924fn format_timestamp(timestamp_ms: u64) -> String {
925    let secs = timestamp_ms / 1000;
926    let datetime = UNIX_EPOCH + Duration::from_secs(secs);
927
928    // Simple ISO 8601 formatting
929    let duration = datetime.duration_since(UNIX_EPOCH).unwrap_or_default();
930    let total_secs = duration.as_secs();
931
932    let days_since_epoch = total_secs / 86400;
933    let secs_today = total_secs % 86400;
934
935    let hours = secs_today / 3600;
936    let minutes = (secs_today % 3600) / 60;
937    let seconds = secs_today % 60;
938
939    // Calculate year/month/day (simplified)
940    let mut year = 1970;
941    let mut remaining_days = days_since_epoch;
942
943    loop {
944        let days_in_year = if is_leap_year(year) { 366 } else { 365 };
945        if remaining_days < days_in_year {
946            break;
947        }
948        remaining_days -= days_in_year;
949        year += 1;
950    }
951
952    let days_in_months: [u64; 12] = if is_leap_year(year) {
953        [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
954    } else {
955        [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
956    };
957
958    let mut month = 1;
959    for &days in &days_in_months {
960        if remaining_days < days {
961            break;
962        }
963        remaining_days -= days;
964        month += 1;
965    }
966    let day = remaining_days + 1;
967
968    format!(
969        "{:04}-{:02}-{:02}T{:02}:{:02}:{:02}Z",
970        year, month, day, hours, minutes, seconds
971    )
972}
973
974fn is_leap_year(year: u64) -> bool {
975    (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)
976}
977
978// =============================================================================
979// RBAC - Role-Based Access Control
980// =============================================================================
981
982/// Permission types for database operations.
983#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
984#[serde(rename_all = "snake_case")]
985pub enum Permission {
986    // Database operations
987    DatabaseCreate,
988    DatabaseDrop,
989    DatabaseList,
990
991    // Table operations
992    TableCreate,
993    TableDrop,
994    TableAlter,
995    TableList,
996
997    // Data operations
998    DataSelect,
999    DataInsert,
1000    DataUpdate,
1001    DataDelete,
1002
1003    // Admin operations
1004    UserCreate,
1005    UserDelete,
1006    UserModify,
1007    RoleCreate,
1008    RoleDelete,
1009    RoleAssign,
1010
1011    // System operations
1012    ConfigView,
1013    ConfigModify,
1014    MetricsView,
1015    LogsView,
1016    BackupCreate,
1017    BackupRestore,
1018
1019    // Cluster operations
1020    NodeAdd,
1021    NodeRemove,
1022    ClusterManage,
1023}
1024
1025/// A role with a set of permissions.
1026#[derive(Debug, Clone, Serialize, Deserialize)]
1027pub struct Role {
1028    pub name: String,
1029    pub description: String,
1030    pub permissions: HashSet<Permission>,
1031    pub created_at: u64,
1032    pub created_by: String,
1033}
1034
1035impl Role {
1036    /// Create a new role with the given permissions.
1037    pub fn new(name: &str, description: &str, permissions: Vec<Permission>) -> Self {
1038        Self {
1039            name: name.to_string(),
1040            description: description.to_string(),
1041            permissions: permissions.into_iter().collect(),
1042            created_at: now_timestamp(),
1043            created_by: "system".to_string(),
1044        }
1045    }
1046
1047    /// Check if this role has a specific permission.
1048    pub fn has_permission(&self, permission: Permission) -> bool {
1049        self.permissions.contains(&permission)
1050    }
1051}
1052
1053/// RBAC manager for role and permission management.
1054pub struct RbacManager {
1055    roles: RwLock<HashMap<String, Role>>,
1056    user_roles: RwLock<HashMap<String, HashSet<String>>>,
1057    row_policies: RwLock<Vec<RowLevelPolicy>>,
1058    data_dir: Option<PathBuf>,
1059}
1060
1061#[derive(serde::Serialize, serde::Deserialize)]
1062struct RbacSnapshot {
1063    roles: HashMap<String, Role>,
1064    user_roles: HashMap<String, HashSet<String>>,
1065    row_policies: Vec<RowLevelPolicy>,
1066}
1067
1068impl RbacManager {
1069    /// Create a new RBAC manager with default roles (no persistence).
1070    pub fn new() -> Self {
1071        Self::with_data_dir(None)
1072    }
1073
1074    /// Create a new RBAC manager, loading state from disk if `data_dir` is provided.
1075    pub fn with_data_dir(data_dir: Option<PathBuf>) -> Self {
1076        // Try to load from disk
1077        if let Some(ref dir) = data_dir {
1078            let path = dir.join("rbac.json");
1079            if path.exists() {
1080                match std::fs::read_to_string(&path) {
1081                    Ok(contents) => match serde_json::from_str::<RbacSnapshot>(&contents) {
1082                        Ok(snapshot) => {
1083                            tracing::info!("Loaded RBAC state from {}", path.display());
1084                            return Self {
1085                                roles: RwLock::new(snapshot.roles),
1086                                user_roles: RwLock::new(snapshot.user_roles),
1087                                row_policies: RwLock::new(snapshot.row_policies),
1088                                data_dir,
1089                            };
1090                        }
1091                        Err(e) => {
1092                            tracing::error!(
1093                                "Failed to parse RBAC snapshot from {}: {}",
1094                                path.display(),
1095                                e
1096                            );
1097                        }
1098                    },
1099                    Err(e) => {
1100                        tracing::error!(
1101                            "Failed to read RBAC snapshot from {}: {}",
1102                            path.display(),
1103                            e
1104                        );
1105                    }
1106                }
1107            }
1108        }
1109
1110        // Fall back to default roles
1111        let mut roles = HashMap::new();
1112
1113        // Create admin role with all permissions
1114        let admin_permissions = vec![
1115            Permission::DatabaseCreate,
1116            Permission::DatabaseDrop,
1117            Permission::DatabaseList,
1118            Permission::TableCreate,
1119            Permission::TableDrop,
1120            Permission::TableAlter,
1121            Permission::TableList,
1122            Permission::DataSelect,
1123            Permission::DataInsert,
1124            Permission::DataUpdate,
1125            Permission::DataDelete,
1126            Permission::UserCreate,
1127            Permission::UserDelete,
1128            Permission::UserModify,
1129            Permission::RoleCreate,
1130            Permission::RoleDelete,
1131            Permission::RoleAssign,
1132            Permission::ConfigView,
1133            Permission::ConfigModify,
1134            Permission::MetricsView,
1135            Permission::LogsView,
1136            Permission::BackupCreate,
1137            Permission::BackupRestore,
1138            Permission::NodeAdd,
1139            Permission::NodeRemove,
1140            Permission::ClusterManage,
1141        ];
1142        roles.insert(
1143            "admin".to_string(),
1144            Role::new("admin", "Full system administrator", admin_permissions),
1145        );
1146
1147        // Create operator role
1148        let operator_permissions = vec![
1149            Permission::DatabaseList,
1150            Permission::TableCreate,
1151            Permission::TableAlter,
1152            Permission::TableList,
1153            Permission::DataSelect,
1154            Permission::DataInsert,
1155            Permission::DataUpdate,
1156            Permission::DataDelete,
1157            Permission::ConfigView,
1158            Permission::MetricsView,
1159            Permission::LogsView,
1160            Permission::BackupCreate,
1161        ];
1162        roles.insert(
1163            "operator".to_string(),
1164            Role::new("operator", "Database operator", operator_permissions),
1165        );
1166
1167        // Create viewer role
1168        let viewer_permissions = vec![
1169            Permission::DatabaseList,
1170            Permission::TableList,
1171            Permission::DataSelect,
1172            Permission::MetricsView,
1173        ];
1174        roles.insert(
1175            "viewer".to_string(),
1176            Role::new("viewer", "Read-only viewer", viewer_permissions),
1177        );
1178
1179        // Create analyst role
1180        let analyst_permissions = vec![
1181            Permission::DatabaseList,
1182            Permission::TableList,
1183            Permission::DataSelect,
1184            Permission::MetricsView,
1185            Permission::LogsView,
1186        ];
1187        roles.insert(
1188            "analyst".to_string(),
1189            Role::new(
1190                "analyst",
1191                "Data analyst with read access",
1192                analyst_permissions,
1193            ),
1194        );
1195
1196        // User-role mappings start empty - assigned when users are created
1197        let user_roles: HashMap<String, HashSet<String>> = HashMap::new();
1198
1199        Self {
1200            roles: RwLock::new(roles),
1201            user_roles: RwLock::new(user_roles),
1202            row_policies: RwLock::new(Vec::new()),
1203            data_dir,
1204        }
1205    }
1206
1207    /// Persist current RBAC state to disk (if data_dir is configured).
1208    fn flush_to_disk(&self) {
1209        let Some(ref dir) = self.data_dir else {
1210            return;
1211        };
1212        let snapshot = RbacSnapshot {
1213            roles: self.roles.read().clone(),
1214            user_roles: self.user_roles.read().clone(),
1215            row_policies: self.row_policies.read().clone(),
1216        };
1217        let path = dir.join("rbac.json");
1218        match serde_json::to_string_pretty(&snapshot) {
1219            Ok(json) => {
1220                if let Err(e) = std::fs::write(&path, json) {
1221                    tracing::error!("Failed to write RBAC snapshot to {}: {}", path.display(), e);
1222                }
1223            }
1224            Err(e) => {
1225                tracing::error!("Failed to serialize RBAC snapshot: {}", e);
1226            }
1227        }
1228    }
1229
1230    /// Create a new role.
1231    pub fn create_role(
1232        &self,
1233        name: &str,
1234        description: &str,
1235        permissions: Vec<Permission>,
1236        created_by: &str,
1237    ) -> Result<(), String> {
1238        let mut roles = self.roles.write();
1239        if roles.contains_key(name) {
1240            return Err(format!("Role '{}' already exists", name));
1241        }
1242
1243        let mut role = Role::new(name, description, permissions);
1244        role.created_by = created_by.to_string();
1245        roles.insert(name.to_string(), role);
1246        drop(roles);
1247        self.flush_to_disk();
1248        Ok(())
1249    }
1250
1251    /// Delete a role.
1252    pub fn delete_role(&self, name: &str) -> Result<(), String> {
1253        let mut roles = self.roles.write();
1254        if !roles.contains_key(name) {
1255            return Err(format!("Role '{}' not found", name));
1256        }
1257        if name == "admin" || name == "operator" || name == "viewer" {
1258            return Err("Cannot delete built-in roles".to_string());
1259        }
1260        roles.remove(name);
1261        drop(roles);
1262        self.flush_to_disk();
1263        Ok(())
1264    }
1265
1266    /// List all roles.
1267    pub fn list_roles(&self) -> Vec<Role> {
1268        self.roles.read().values().cloned().collect()
1269    }
1270
1271    /// Get a specific role.
1272    pub fn get_role(&self, name: &str) -> Option<Role> {
1273        self.roles.read().get(name).cloned()
1274    }
1275
1276    /// Assign a role to a user.
1277    pub fn assign_role(&self, user_id: &str, role_name: &str) -> Result<(), String> {
1278        if !self.roles.read().contains_key(role_name) {
1279            return Err(format!("Role '{}' not found", role_name));
1280        }
1281
1282        let mut user_roles = self.user_roles.write();
1283        user_roles
1284            .entry(user_id.to_string())
1285            .or_default()
1286            .insert(role_name.to_string());
1287        drop(user_roles);
1288        self.flush_to_disk();
1289        Ok(())
1290    }
1291
1292    /// Revoke a role from a user.
1293    pub fn revoke_role(&self, user_id: &str, role_name: &str) -> Result<(), String> {
1294        let mut user_roles = self.user_roles.write();
1295        if let Some(roles) = user_roles.get_mut(user_id) {
1296            roles.remove(role_name);
1297            drop(user_roles);
1298            self.flush_to_disk();
1299            Ok(())
1300        } else {
1301            Err(format!("User '{}' has no roles assigned", user_id))
1302        }
1303    }
1304
1305    /// Get all roles for a user.
1306    pub fn get_user_roles(&self, user_id: &str) -> Vec<String> {
1307        self.user_roles
1308            .read()
1309            .get(user_id)
1310            .map(|r| r.iter().cloned().collect())
1311            .unwrap_or_default()
1312    }
1313
1314    /// Check if a user has a specific permission.
1315    pub fn check_permission(&self, user_id: &str, permission: Permission) -> bool {
1316        let user_roles = self.user_roles.read();
1317        let roles = self.roles.read();
1318
1319        if let Some(user_role_names) = user_roles.get(user_id) {
1320            for role_name in user_role_names {
1321                if let Some(role) = roles.get(role_name) {
1322                    if role.has_permission(permission) {
1323                        return true;
1324                    }
1325                }
1326            }
1327        }
1328        false
1329    }
1330
1331    /// Get all permissions for a user.
1332    pub fn get_user_permissions(&self, user_id: &str) -> HashSet<Permission> {
1333        let mut permissions = HashSet::new();
1334        let user_roles = self.user_roles.read();
1335        let roles = self.roles.read();
1336
1337        if let Some(user_role_names) = user_roles.get(user_id) {
1338            for role_name in user_role_names {
1339                if let Some(role) = roles.get(role_name) {
1340                    permissions.extend(role.permissions.iter().cloned());
1341                }
1342            }
1343        }
1344        permissions
1345    }
1346
1347    /// Add a row-level security policy.
1348    pub fn add_row_policy(&self, policy: RowLevelPolicy) {
1349        self.row_policies.write().push(policy);
1350        self.flush_to_disk();
1351    }
1352
1353    /// Get row-level policies for a table.
1354    pub fn get_row_policies(&self, table: &str, user_id: &str) -> Vec<RowLevelPolicy> {
1355        self.row_policies
1356            .read()
1357            .iter()
1358            .filter(|p| {
1359                p.table == table
1360                    && (p.applies_to.is_empty() || p.applies_to.contains(&user_id.to_string()))
1361            })
1362            .cloned()
1363            .collect()
1364    }
1365}
1366
1367impl Default for RbacManager {
1368    fn default() -> Self {
1369        Self::new()
1370    }
1371}
1372
1373// =============================================================================
1374// Row-Level Security
1375// =============================================================================
1376
1377/// Row-level security policy.
1378#[derive(Debug, Clone, Serialize, Deserialize)]
1379pub struct RowLevelPolicy {
1380    pub name: String,
1381    pub table: String,
1382    pub operation: RowPolicyOperation,
1383    pub condition: String,
1384    pub applies_to: Vec<String>,
1385    pub enabled: bool,
1386}
1387
1388/// Operations that row policies apply to.
1389#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
1390#[serde(rename_all = "lowercase")]
1391pub enum RowPolicyOperation {
1392    Select,
1393    Insert,
1394    Update,
1395    Delete,
1396    All,
1397}
1398
1399// =============================================================================
1400// Audit Logging
1401// =============================================================================
1402
1403/// Audit event types.
1404#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
1405#[serde(rename_all = "snake_case")]
1406pub enum AuditEventType {
1407    // Authentication events
1408    LoginSuccess,
1409    LoginFailure,
1410    Logout,
1411    MfaVerified,
1412    MfaFailed,
1413    SessionExpired,
1414
1415    // Authorization events
1416    PermissionGranted,
1417    PermissionDenied,
1418    RoleAssigned,
1419    RoleRevoked,
1420
1421    // Data events
1422    DataRead,
1423    DataWrite,
1424    DataDelete,
1425    SchemaChange,
1426
1427    // Admin events
1428    UserCreated,
1429    UserDeleted,
1430    UserModified,
1431    ConfigChanged,
1432    BackupCreated,
1433    BackupRestored,
1434
1435    // System events
1436    ServiceStarted,
1437    ServiceStopped,
1438    NodeJoined,
1439    NodeLeft,
1440}
1441
1442/// An audit log entry.
1443#[derive(Debug, Clone, Serialize, Deserialize)]
1444pub struct AuditEntry {
1445    pub id: String,
1446    pub timestamp: u64,
1447    pub event_type: AuditEventType,
1448    pub user_id: Option<String>,
1449    pub username: Option<String>,
1450    pub ip_address: Option<String>,
1451    pub resource: Option<String>,
1452    pub action: String,
1453    pub result: AuditResult,
1454    pub details: HashMap<String, String>,
1455}
1456
1457/// Result of an audited action.
1458#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
1459#[serde(rename_all = "lowercase")]
1460pub enum AuditResult {
1461    Success,
1462    Failure,
1463    Denied,
1464}
1465
1466/// Audit logger for compliance and security tracking.
1467pub struct AuditLogger {
1468    entries: RwLock<Vec<AuditEntry>>,
1469    max_entries: usize,
1470    entry_counter: RwLock<u64>,
1471}
1472
1473impl AuditLogger {
1474    /// Create a new audit logger.
1475    pub fn new(max_entries: usize) -> Self {
1476        Self {
1477            entries: RwLock::new(Vec::with_capacity(max_entries)),
1478            max_entries,
1479            entry_counter: RwLock::new(0),
1480        }
1481    }
1482
1483    /// Log an audit event.
1484    pub fn log(
1485        &self,
1486        event_type: AuditEventType,
1487        user_id: Option<&str>,
1488        username: Option<&str>,
1489        ip_address: Option<&str>,
1490        resource: Option<&str>,
1491        action: &str,
1492        result: AuditResult,
1493        details: HashMap<String, String>,
1494    ) {
1495        let mut counter = self.entry_counter.write();
1496        *counter += 1;
1497        let id = format!("audit-{:012}", *counter);
1498
1499        let entry = AuditEntry {
1500            id,
1501            timestamp: now_timestamp(),
1502            event_type,
1503            user_id: user_id.map(String::from),
1504            username: username.map(String::from),
1505            ip_address: ip_address.map(String::from),
1506            resource: resource.map(String::from),
1507            action: action.to_string(),
1508            result,
1509            details,
1510        };
1511
1512        let mut entries = self.entries.write();
1513        if entries.len() >= self.max_entries {
1514            entries.remove(0);
1515        }
1516        entries.push(entry);
1517    }
1518
1519    /// Log a login success.
1520    pub fn log_login_success(&self, user_id: &str, username: &str, ip: Option<&str>) {
1521        self.log(
1522            AuditEventType::LoginSuccess,
1523            Some(user_id),
1524            Some(username),
1525            ip,
1526            None,
1527            "User logged in",
1528            AuditResult::Success,
1529            HashMap::new(),
1530        );
1531    }
1532
1533    /// Log a login failure.
1534    pub fn log_login_failure(&self, username: &str, ip: Option<&str>, reason: &str) {
1535        let mut details = HashMap::new();
1536        details.insert("reason".to_string(), reason.to_string());
1537        self.log(
1538            AuditEventType::LoginFailure,
1539            None,
1540            Some(username),
1541            ip,
1542            None,
1543            "Login attempt failed",
1544            AuditResult::Failure,
1545            details,
1546        );
1547    }
1548
1549    /// Log a permission denial.
1550    pub fn log_permission_denied(
1551        &self,
1552        user_id: &str,
1553        username: &str,
1554        resource: &str,
1555        permission: &str,
1556    ) {
1557        let mut details = HashMap::new();
1558        details.insert("permission".to_string(), permission.to_string());
1559        self.log(
1560            AuditEventType::PermissionDenied,
1561            Some(user_id),
1562            Some(username),
1563            None,
1564            Some(resource),
1565            "Permission denied",
1566            AuditResult::Denied,
1567            details,
1568        );
1569    }
1570
1571    /// Log a data access.
1572    pub fn log_data_access(
1573        &self,
1574        user_id: &str,
1575        username: &str,
1576        table: &str,
1577        operation: &str,
1578        rows_affected: u64,
1579    ) {
1580        let mut details = HashMap::new();
1581        details.insert("rows_affected".to_string(), rows_affected.to_string());
1582        let event_type = match operation {
1583            "SELECT" => AuditEventType::DataRead,
1584            "INSERT" | "UPDATE" => AuditEventType::DataWrite,
1585            "DELETE" => AuditEventType::DataDelete,
1586            _ => AuditEventType::DataRead,
1587        };
1588        self.log(
1589            event_type,
1590            Some(user_id),
1591            Some(username),
1592            None,
1593            Some(table),
1594            &format!("{} on {}", operation, table),
1595            AuditResult::Success,
1596            details,
1597        );
1598    }
1599
1600    /// Log a schema change.
1601    pub fn log_schema_change(&self, user_id: &str, username: &str, object: &str, action: &str) {
1602        let mut details = HashMap::new();
1603        details.insert("action".to_string(), action.to_string());
1604        self.log(
1605            AuditEventType::SchemaChange,
1606            Some(user_id),
1607            Some(username),
1608            None,
1609            Some(object),
1610            &format!("Schema change: {} on {}", action, object),
1611            AuditResult::Success,
1612            details,
1613        );
1614    }
1615
1616    /// Get recent audit entries.
1617    pub fn get_entries(&self, limit: usize, offset: usize) -> Vec<AuditEntry> {
1618        let entries = self.entries.read();
1619        let start = entries.len().saturating_sub(limit + offset);
1620        let end = entries.len().saturating_sub(offset);
1621        entries[start..end].iter().rev().cloned().collect()
1622    }
1623
1624    /// Get entries by event type.
1625    pub fn get_entries_by_type(&self, event_type: AuditEventType, limit: usize) -> Vec<AuditEntry> {
1626        let entries = self.entries.read();
1627        entries
1628            .iter()
1629            .rev()
1630            .filter(|e| e.event_type == event_type)
1631            .take(limit)
1632            .cloned()
1633            .collect()
1634    }
1635
1636    /// Get entries for a specific user.
1637    pub fn get_entries_for_user(&self, user_id: &str, limit: usize) -> Vec<AuditEntry> {
1638        let entries = self.entries.read();
1639        entries
1640            .iter()
1641            .rev()
1642            .filter(|e| e.user_id.as_deref() == Some(user_id))
1643            .take(limit)
1644            .cloned()
1645            .collect()
1646    }
1647
1648    /// Get failed login attempts.
1649    pub fn get_failed_logins(&self, since: u64) -> Vec<AuditEntry> {
1650        let entries = self.entries.read();
1651        entries
1652            .iter()
1653            .filter(|e| e.event_type == AuditEventType::LoginFailure && e.timestamp >= since)
1654            .cloned()
1655            .collect()
1656    }
1657
1658    /// Count entries.
1659    pub fn count(&self) -> usize {
1660        self.entries.read().len()
1661    }
1662
1663    /// Export entries for compliance reporting.
1664    pub fn export(&self, start_time: u64, end_time: u64) -> Vec<AuditEntry> {
1665        let entries = self.entries.read();
1666        entries
1667            .iter()
1668            .filter(|e| e.timestamp >= start_time && e.timestamp <= end_time)
1669            .cloned()
1670            .collect()
1671    }
1672}
1673
1674impl Default for AuditLogger {
1675    fn default() -> Self {
1676        Self::new(100_000) // Keep 100k audit entries by default
1677    }
1678}
1679
1680// =============================================================================
1681// LDAP Authentication
1682// =============================================================================
1683
1684/// LDAP authenticator for enterprise directory integration.
1685pub struct LdapAuthenticator {
1686    config: LdapConfig,
1687}
1688
1689impl LdapAuthenticator {
1690    /// Create a new LDAP authenticator.
1691    pub fn new(config: LdapConfig) -> Self {
1692        Self { config }
1693    }
1694
1695    /// Authenticate user against LDAP using the ldap3 crate.
1696    pub fn authenticate(&self, username: &str, password: &str) -> LdapAuthResult {
1697        // Validate configuration
1698        if self.config.server_url.is_empty() {
1699            return LdapAuthResult {
1700                success: false,
1701                user_dn: None,
1702                email: None,
1703                display_name: None,
1704                groups: vec![],
1705                error: Some("LDAP server URL not configured".to_string()),
1706            };
1707        }
1708
1709        if password.is_empty() {
1710            return LdapAuthResult {
1711                success: false,
1712                user_dn: None,
1713                email: None,
1714                display_name: None,
1715                groups: vec![],
1716                error: Some("Password is required".to_string()),
1717            };
1718        }
1719
1720        // Use a runtime for blocking LDAP operations
1721        let runtime = match tokio::runtime::Handle::try_current() {
1722            Ok(handle) => handle,
1723            Err(_) => {
1724                return LdapAuthResult {
1725                    success: false,
1726                    user_dn: None,
1727                    email: None,
1728                    display_name: None,
1729                    groups: vec![],
1730                    error: Some("No async runtime available".to_string()),
1731                };
1732            }
1733        };
1734
1735        let config = self.config.clone();
1736        let username = username.to_string();
1737        let password = password.to_string();
1738
1739        // Perform LDAP operations
1740        let result = runtime
1741            .block_on(async move { Self::authenticate_async(&config, &username, &password).await });
1742
1743        result
1744    }
1745
1746    /// Async LDAP authentication implementation.
1747    async fn authenticate_async(
1748        config: &LdapConfig,
1749        username: &str,
1750        password: &str,
1751    ) -> LdapAuthResult {
1752        use ldap3::{LdapConnAsync, Scope, SearchEntry};
1753
1754        // Connect to LDAP server
1755        let (conn, mut ldap) = match LdapConnAsync::new(&config.server_url).await {
1756            Ok(c) => c,
1757            Err(e) => {
1758                return LdapAuthResult {
1759                    success: false,
1760                    user_dn: None,
1761                    email: None,
1762                    display_name: None,
1763                    groups: vec![],
1764                    error: Some(format!("Failed to connect to LDAP server: {}", e)),
1765                };
1766            }
1767        };
1768
1769        // Spawn connection driver
1770        ldap3::drive!(conn);
1771
1772        // Bind with service account to search for user
1773        if let Err(e) = ldap
1774            .simple_bind(&config.bind_dn, &config.bind_password)
1775            .await
1776        {
1777            let _ = ldap.unbind().await;
1778            return LdapAuthResult {
1779                success: false,
1780                user_dn: None,
1781                email: None,
1782                display_name: None,
1783                groups: vec![],
1784                error: Some(format!("Service account bind failed: {}", e)),
1785            };
1786        }
1787
1788        // Search for user
1789        let user_filter = config.user_filter.replace("{username}", username);
1790        let search_result = match ldap
1791            .search(
1792                &config.base_dn,
1793                Scope::Subtree,
1794                &user_filter,
1795                vec!["dn", "mail", "displayName", "cn", &config.group_attribute],
1796            )
1797            .await
1798        {
1799            Ok(result) => result,
1800            Err(e) => {
1801                let _ = ldap.unbind().await;
1802                return LdapAuthResult {
1803                    success: false,
1804                    user_dn: None,
1805                    email: None,
1806                    display_name: None,
1807                    groups: vec![],
1808                    error: Some(format!("User search failed: {}", e)),
1809                };
1810            }
1811        };
1812
1813        let (entries, _result) = search_result.success().unwrap_or((
1814            vec![],
1815            ldap3::LdapResult {
1816                rc: 0,
1817                matched: String::new(),
1818                text: String::new(),
1819                refs: vec![],
1820                ctrls: vec![],
1821            },
1822        ));
1823
1824        if entries.is_empty() {
1825            let _ = ldap.unbind().await;
1826            return LdapAuthResult {
1827                success: false,
1828                user_dn: None,
1829                email: None,
1830                display_name: None,
1831                groups: vec![],
1832                error: Some("User not found".to_string()),
1833            };
1834        }
1835
1836        // Parse user entry
1837        let entry = SearchEntry::construct(entries[0].clone());
1838        let user_dn = entry.dn.clone();
1839        let email = entry.attrs.get("mail").and_then(|v| v.first()).cloned();
1840        let display_name = entry
1841            .attrs
1842            .get("displayName")
1843            .or_else(|| entry.attrs.get("cn"))
1844            .and_then(|v| v.first())
1845            .cloned();
1846        let groups: Vec<String> = entry
1847            .attrs
1848            .get(&config.group_attribute)
1849            .cloned()
1850            .unwrap_or_default();
1851
1852        // Bind as user to verify password
1853        if let Err(e) = ldap.simple_bind(&user_dn, password).await {
1854            let _ = ldap.unbind().await;
1855            return LdapAuthResult {
1856                success: false,
1857                user_dn: None,
1858                email: None,
1859                display_name: None,
1860                groups: vec![],
1861                error: Some(format!("Authentication failed: {}", e)),
1862            };
1863        }
1864
1865        // Verify bind was successful
1866        let bind_result = ldap.simple_bind(&user_dn, password).await;
1867        let _ = ldap.unbind().await;
1868
1869        match bind_result {
1870            Ok(result) => {
1871                if result.rc == 0 {
1872                    LdapAuthResult {
1873                        success: true,
1874                        user_dn: Some(user_dn),
1875                        email,
1876                        display_name,
1877                        groups,
1878                        error: None,
1879                    }
1880                } else {
1881                    LdapAuthResult {
1882                        success: false,
1883                        user_dn: None,
1884                        email: None,
1885                        display_name: None,
1886                        groups: vec![],
1887                        error: Some("Invalid credentials".to_string()),
1888                    }
1889                }
1890            }
1891            Err(e) => LdapAuthResult {
1892                success: false,
1893                user_dn: None,
1894                email: None,
1895                display_name: None,
1896                groups: vec![],
1897                error: Some(format!("Bind failed: {}", e)),
1898            },
1899        }
1900    }
1901
1902    /// Determine role from LDAP groups.
1903    pub fn determine_role(&self, groups: &[String]) -> UserRole {
1904        for group in groups {
1905            if self.config.admin_groups.contains(group) {
1906                return UserRole::Admin;
1907            }
1908        }
1909        for group in groups {
1910            if self.config.operator_groups.contains(group) {
1911                return UserRole::Operator;
1912            }
1913        }
1914        UserRole::Viewer
1915    }
1916}
1917
1918// =============================================================================
1919// OAuth2 Authentication
1920// =============================================================================
1921
1922/// OAuth2 authenticator for external identity providers.
1923pub struct OAuth2Authenticator {
1924    config: OAuth2Config,
1925    pending_states: RwLock<HashMap<String, OAuth2State>>,
1926    http_client: reqwest::Client,
1927}
1928
1929/// Pending OAuth2 state for CSRF protection.
1930#[derive(Debug, Clone)]
1931#[allow(dead_code)]
1932struct OAuth2State {
1933    created_at: Instant,
1934    redirect_uri: String,
1935}
1936
1937impl OAuth2Authenticator {
1938    /// Create a new OAuth2 authenticator.
1939    pub fn new(config: OAuth2Config) -> Self {
1940        let http_client = reqwest::Client::builder()
1941            .timeout(Duration::from_secs(30))
1942            .build()
1943            .unwrap_or_else(|_| reqwest::Client::new());
1944
1945        Self {
1946            config,
1947            pending_states: RwLock::new(HashMap::new()),
1948            http_client,
1949        }
1950    }
1951
1952    /// Generate authorization URL for OAuth2 flow.
1953    pub fn get_authorization_url(&self) -> (String, String) {
1954        let state = generate_token();
1955
1956        // Store state for verification
1957        self.pending_states.write().insert(
1958            state.clone(),
1959            OAuth2State {
1960                created_at: Instant::now(),
1961                redirect_uri: self.config.redirect_uri.clone(),
1962            },
1963        );
1964
1965        let scopes = self.config.scopes.join(" ");
1966        let url = format!(
1967            "{}?client_id={}&redirect_uri={}&response_type=code&scope={}&state={}",
1968            self.config.authorization_url,
1969            self.config.client_id,
1970            urlencoding_encode(&self.config.redirect_uri),
1971            urlencoding_encode(&scopes),
1972            state
1973        );
1974
1975        (url, state)
1976    }
1977
1978    /// Verify state parameter.
1979    pub fn verify_state(&self, state: &str) -> bool {
1980        let mut states = self.pending_states.write();
1981        if let Some(stored) = states.remove(state) {
1982            stored.created_at.elapsed() < Duration::from_secs(600) // 10 minute timeout
1983        } else {
1984            false
1985        }
1986    }
1987
1988    /// Exchange authorization code for tokens using real HTTP request.
1989    pub fn exchange_code(&self, code: &str) -> Result<OAuth2TokenResponse, String> {
1990        let runtime = tokio::runtime::Handle::try_current()
1991            .map_err(|_| "No async runtime available".to_string())?;
1992
1993        let client = self.http_client.clone();
1994        let token_url = self.config.token_url.clone();
1995        let client_id = self.config.client_id.clone();
1996        let client_secret = self.config.client_secret.clone();
1997        let redirect_uri = self.config.redirect_uri.clone();
1998        let code = code.to_string();
1999
2000        runtime.block_on(async move {
2001            Self::exchange_code_async(
2002                &client,
2003                &token_url,
2004                &client_id,
2005                &client_secret,
2006                &redirect_uri,
2007                &code,
2008            )
2009            .await
2010        })
2011    }
2012
2013    /// Async implementation of token exchange.
2014    async fn exchange_code_async(
2015        client: &reqwest::Client,
2016        token_url: &str,
2017        client_id: &str,
2018        client_secret: &str,
2019        redirect_uri: &str,
2020        code: &str,
2021    ) -> Result<OAuth2TokenResponse, String> {
2022        let params = [
2023            ("grant_type", "authorization_code"),
2024            ("code", code),
2025            ("client_id", client_id),
2026            ("client_secret", client_secret),
2027            ("redirect_uri", redirect_uri),
2028        ];
2029
2030        let response = client
2031            .post(token_url)
2032            .form(&params)
2033            .send()
2034            .await
2035            .map_err(|e| format!("Token request failed: {}", e))?;
2036
2037        if !response.status().is_success() {
2038            let status = response.status();
2039            let body = response.text().await.unwrap_or_default();
2040            return Err(format!("Token endpoint returned {}: {}", status, body));
2041        }
2042
2043        response
2044            .json::<OAuth2TokenResponse>()
2045            .await
2046            .map_err(|e| format!("Failed to parse token response: {}", e))
2047    }
2048
2049    /// Get user info from OAuth2 provider using real HTTP request.
2050    pub fn get_user_info(&self, access_token: &str) -> Result<OAuth2UserInfo, String> {
2051        let runtime = tokio::runtime::Handle::try_current()
2052            .map_err(|_| "No async runtime available".to_string())?;
2053
2054        let client = self.http_client.clone();
2055        let userinfo_url = self.config.userinfo_url.clone();
2056        let access_token = access_token.to_string();
2057
2058        runtime.block_on(async move {
2059            Self::get_user_info_async(&client, &userinfo_url, &access_token).await
2060        })
2061    }
2062
2063    /// Async implementation of user info retrieval.
2064    async fn get_user_info_async(
2065        client: &reqwest::Client,
2066        userinfo_url: &str,
2067        access_token: &str,
2068    ) -> Result<OAuth2UserInfo, String> {
2069        let response = client
2070            .get(userinfo_url)
2071            .bearer_auth(access_token)
2072            .send()
2073            .await
2074            .map_err(|e| format!("Userinfo request failed: {}", e))?;
2075
2076        if !response.status().is_success() {
2077            let status = response.status();
2078            let body = response.text().await.unwrap_or_default();
2079            return Err(format!("Userinfo endpoint returned {}: {}", status, body));
2080        }
2081
2082        response
2083            .json::<OAuth2UserInfo>()
2084            .await
2085            .map_err(|e| format!("Failed to parse userinfo response: {}", e))
2086    }
2087
2088    /// Determine role from OAuth2 claims.
2089    pub fn determine_role(&self, roles: &[String]) -> UserRole {
2090        for role in roles {
2091            if self.config.admin_roles.contains(role) {
2092                return UserRole::Admin;
2093            }
2094        }
2095        for role in roles {
2096            if self.config.operator_roles.contains(role) {
2097                return UserRole::Operator;
2098            }
2099        }
2100        UserRole::Viewer
2101    }
2102}
2103
2104/// Simple URL encoding (subset).
2105fn urlencoding_encode(s: &str) -> String {
2106    s.replace(' ', "%20")
2107        .replace('&', "%26")
2108        .replace('=', "%3D")
2109        .replace('?', "%3F")
2110        .replace('/', "%2F")
2111}
2112
2113// =============================================================================
2114// Tests
2115// =============================================================================
2116
2117#[cfg(test)]
2118mod tests {
2119    use super::*;
2120
2121    /// Helper to create an auth service with a test user
2122    fn auth_with_test_user() -> AuthService {
2123        let auth = AuthService::new();
2124        auth.create_user("testuser", "test@example.com", "TestPassword123!", "viewer")
2125            .expect("failed to create test user");
2126        auth
2127    }
2128
2129    /// Helper to create an auth service with an admin user (MFA enabled)
2130    fn auth_with_admin_user() -> (AuthService, String) {
2131        let auth = AuthService::new();
2132        auth.create_user(
2133            "testadmin",
2134            "admin@example.com",
2135            "AdminPassword123!",
2136            "admin",
2137        )
2138        .expect("failed to create admin user");
2139
2140        // Enable MFA and get the secret
2141        let secret = generate_mfa_secret();
2142        {
2143            let mut users = auth.users.write();
2144            if let Some(user) = users.get_mut("testadmin") {
2145                user.mfa_enabled = true;
2146                user.mfa_secret = Some(secret.clone());
2147            }
2148        }
2149
2150        (auth, secret)
2151    }
2152
2153    #[test]
2154    fn test_login_success() {
2155        let auth = auth_with_test_user();
2156        let response = auth.login("testuser", "TestPassword123!");
2157        assert!(response.token.is_some());
2158        assert!(response.user.is_some());
2159    }
2160
2161    #[test]
2162    fn test_login_invalid_password() {
2163        let auth = auth_with_test_user();
2164        let response = auth.login("testuser", "wrong");
2165        assert!(response.error.is_some());
2166    }
2167
2168    #[test]
2169    fn test_login_mfa_required() {
2170        let (auth, _secret) = auth_with_admin_user();
2171        let response = auth.login("testadmin", "AdminPassword123!");
2172        assert!(response.requires_mfa == Some(true));
2173        assert!(response.token.is_some());
2174    }
2175
2176    #[test]
2177    fn test_mfa_verification() {
2178        let (auth, secret) = auth_with_admin_user();
2179        let login_response = auth.login("testadmin", "AdminPassword123!");
2180        let temp_token = login_response
2181            .token
2182            .expect("login should return temp token for MFA");
2183
2184        // Generate a valid TOTP code using the user's secret
2185        let totp_code = generate_test_totp(&secret);
2186
2187        let mfa_response = auth.verify_mfa(&totp_code, &temp_token);
2188        assert!(
2189            mfa_response.token.is_some(),
2190            "MFA verification failed: {:?}",
2191            mfa_response.error
2192        );
2193        assert!(mfa_response.user.is_some());
2194    }
2195
2196    /// Generate a TOTP code for testing using the same algorithm as verify_totp
2197    fn generate_test_totp(secret: &str) -> String {
2198        use data_encoding::BASE32_NOPAD;
2199        use hmac::{Hmac, Mac};
2200        use sha1::Sha1;
2201
2202        let secret_bytes = BASE32_NOPAD
2203            .decode(secret.to_uppercase().as_bytes())
2204            .unwrap_or_else(|_| {
2205                let padded = format!(
2206                    "{}{}",
2207                    secret.to_uppercase(),
2208                    &"========"[..((8 - secret.len() % 8) % 8)]
2209                );
2210                data_encoding::BASE32
2211                    .decode(padded.as_bytes())
2212                    .expect("padded BASE32 decode should succeed")
2213            });
2214
2215        let timestamp = std::time::SystemTime::now()
2216            .duration_since(std::time::UNIX_EPOCH)
2217            .expect("system time should be after UNIX_EPOCH")
2218            .as_secs();
2219        let time_step = timestamp / 30;
2220        let counter_bytes = time_step.to_be_bytes();
2221
2222        let mut mac =
2223            Hmac::<Sha1>::new_from_slice(&secret_bytes).expect("HMAC key should be valid");
2224        mac.update(&counter_bytes);
2225        let result = mac.finalize().into_bytes();
2226
2227        let offset_idx = (result[19] & 0x0f) as usize;
2228        let binary_code = ((result[offset_idx] & 0x7f) as u32) << 24
2229            | (result[offset_idx + 1] as u32) << 16
2230            | (result[offset_idx + 2] as u32) << 8
2231            | (result[offset_idx + 3] as u32);
2232
2233        let otp = binary_code % 1_000_000;
2234        format!("{:06}", otp)
2235    }
2236
2237    #[test]
2238    fn test_session_validation() {
2239        let auth = auth_with_test_user();
2240        let response = auth.login("testuser", "TestPassword123!");
2241        let token = response.token.expect("login should return token");
2242
2243        let user = auth.validate_session(&token);
2244        assert!(user.is_some());
2245        assert_eq!(user.expect("user should be present").username, "testuser");
2246    }
2247
2248    #[test]
2249    fn test_logout() {
2250        let auth = auth_with_test_user();
2251        let response = auth.login("testuser", "TestPassword123!");
2252        let token = response.token.expect("login should return token");
2253
2254        assert!(auth.logout(&token));
2255        assert!(auth.validate_session(&token).is_none());
2256    }
2257
2258    #[test]
2259    fn test_password_validation() {
2260        let auth = AuthService::new();
2261
2262        // Password too short
2263        let result = auth.create_user("user1", "test@example.com", "short", "viewer");
2264        assert!(result.is_err());
2265        assert!(result.unwrap_err().contains("8 characters"));
2266    }
2267
2268    #[test]
2269    fn test_username_validation() {
2270        let auth = AuthService::new();
2271
2272        // Invalid username (contains special chars)
2273        let result = auth.create_user(
2274            "user@name",
2275            "test@example.com",
2276            "ValidPassword123",
2277            "viewer",
2278        );
2279        assert!(result.is_err());
2280        assert!(result.unwrap_err().contains("alphanumeric"));
2281    }
2282
2283    #[test]
2284    fn test_email_validation() {
2285        let auth = AuthService::new();
2286
2287        // Invalid email
2288        let result = auth.create_user("username", "invalid-email", "ValidPassword123", "viewer");
2289        assert!(result.is_err());
2290        assert!(result.unwrap_err().contains("email"));
2291    }
2292
2293    // RBAC Tests
2294    #[test]
2295    fn test_rbac_default_roles() {
2296        let rbac = RbacManager::new();
2297        let roles = rbac.list_roles();
2298        assert!(roles.iter().any(|r| r.name == "admin"));
2299        assert!(roles.iter().any(|r| r.name == "operator"));
2300        assert!(roles.iter().any(|r| r.name == "viewer"));
2301    }
2302
2303    #[test]
2304    fn test_rbac_check_permission() {
2305        let rbac = RbacManager::new();
2306
2307        // Assign admin role to a test user
2308        rbac.assign_role("test-user-1", "admin")
2309            .expect("failed to assign admin role");
2310        rbac.assign_role("test-user-2", "viewer")
2311            .expect("failed to assign viewer role");
2312
2313        // Admin user should have all permissions
2314        assert!(rbac.check_permission("test-user-1", Permission::DatabaseCreate));
2315        assert!(rbac.check_permission("test-user-1", Permission::ClusterManage));
2316
2317        // Viewer should only have read permissions
2318        assert!(rbac.check_permission("test-user-2", Permission::DataSelect));
2319        assert!(!rbac.check_permission("test-user-2", Permission::DataInsert));
2320    }
2321
2322    #[test]
2323    fn test_rbac_create_role() {
2324        let rbac = RbacManager::new();
2325        let result = rbac.create_role(
2326            "custom_role",
2327            "Custom role for testing",
2328            vec![Permission::DataSelect, Permission::DataInsert],
2329            "admin",
2330        );
2331        assert!(result.is_ok());
2332
2333        let role = rbac.get_role("custom_role");
2334        assert!(role.is_some());
2335        assert!(role
2336            .expect("role should exist")
2337            .has_permission(Permission::DataSelect));
2338    }
2339
2340    #[test]
2341    fn test_rbac_assign_role() {
2342        let rbac = RbacManager::new();
2343        let result = rbac.assign_role("new-user", "analyst");
2344        assert!(result.is_ok());
2345
2346        let roles = rbac.get_user_roles("new-user");
2347        assert!(roles.contains(&"analyst".to_string()));
2348    }
2349
2350    #[test]
2351    fn test_rbac_cannot_delete_builtin() {
2352        let rbac = RbacManager::new();
2353        let result = rbac.delete_role("admin");
2354        assert!(result.is_err());
2355    }
2356
2357    // Audit Logging Tests
2358    #[test]
2359    fn test_audit_log_entry() {
2360        let audit = AuditLogger::new(1000);
2361        audit.log_login_success("user-001", "testuser", Some("192.168.1.1"));
2362
2363        let entries = audit.get_entries(10, 0);
2364        assert_eq!(entries.len(), 1);
2365        assert_eq!(entries[0].event_type, AuditEventType::LoginSuccess);
2366    }
2367
2368    #[test]
2369    fn test_audit_get_by_type() {
2370        let audit = AuditLogger::new(1000);
2371        audit.log_login_success("user-001", "admin", None);
2372        audit.log_login_failure("baduser", None, "Invalid password");
2373        audit.log_login_success("user-002", "demo", None);
2374
2375        let failures = audit.get_entries_by_type(AuditEventType::LoginFailure, 10);
2376        assert_eq!(failures.len(), 1);
2377
2378        let successes = audit.get_entries_by_type(AuditEventType::LoginSuccess, 10);
2379        assert_eq!(successes.len(), 2);
2380    }
2381
2382    #[test]
2383    fn test_audit_get_for_user() {
2384        let audit = AuditLogger::new(1000);
2385        audit.log_login_success("user-001", "admin", None);
2386        audit.log_data_access("user-001", "admin", "users", "SELECT", 10);
2387        audit.log_login_success("user-002", "demo", None);
2388
2389        let user1_entries = audit.get_entries_for_user("user-001", 10);
2390        assert_eq!(user1_entries.len(), 2);
2391    }
2392
2393    #[test]
2394    fn test_audit_max_entries() {
2395        let audit = AuditLogger::new(5);
2396        for i in 0..10 {
2397            audit.log_login_success(&format!("user-{}", i), "test", None);
2398        }
2399
2400        assert_eq!(audit.count(), 5);
2401    }
2402
2403    // Password hashing tests
2404    #[test]
2405    fn test_password_hashing_unique() {
2406        let hash1 = hash_password("testpassword");
2407        let hash2 = hash_password("testpassword");
2408        // Each hash should be different due to random salt
2409        assert_ne!(hash1, hash2);
2410    }
2411
2412    #[test]
2413    fn test_password_verification() {
2414        let password = "SecurePassword123!";
2415        let hash = hash_password(password);
2416        assert!(verify_password(password, &hash));
2417        assert!(!verify_password("wrongpassword", &hash));
2418    }
2419
2420    #[test]
2421    fn test_token_generation_unique() {
2422        let token1 = generate_token();
2423        let token2 = generate_token();
2424        assert_ne!(token1, token2);
2425        assert_eq!(token1.len(), 64); // 256 bits = 64 hex chars
2426    }
2427
2428    #[test]
2429    fn test_mfa_secret_generation() {
2430        let secret1 = generate_mfa_secret();
2431        let secret2 = generate_mfa_secret();
2432        assert_ne!(secret1, secret2);
2433        assert_eq!(secret1.len(), 32); // 160 bits = 32 base32 chars
2434    }
2435
2436    // LDAP Tests
2437    #[test]
2438    fn test_ldap_authenticator_config_validation() {
2439        // Test with empty server URL - should fail validation
2440        let mut config = LdapConfig::default();
2441        config.server_url = String::new();
2442        let ldap = LdapAuthenticator::new(config);
2443
2444        let result = ldap.authenticate("testuser", "password");
2445        assert!(!result.success);
2446        assert!(result.error.is_some());
2447        assert!(result
2448            .error
2449            .as_ref()
2450            .expect("error should be present")
2451            .contains("not configured"));
2452    }
2453
2454    #[test]
2455    fn test_ldap_authenticator_empty_password() {
2456        let config = LdapConfig::default();
2457        let ldap = LdapAuthenticator::new(config);
2458
2459        let result = ldap.authenticate("testuser", "");
2460        assert!(!result.success);
2461        assert!(result.error.is_some());
2462        assert!(result
2463            .error
2464            .as_ref()
2465            .expect("error should be present")
2466            .contains("Password is required"));
2467    }
2468
2469    #[test]
2470    fn test_ldap_role_mapping() {
2471        let config = LdapConfig::default();
2472        let ldap = LdapAuthenticator::new(config.clone());
2473
2474        let admin_role = ldap.determine_role(&config.admin_groups);
2475        assert_eq!(admin_role, UserRole::Admin);
2476
2477        let viewer_role = ldap.determine_role(&[]);
2478        assert_eq!(viewer_role, UserRole::Viewer);
2479    }
2480
2481    // OAuth2 Tests
2482    #[test]
2483    fn test_oauth2_authorization_url() {
2484        let config = OAuth2Config::default();
2485        let oauth = OAuth2Authenticator::new(config);
2486
2487        let (url, state) = oauth.get_authorization_url();
2488        assert!(url.contains("client_id="));
2489        assert!(url.contains(&state));
2490    }
2491
2492    #[test]
2493    fn test_oauth2_state_verification() {
2494        let config = OAuth2Config::default();
2495        let oauth = OAuth2Authenticator::new(config);
2496
2497        let (_, state) = oauth.get_authorization_url();
2498        assert!(oauth.verify_state(&state));
2499        assert!(!oauth.verify_state(&state)); // Second call should fail
2500    }
2501
2502    #[test]
2503    fn test_oauth2_role_mapping() {
2504        let config = OAuth2Config::default();
2505        let oauth = OAuth2Authenticator::new(config.clone());
2506
2507        let admin_role = oauth.determine_role(&config.admin_roles);
2508        assert_eq!(admin_role, UserRole::Admin);
2509    }
2510}