auth_framework/auth_modular/
session_manager.rs

1//! Session management module
2
3use crate::errors::{AuthError, Result};
4use crate::storage::{AuthStorage, SessionData};
5use std::collections::HashMap;
6use std::sync::Arc;
7use std::time::Duration;
8use tracing::{debug, info};
9
10/// Session manager for handling user sessions
11pub struct SessionManager {
12    storage: Arc<dyn AuthStorage>,
13}
14
15impl SessionManager {
16    /// Create a new session manager
17    pub fn new(storage: Arc<dyn AuthStorage>) -> Self {
18        Self { storage }
19    }
20
21    /// Create a new session
22    pub async fn create_session(
23        &self,
24        user_id: &str,
25        expires_in: Duration,
26        ip_address: Option<String>,
27        user_agent: Option<String>,
28    ) -> Result<String> {
29        debug!("Creating session for user '{}'", user_id);
30
31        // Validate session duration
32        if expires_in.is_zero() {
33            return Err(AuthError::invalid_credential(
34                "session_duration",
35                "Session duration must be greater than zero",
36            ));
37        }
38
39        if expires_in > Duration::from_secs(365 * 24 * 60 * 60) {
40            // 1 year max
41            return Err(AuthError::invalid_credential(
42                "session_duration",
43                "Session duration exceeds maximum allowed (1 year)",
44            ));
45        }
46
47        let session_id = crate::utils::string::generate_id(Some("sess"));
48        let session = SessionData::new(session_id.clone(), user_id, expires_in)
49            .with_metadata(ip_address, user_agent);
50
51        self.storage.store_session(&session_id, &session).await?;
52
53        info!("Session '{}' created for user '{}'", session_id, user_id);
54        Ok(session_id)
55    }
56
57    /// Get session information
58    pub async fn get_session(&self, session_id: &str) -> Result<Option<SessionData>> {
59        debug!("Getting session '{}'", session_id);
60
61        let session = self.storage.get_session(session_id).await?;
62
63        // Check if session is expired
64        if let Some(ref session_data) = session
65            && session_data.is_expired()
66        {
67            // Remove expired session
68            let _ = self.delete_session(session_id).await;
69            return Ok(None);
70        }
71
72        Ok(session)
73    }
74
75    /// Delete a session
76    pub async fn delete_session(&self, session_id: &str) -> Result<()> {
77        debug!("Deleting session '{}'", session_id);
78
79        self.storage.delete_session(session_id).await?;
80        info!("Session '{}' deleted", session_id);
81        Ok(())
82    }
83
84    /// Update session last activity
85    pub async fn update_session_activity(&self, session_id: &str) -> Result<()> {
86        if let Some(mut session) = self.storage.get_session(session_id).await? {
87            session.last_activity = chrono::Utc::now();
88            self.storage.store_session(session_id, &session).await?;
89        }
90        Ok(())
91    }
92
93    /// Get all sessions for a user
94    pub async fn get_user_sessions(&self, user_id: &str) -> Result<Vec<(String, SessionData)>> {
95        // Note: This would require storage backend support for querying by user_id
96        // For now, return empty vector as this would be a more complex implementation
97        debug!("Getting all sessions for user '{}'", user_id);
98        Ok(vec![])
99    }
100
101    /// Delete all sessions for a user
102    pub async fn delete_user_sessions(&self, user_id: &str) -> Result<()> {
103        debug!("Deleting all sessions for user '{}'", user_id);
104
105        // Get user sessions and delete them
106        let sessions = self.get_user_sessions(user_id).await?;
107        for (session_id, _) in sessions {
108            let _ = self.delete_session(&session_id).await;
109        }
110
111        info!("All sessions deleted for user '{}'", user_id);
112        Ok(())
113    }
114
115    /// Clean up expired sessions
116    pub async fn cleanup_expired_sessions(&self) -> Result<()> {
117        debug!("Cleaning up expired sessions");
118
119        // This would require storage backend support for bulk cleanup
120        // For now, this is handled by the storage implementation's cleanup_expired method
121        Ok(())
122    }
123
124    /// Validate session and return user info
125    pub async fn validate_session(&self, session_id: &str) -> Result<Option<String>> {
126        if let Some(session) = self.get_session(session_id).await?
127            && !session.is_expired()
128        {
129            // Update last activity
130            let _ = self.update_session_activity(session_id).await;
131            return Ok(Some(session.user_id));
132        }
133        Ok(None)
134    }
135
136    /// Extend session expiration
137    pub async fn extend_session(&self, session_id: &str, additional_time: Duration) -> Result<()> {
138        debug!(
139            "Extending session '{}' by {:?}",
140            session_id, additional_time
141        );
142
143        if let Some(mut session) = self.storage.get_session(session_id).await? {
144            session.expires_at += chrono::Duration::from_std(additional_time)
145                .map_err(|e| AuthError::internal(format!("Failed to convert duration: {}", e)))?;
146            self.storage.store_session(session_id, &session).await?;
147            info!("Session '{}' extended", session_id);
148        }
149
150        Ok(())
151    }
152
153    /// Count the number of currently active sessions
154    /// Used for security audit statistics
155    pub async fn count_active_sessions(&self) -> Result<u64> {
156        debug!("Counting active sessions");
157
158        // Use the storage layer's count_active_sessions method
159        let active_count = self.storage.count_active_sessions().await?;
160
161        debug!("Found {} active sessions", active_count);
162        Ok(active_count)
163    }
164
165    /// Get security metrics for sessions
166    pub async fn get_session_security_metrics(&self) -> Result<HashMap<String, serde_json::Value>> {
167        debug!("Collecting session security metrics");
168
169        let mut metrics = HashMap::new();
170        let active_count = self.count_active_sessions().await?;
171
172        metrics.insert(
173            "active_sessions".to_string(),
174            serde_json::Value::Number(serde_json::Number::from(active_count)),
175        );
176        metrics.insert(
177            "last_check".to_string(),
178            serde_json::Value::String(chrono::Utc::now().to_rfc3339()),
179        );
180
181        Ok(metrics)
182    }
183}
184
185