auth_framework/
auth.rs

1//! Main authentication framework implementation.
2
3use crate::config::AuthConfig;
4use crate::credentials::{Credential, CredentialMetadata};
5use crate::errors::{AuthError, MfaError, Result};
6use crate::methods::{AuthMethod, MfaChallenge, MethodResult};
7use crate::permissions::{Permission, PermissionChecker};
8use crate::storage::{AuthStorage, MemoryStorage, SessionData};
9use crate::tokens::{AuthToken, TokenManager};
10use crate::utils::rate_limit::RateLimiter;
11use std::collections::HashMap;
12use std::sync::Arc;
13use std::time::Duration;
14use tokio::sync::RwLock;
15use tracing::{debug, info, warn, error};
16
17/// Result of an authentication attempt.
18#[derive(Debug, Clone)]
19pub enum AuthResult {
20    /// Authentication was successful
21    Success(Box<AuthToken>),
22    
23    /// Multi-factor authentication is required
24    MfaRequired(Box<MfaChallenge>),
25    
26    /// Authentication failed
27    Failure(String),
28}
29
30/// Information about a user.
31#[derive(Debug, Clone)]
32pub struct UserInfo {
33    /// User ID
34    pub id: String,
35    
36    /// Username
37    pub username: String,
38    
39    /// Email address
40    pub email: Option<String>,
41    
42    /// Display name
43    pub name: Option<String>,
44    
45    /// User roles
46    pub roles: Vec<String>,
47    
48    /// Whether the user is active
49    pub active: bool,
50    
51    /// Additional user attributes
52    pub attributes: HashMap<String, serde_json::Value>,
53}
54
55/// Main authentication framework.
56pub struct AuthFramework {
57    /// Configuration
58    config: AuthConfig,
59    
60    /// Registered authentication methods
61    methods: HashMap<String, Box<dyn AuthMethod>>,
62    
63    /// Token manager
64    token_manager: TokenManager,
65    
66    /// Storage backend
67    storage: Arc<dyn AuthStorage>,
68    
69    /// Permission checker
70    permission_checker: Arc<RwLock<PermissionChecker>>,
71    
72    /// Rate limiter
73    rate_limiter: Option<RateLimiter>,
74    
75    /// Active MFA challenges
76    mfa_challenges: Arc<RwLock<HashMap<String, MfaChallenge>>>,
77    
78    /// Active sessions
79    sessions: Arc<RwLock<HashMap<String, SessionData>>>,
80    
81    /// Framework initialization state
82    initialized: bool,
83}
84
85impl AuthFramework {
86    /// Create a new authentication framework.
87    pub fn new(config: AuthConfig) -> Self {
88        // Validate configuration
89        config.validate().expect("Invalid configuration");
90        
91        // Create token manager
92        let token_manager = if let Some(secret) = &config.security.secret_key {
93            TokenManager::new_hmac(
94                secret.as_bytes(),
95                "auth-framework",
96                "auth-framework",
97            )
98        } else {
99            TokenManager::new_hmac(
100                b"default-secret-key", // This should be replaced with a proper secret
101                "auth-framework",
102                "auth-framework",
103            )
104        };
105        
106        // Create storage backend
107        let storage: Arc<dyn AuthStorage> = match &config.storage {
108            #[cfg(feature = "redis-storage")]
109            crate::config::StorageConfig::Redis { url, key_prefix } => {
110                Arc::new(crate::storage::RedisStorage::new(url, key_prefix)
111                    .expect("Failed to create Redis storage"))
112            }
113            _ => Arc::new(MemoryStorage::new()),
114        };
115        
116        // Create rate limiter if enabled
117        let rate_limiter = if config.rate_limiting.enabled {
118            Some(RateLimiter::new(
119                config.rate_limiting.max_requests,
120                config.rate_limiting.window,
121            ))
122        } else {
123            None
124        };
125        
126        Self {
127            config,
128            methods: HashMap::new(),
129            token_manager,
130            storage,
131            permission_checker: Arc::new(RwLock::new(PermissionChecker::new())),
132            rate_limiter,
133            mfa_challenges: Arc::new(RwLock::new(HashMap::new())),
134            sessions: Arc::new(RwLock::new(HashMap::new())),
135            initialized: false,
136        }
137    }
138
139    /// Register an authentication method.
140    pub fn register_method(&mut self, name: impl Into<String>, method: Box<dyn AuthMethod>) {
141        let name = name.into();
142        info!("Registering authentication method: {}", name);
143        
144        // Validate method configuration
145        if let Err(e) = method.validate_config() {
146            error!("Method '{}' configuration validation failed: {}", name, e);
147            return;
148        }
149        
150        self.methods.insert(name, method);
151    }
152
153    /// Initialize the authentication framework.
154    pub async fn initialize(&mut self) -> Result<()> {
155        if self.initialized {
156            return Ok(());
157        }
158
159        info!("Initializing authentication framework");
160        
161        // Initialize permission checker with default roles
162        {
163            let mut checker = self.permission_checker.write().await;
164            checker.create_default_roles();
165        }
166        
167        // Perform any necessary setup
168        self.cleanup_expired_data().await?;
169        
170        self.initialized = true;
171        info!("Authentication framework initialized successfully");
172        
173        Ok(())
174    }
175
176    /// Authenticate a user with the specified method.
177    pub async fn authenticate(
178        &self,
179        method_name: &str,
180        credential: Credential,
181    ) -> Result<AuthResult> {
182        self.authenticate_with_metadata(method_name, credential, CredentialMetadata::new()).await
183    }
184
185    /// Authenticate a user with the specified method and metadata.
186    pub async fn authenticate_with_metadata(
187        &self,
188        method_name: &str,
189        credential: Credential,
190        metadata: CredentialMetadata,
191    ) -> Result<AuthResult> {
192        if !self.initialized {
193            return Err(AuthError::internal("Framework not initialized"));
194        }
195
196        // Check rate limiting
197        if let Some(ref rate_limiter) = self.rate_limiter {
198            let rate_key = format!("auth:{}:{}", 
199                method_name, 
200                metadata.client_ip.as_deref().unwrap_or("unknown")
201            );
202            
203            if !rate_limiter.is_allowed(&rate_key) {
204                warn!("Rate limit exceeded for method '{}' from IP {:?}", 
205                    method_name, metadata.client_ip);
206                return Err(AuthError::rate_limit("Too many authentication attempts"));
207            }
208        }
209
210        // Get the authentication method
211        let method = self.methods.get(method_name)
212            .ok_or_else(|| AuthError::auth_method(
213                method_name,
214                "Authentication method not found".to_string(),
215            ))?;
216
217        // Log authentication attempt
218        debug!("Authentication attempt with method '{}' for credential: {}", 
219            method_name, credential.safe_display());
220
221        // Perform authentication
222        let result = method.authenticate(&credential, &metadata).await?;
223
224        // Log and handle the result
225        match &result {
226            MethodResult::Success(token) => {
227                info!("Authentication successful for user '{}' with method '{}'", 
228                    token.user_id, method_name);
229                
230                // Store token
231                self.storage.store_token(token).await?;
232                
233                // Log audit event
234                self.log_audit_event("auth_success", &token.user_id, method_name, &metadata).await;
235                
236                Ok(AuthResult::Success(token.clone()))
237            }
238            
239            MethodResult::MfaRequired(challenge) => {
240                info!("MFA required for user '{}' with method '{}'", 
241                    challenge.user_id, method_name);
242                
243                // Store MFA challenge
244                let mut challenges = self.mfa_challenges.write().await;
245                challenges.insert(challenge.id.clone(), (**challenge).clone());
246                
247                // Log audit event
248                self.log_audit_event("mfa_required", &challenge.user_id, method_name, &metadata).await;
249                
250                Ok(AuthResult::MfaRequired(challenge.clone()))
251            }
252            
253            MethodResult::Failure { reason } => {
254                warn!("Authentication failed for method '{}': {}", method_name, reason);
255                
256                // Log audit event
257                self.log_audit_event("auth_failure", "unknown", method_name, &metadata).await;
258                
259                Ok(AuthResult::Failure(reason.clone()))
260            }
261        }
262    }
263
264    /// Complete multi-factor authentication.
265    pub async fn complete_mfa(
266        &self,
267        challenge: MfaChallenge,
268        mfa_code: &str,
269    ) -> Result<AuthToken> {
270        debug!("Completing MFA for challenge '{}'", challenge.id);
271
272        // Check if challenge exists and is valid
273        let mut challenges = self.mfa_challenges.write().await;
274        let stored_challenge = challenges.get(&challenge.id)
275            .ok_or(MfaError::ChallengeExpired)?;
276
277        if stored_challenge.is_expired() {
278            challenges.remove(&challenge.id);
279            return Err(MfaError::ChallengeExpired.into());
280        }
281
282        // Verify MFA code (this would integrate with actual MFA providers)
283        if !self.verify_mfa_code(stored_challenge, mfa_code).await? {
284            return Err(MfaError::InvalidCode.into());
285        }
286
287        // Remove the challenge
288        challenges.remove(&challenge.id);
289
290        // Create authentication token
291        let token = self.token_manager.create_auth_token(
292            &challenge.user_id,
293            vec![], // Scopes would be determined by user permissions
294            "mfa",
295            None,
296        )?;
297
298        // Store the token
299        self.storage.store_token(&token).await?;
300
301        info!("MFA completed successfully for user '{}'", challenge.user_id);
302        
303        Ok(token)
304    }
305
306    /// Validate a token.
307    pub async fn validate_token(&self, token: &AuthToken) -> Result<bool> {
308        // Check basic token validity
309        if !token.is_valid() {
310            return Ok(false);
311        }
312
313        // Validate with token manager
314        self.token_manager.validate_auth_token(token)?;
315
316        // Check if token exists in storage
317        if let Some(stored_token) = self.storage.get_token(&token.token_id).await? {
318            // Update last used time
319            let mut updated_token = stored_token;
320            updated_token.mark_used();
321            self.storage.update_token(&updated_token).await?;
322            
323            Ok(true)
324        } else {
325            Ok(false)
326        }
327    }
328
329    /// Get user information from a token.
330    pub async fn get_user_info(&self, token: &AuthToken) -> Result<UserInfo> {
331        if !self.validate_token(token).await? {
332            return Err(AuthError::auth_method("token", "Invalid token".to_string()));
333        }
334
335        // Extract user info from token
336        let token_info = self.token_manager.extract_token_info(&token.access_token)?;
337        
338        Ok(UserInfo {
339            id: token_info.user_id,
340            username: token_info.username.unwrap_or_else(|| "unknown".to_string()),
341            email: token_info.email,
342            name: token_info.name,
343            roles: token_info.roles,
344            active: true, // This would come from user storage
345            attributes: token_info.attributes,
346        })
347    }
348
349    /// Check if a token has a specific permission.
350    pub async fn check_permission(
351        &self,
352        token: &AuthToken,
353        action: &str,
354        resource: &str,
355    ) -> Result<bool> {
356        if !self.validate_token(token).await? {
357            return Ok(false);
358        }
359
360        let permission = Permission::new(action, resource);
361        let mut checker = self.permission_checker.write().await;
362        checker.check_token_permission(token, &permission)
363    }
364
365    /// Refresh a token.
366    pub async fn refresh_token(&self, token: &AuthToken) -> Result<AuthToken> {
367        debug!("Refreshing token for user '{}'", token.user_id);
368
369        // Check if the auth method supports refresh
370        if let Some(method) = self.methods.get(&token.auth_method) {
371            if method.supports_refresh() {
372                if let Some(ref refresh_token) = token.refresh_token {
373                    let new_token = method.refresh_token(refresh_token).await?;
374                    self.storage.store_token(&new_token).await?;
375                    return Ok(new_token);
376                }
377            }
378        }
379
380        // Fallback to creating a new token with the same properties
381        let new_token = self.token_manager.refresh_token(token)?;
382        self.storage.store_token(&new_token).await?;
383        
384        info!("Token refreshed for user '{}'", token.user_id);
385        
386        Ok(new_token)
387    }
388
389    /// Revoke a token.
390    pub async fn revoke_token(&self, token: &AuthToken) -> Result<()> {
391        debug!("Revoking token for user '{}'", token.user_id);
392
393        // Mark token as revoked
394        let mut revoked_token = token.clone();
395        revoked_token.revoke(Some("Manual revocation".to_string()));
396        
397        // Update in storage
398        self.storage.update_token(&revoked_token).await?;
399        
400        info!("Token revoked for user '{}'", token.user_id);
401        
402        Ok(())
403    }
404
405    /// Create a new API key for a user.
406    pub async fn create_api_key(&self, user_id: &str, expires_in: Option<Duration>) -> Result<String> {
407        debug!("Creating API key for user '{}'", user_id);
408
409        // Generate a secure API key
410        let api_key = format!("ak_{}", crate::utils::crypto::generate_token(32));
411        
412        // Create a token for the API key
413        let token = self.token_manager.create_auth_token(
414            user_id,
415            vec!["api".to_string()],
416            "api-key",
417            expires_in,
418        )?;
419
420        // Store the token
421        self.storage.store_token(&token).await?;
422
423        info!("API key created for user '{}'", user_id);
424        
425        Ok(api_key)
426    }
427
428    /// Create a new session.
429    pub async fn create_session(
430        &self,
431        user_id: &str,
432        expires_in: Duration,
433        ip_address: Option<String>,
434        user_agent: Option<String>,
435    ) -> Result<String> {
436        let session_id = crate::utils::string::generate_id(Some("sess"));
437        let session = SessionData::new(session_id.clone(), user_id, expires_in)
438            .with_metadata(ip_address, user_agent);
439
440        self.storage.store_session(&session_id, &session).await?;
441        
442        info!("Session created for user '{}'", user_id);
443        
444        Ok(session_id)
445    }
446
447    /// Get session information.
448    pub async fn get_session(&self, session_id: &str) -> Result<Option<SessionData>> {
449        self.storage.get_session(session_id).await
450    }
451
452    /// Delete a session.
453    pub async fn delete_session(&self, session_id: &str) -> Result<()> {
454        self.storage.delete_session(session_id).await?;
455        info!("Session '{}' deleted", session_id);
456        Ok(())
457    }
458
459    /// Get all tokens for a user.
460    pub async fn list_user_tokens(&self, user_id: &str) -> Result<Vec<AuthToken>> {
461        self.storage.list_user_tokens(user_id).await
462    }
463
464    /// Clean up expired data.
465    pub async fn cleanup_expired_data(&self) -> Result<()> {
466        debug!("Cleaning up expired data");
467
468        // Clean up storage
469        self.storage.cleanup_expired().await?;
470
471        // Clean up MFA challenges
472        {
473            let mut challenges = self.mfa_challenges.write().await;
474            let now = chrono::Utc::now();
475            challenges.retain(|_, challenge| challenge.expires_at > now);
476        }
477
478        // Clean up sessions
479        {
480            let mut sessions = self.sessions.write().await;
481            let now = chrono::Utc::now();
482            sessions.retain(|_, session| session.expires_at > now);
483        }
484
485        // Clean up rate limiter
486        if let Some(ref rate_limiter) = self.rate_limiter {
487            rate_limiter.cleanup();
488        }
489
490        Ok(())
491    }
492
493    /// Get authentication framework statistics.
494    pub async fn get_stats(&self) -> Result<AuthStats> {
495        let mut stats = AuthStats::default();
496
497        // Count active tokens per user
498        // This would be more efficient with proper storage queries
499        let _user_tokens: HashMap<String, u32> = HashMap::new();
500        for method in self.methods.keys() {
501            stats.registered_methods.push(method.clone());
502        }
503
504        stats.active_sessions = self.sessions.read().await.len() as u64;
505        stats.active_mfa_challenges = self.mfa_challenges.read().await.len() as u64;
506
507        Ok(stats)
508    }
509
510    /// Verify MFA code (placeholder implementation).
511    async fn verify_mfa_code(&self, _challenge: &MfaChallenge, _code: &str) -> Result<bool> {
512        // This would integrate with actual MFA providers (TOTP, SMS, etc.)
513        // For now, we'll accept any 6-digit code
514        Ok(true)
515    }
516
517    /// Log an audit event.
518    async fn log_audit_event(
519        &self,
520        event_type: &str,
521        user_id: &str,
522        method: &str,
523        metadata: &CredentialMetadata,
524    ) {
525        if self.config.audit.enabled {
526            let should_log = match event_type {
527                "auth_success" => self.config.audit.log_success,
528                "auth_failure" => self.config.audit.log_failures,
529                "mfa_required" => self.config.audit.log_success,
530                _ => true,
531            };
532
533            if should_log {
534                info!(
535                    target: "auth_audit",
536                    event_type = event_type,
537                    user_id = user_id,
538                    method = method,
539                    client_ip = metadata.client_ip.as_deref().unwrap_or("unknown"),
540                    user_agent = metadata.user_agent.as_deref().unwrap_or("unknown"),
541                    timestamp = chrono::Utc::now().to_rfc3339(),
542                    "Authentication event"
543                );
544            }
545        }
546    }
547
548    /// Create an authentication token directly (useful for testing and demos).
549    /// 
550    /// Note: In production, tokens should be created through the `authenticate` method.
551    pub async fn create_auth_token(
552        &self,
553        user_id: impl Into<String>,
554        scopes: Vec<String>,
555        method_name: impl Into<String>,
556        lifetime: Option<Duration>,
557    ) -> Result<AuthToken> {
558        let method_name = method_name.into();
559        let user_id = user_id.into();
560        
561        // Get the method to access its token manager
562        let _method = self.methods.get(&method_name)
563            .ok_or_else(|| AuthError::auth_method(&method_name, "Method not found"))?;
564        
565        // Create a proper JWT token using the default token manager
566        let jwt_token = self.token_manager.create_jwt_token(
567            &user_id,
568            scopes.clone(),
569            lifetime,
570        )?;
571        
572        // Create the auth token
573        let token = AuthToken::new(
574            user_id,
575            jwt_token,
576            lifetime.unwrap_or(Duration::from_secs(3600)),
577            method_name,
578        ).with_scopes(scopes);
579        
580        // Store the token
581        self.storage.store_token(&token).await?;
582        
583        Ok(token)
584    }
585}
586
587/// Authentication framework statistics.
588#[derive(Debug, Clone, Default)]
589pub struct AuthStats {
590    /// Number of registered authentication methods
591    pub registered_methods: Vec<String>,
592    
593    /// Number of active sessions
594    pub active_sessions: u64,
595    
596    /// Number of active MFA challenges
597    pub active_mfa_challenges: u64,
598    
599    /// Number of tokens issued (this would need proper tracking)
600    pub tokens_issued: u64,
601    
602    /// Number of authentication attempts (this would need proper tracking)
603    pub auth_attempts: u64,
604}
605
606#[cfg(test)]
607mod tests {
608    use super::*;
609    use crate::methods::JwtMethod;
610    use crate::config::AuthConfig;
611
612    #[tokio::test]
613    async fn test_framework_initialization() {
614        let config = AuthConfig::new();
615        let mut framework = AuthFramework::new(config);
616        
617        assert!(framework.initialize().await.is_ok());
618        assert!(framework.initialized);
619    }
620
621    #[tokio::test]
622    async fn test_method_registration() {
623        let config = AuthConfig::new();
624        let mut framework = AuthFramework::new(config);
625        
626        let jwt_method = JwtMethod::new().secret_key("test-secret");
627        framework.register_method("jwt", Box::new(jwt_method));
628        
629        assert!(framework.methods.contains_key("jwt"));
630    }
631
632    #[tokio::test]
633    async fn test_token_validation() {
634        let config = AuthConfig::new();
635        let mut framework = AuthFramework::new(config);
636        framework.initialize().await.unwrap();
637        
638        let token = framework.token_manager.create_auth_token(
639            "test-user",
640            vec!["read".to_string()],
641            "test",
642            None,
643        ).unwrap();
644        
645        // Store the token first
646        framework.storage.store_token(&token).await.unwrap();
647        
648        assert!(framework.validate_token(&token).await.unwrap());
649    }
650
651    #[tokio::test]
652    async fn test_session_management() {
653        let config = AuthConfig::new();
654        let mut framework = AuthFramework::new(config);
655        framework.initialize().await.unwrap();
656        
657        let session_id = framework.create_session(
658            "test-user",
659            Duration::from_secs(3600),
660            Some("192.168.1.1".to_string()),
661            Some("Test Agent".to_string()),
662        ).await.unwrap();
663        
664        let session = framework.get_session(&session_id).await.unwrap();
665        assert!(session.is_some());
666        
667        framework.delete_session(&session_id).await.unwrap();
668        let session = framework.get_session(&session_id).await.unwrap();
669        assert!(session.is_none());
670    }
671
672    #[tokio::test]
673    async fn test_cleanup_expired_data() {
674        let config = AuthConfig::new();
675        let mut framework = AuthFramework::new(config);
676        framework.initialize().await.unwrap();
677        
678        // This test would need expired data to be meaningful
679        assert!(framework.cleanup_expired_data().await.is_ok());
680    }
681}