torii_core/session/
opaque.rs

1//! Opaque session provider implementation
2//!
3//! This module provides a stateful session provider using opaque tokens
4//! backed by persistent storage (database).
5
6use std::sync::Arc;
7
8use async_trait::async_trait;
9use chrono::{Duration, Utc};
10
11use crate::{
12    Error, Session, SessionStorage, SessionToken, UserId,
13    error::{SessionError, StorageError},
14};
15
16use super::provider::SessionProvider;
17
18/// Opaque token session provider
19///
20/// This provider creates random opaque tokens and stores session data
21/// in a persistent storage backend. Each session lookup requires a
22/// database query.
23pub struct OpaqueSessionProvider<S: SessionStorage> {
24    storage: Arc<S>,
25}
26
27impl<S: SessionStorage> OpaqueSessionProvider<S> {
28    /// Create a new opaque session provider with the given storage backend
29    pub fn new(storage: Arc<S>) -> Self {
30        Self { storage }
31    }
32}
33
34#[async_trait]
35impl<S: SessionStorage> SessionProvider for OpaqueSessionProvider<S> {
36    async fn create_session(
37        &self,
38        user_id: &UserId,
39        user_agent: Option<String>,
40        ip_address: Option<String>,
41        duration: Duration,
42    ) -> Result<Session, Error> {
43        let session = Session::builder()
44            .token(SessionToken::new_random())
45            .user_id(user_id.clone())
46            .user_agent(user_agent)
47            .ip_address(ip_address)
48            .expires_at(Utc::now() + duration)
49            .build()?;
50
51        let session = self
52            .storage
53            .create_session(&session)
54            .await
55            .map_err(|e| StorageError::Database(e.to_string()))?;
56
57        Ok(session)
58    }
59
60    async fn get_session(&self, token: &SessionToken) -> Result<Session, Error> {
61        let session = self
62            .storage
63            .get_session(token)
64            .await
65            .map_err(|e| StorageError::Database(e.to_string()))?;
66
67        if let Some(session) = session {
68            if session.is_expired() {
69                self.delete_session(token).await?;
70                return Err(Error::Session(SessionError::Expired));
71            }
72            Ok(session)
73        } else {
74            Err(Error::Session(SessionError::NotFound))
75        }
76    }
77
78    async fn delete_session(&self, token: &SessionToken) -> Result<(), Error> {
79        self.storage
80            .delete_session(token)
81            .await
82            .map_err(|e| StorageError::Database(e.to_string()))?;
83
84        Ok(())
85    }
86
87    async fn cleanup_expired_sessions(&self) -> Result<(), Error> {
88        self.storage
89            .cleanup_expired_sessions()
90            .await
91            .map_err(|e| StorageError::Database(e.to_string()))?;
92
93        Ok(())
94    }
95
96    async fn delete_sessions_for_user(&self, user_id: &UserId) -> Result<(), Error> {
97        self.storage
98            .delete_sessions_for_user(user_id)
99            .await
100            .map_err(|e| StorageError::Database(e.to_string()))?;
101
102        Ok(())
103    }
104}