diff options
| author | UMTS at Teleco <crt@teleco.ch> | 2025-12-13 02:48:13 +0100 |
|---|---|---|
| committer | UMTS at Teleco <crt@teleco.ch> | 2025-12-13 02:48:13 +0100 |
| commit | e52b8e1c2e110d0feb74feb7905c2ff064b51d55 (patch) | |
| tree | 3090814e422250e07e72cf1c83241ffd95cf20f7 /src/auth/session.rs | |
Diffstat (limited to 'src/auth/session.rs')
| -rw-r--r-- | src/auth/session.rs | 129 |
1 files changed, 129 insertions, 0 deletions
diff --git a/src/auth/session.rs b/src/auth/session.rs new file mode 100644 index 0000000..277cef2 --- /dev/null +++ b/src/auth/session.rs @@ -0,0 +1,129 @@ +// Session management for SeckelAPI +use crate::config::Config; +use crate::models::Session; +use chrono::{Duration, Utc}; +use std::collections::HashMap; +use std::sync::{Arc, RwLock}; +use uuid::Uuid; + +#[derive(Clone)] +pub struct SessionManager { + sessions: Arc<RwLock<HashMap<String, Session>>>, + config: Arc<Config>, +} + +impl SessionManager { + pub fn new(config: Arc<Config>) -> Self { + Self { + sessions: Arc::new(RwLock::new(HashMap::new())), + config, + } + } + + fn get_timeout_for_power(&self, power: i32) -> u64 { + self.config.get_session_timeout(power) + } + + pub fn create_session( + &self, + user_id: i32, + username: String, + role_id: i32, + role_name: String, + power: i32, + ) -> String { + let token = Uuid::new_v4().to_string(); + let now = Utc::now(); + + let session = Session { + user_id, + username, + role_id, + role_name, + power, + created_at: now, + last_accessed: now, + }; + + if let Ok(mut sessions) = self.sessions.write() { + // Check concurrent session limit for this user + let max_sessions = self.config.get_max_concurrent_sessions(power); + let user_sessions: Vec<(String, chrono::DateTime<Utc>)> = sessions + .iter() + .filter(|(_, s)| s.user_id == user_id) + .map(|(token, s)| (token.clone(), s.created_at)) + .collect(); + + // If at limit, remove the oldest session + if user_sessions.len() >= max_sessions as usize { + if let Some(oldest_token) = user_sessions + .iter() + .min_by_key(|(_, created)| created) + .map(|(token, _)| token.clone()) + { + sessions.remove(&oldest_token); + } + } + + sessions.insert(token.clone(), session); + } + + token + } + + pub fn get_session(&self, token: &str) -> Option<Session> { + let mut session_to_update = None; + + { + if let Ok(sessions) = self.sessions.read() { + if let Some(session) = sessions.get(token) { + let now = Utc::now(); + let timeout_for_user = self.get_timeout_for_power(session.power); + let timeout_duration = Duration::minutes(timeout_for_user as i64); + + // Check if session has expired + if now - session.last_accessed > timeout_duration { + return None; // Session expired, will be cleaned up later + } else { + session_to_update = Some(session.clone()); + } + } + } + } + + if let Some(mut session) = session_to_update { + // Update last accessed time only if refresh_on_activity is enabled + if self.config.security.refresh_session_on_activity { + session.last_accessed = Utc::now(); + + if let Ok(mut sessions) = self.sessions.write() { + sessions.insert(token.to_string(), session.clone()); + } + } + + Some(session) + } else { + None + } + } + + pub fn remove_session(&self, token: &str) -> bool { + if let Ok(mut sessions) = self.sessions.write() { + sessions.remove(token).is_some() + } else { + false + } + } + + pub fn cleanup_expired_sessions(&self) { + let now = Utc::now(); + + if let Ok(mut sessions) = self.sessions.write() { + sessions.retain(|_, session| { + let timeout_for_user = self.get_timeout_for_power(session.power); + let timeout_duration = Duration::minutes(timeout_for_user as i64); + now - session.last_accessed <= timeout_duration + }); + } + } +} |
