aboutsummaryrefslogtreecommitdiff
path: root/src/auth/session.rs
blob: 277cef298fddf3411d35c53196d8a0aa8e1e9839 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
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
            });
        }
    }
}