Skip to main content

codlet_sqlx/
session.rs

1//! SQLite implementation of [`codlet_core::store::session::SessionStore`].
2
3use codlet_core::hashing::LookupKey;
4use codlet_core::secret::{SessionId, SubjectId};
5use codlet_core::store::error::StoreError;
6use codlet_core::store::session::{ActiveSessionRecord, SessionRecord, SessionStore};
7
8use crate::SqliteStore;
9
10impl SessionStore for SqliteStore {
11    async fn find_active_session(
12        &self,
13        candidates: &[LookupKey],
14        now: u64,
15    ) -> Result<Option<ActiveSessionRecord>, StoreError> {
16        let now_i = now as i64;
17        for candidate in candidates {
18            let row: Option<(String, String, String, i64)> = sqlx::query_as(
19                "SELECT id, subject, key_version, expires_at
20                 FROM codlet_sessions
21                 WHERE lookup_key  = ?
22                   AND revoked_at  IS NULL
23                   AND expires_at  > ?
24                 LIMIT 1",
25            )
26            .bind(candidate.as_str())
27            .bind(now_i)
28            .fetch_optional(&self.pool)
29            .await
30            .map_err(|e| StoreError::Backend(e.to_string()))?;
31
32            if let Some((id, subject, _kv, exp)) = row {
33                return Ok(Some(ActiveSessionRecord {
34                    id: SessionId::new(id),
35                    subject: SubjectId::new(subject),
36                    expires_at: exp as u64,
37                }));
38            }
39        }
40        Ok(None)
41    }
42
43    async fn insert_session(&self, record: SessionRecord) -> Result<(), StoreError> {
44        sqlx::query(
45            "INSERT INTO codlet_sessions
46             (id, lookup_key, key_version, subject, created_at, expires_at)
47             VALUES (?, ?, ?, ?, ?, ?)",
48        )
49        .bind(record.id.as_str())
50        .bind(record.lookup_key.as_str())
51        .bind(record.key_version.as_str())
52        .bind(record.subject.as_str())
53        .bind(record.created_at as i64)
54        .bind(record.expires_at as i64)
55        .execute(&self.pool)
56        .await
57        .map_err(|e| StoreError::Backend(e.to_string()))?;
58        Ok(())
59    }
60
61    async fn revoke_session(&self, session_id: &SessionId, now: u64) -> Result<(), StoreError> {
62        sqlx::query(
63            "UPDATE codlet_sessions
64             SET revoked_at = ?
65             WHERE id = ? AND revoked_at IS NULL",
66        )
67        .bind(now as i64)
68        .bind(session_id.as_str())
69        .execute(&self.pool)
70        .await
71        .map_err(|e| StoreError::Backend(e.to_string()))?;
72        Ok(())
73    }
74}