use cookie::{Cookie, CookieJar};
use crate::auth::session::CookieSessionService;
use crate::auth::session::CookieSessionsConfig;
use crate::auth::session::meta::SessionMeta;
use crate::auth::session::store::SessionStore;
use crate::cookie::{CookieConfig, Key, key_from_config};
use super::db::TestDb;
const SESSIONS_TABLE_SQL: &str = "CREATE TABLE authenticated_sessions (
id TEXT PRIMARY KEY,
session_token_hash TEXT NOT NULL UNIQUE,
user_id TEXT NOT NULL,
ip_address TEXT NOT NULL,
user_agent TEXT NOT NULL,
device_name TEXT NOT NULL,
device_type TEXT NOT NULL,
fingerprint TEXT NOT NULL,
data TEXT NOT NULL DEFAULT '{}',
created_at TEXT NOT NULL,
last_active_at TEXT NOT NULL,
expires_at TEXT NOT NULL
)";
const SESSIONS_INDEXES_SQL: &[&str] = &[
"CREATE INDEX idx_sessions_user_id ON authenticated_sessions (user_id)",
"CREATE INDEX idx_sessions_expires_at ON authenticated_sessions (expires_at)",
];
pub struct TestSession {
store: SessionStore,
key: Key,
session_config: CookieSessionsConfig,
service: CookieSessionService,
}
impl TestSession {
pub const SCHEMA_SQL: &'static str = SESSIONS_TABLE_SQL;
pub const INDEXES_SQL: &'static [&'static str] = SESSIONS_INDEXES_SQL;
pub async fn new(db: &TestDb) -> Self {
let cookie_config = CookieConfig {
secret: "a".repeat(64),
secure: false,
http_only: true,
same_site: "lax".to_string(),
};
Self::with_config(db, CookieSessionsConfig::default(), cookie_config).await
}
pub async fn with_config(
db: &TestDb,
session_config: CookieSessionsConfig,
cookie_config: CookieConfig,
) -> Self {
use crate::db::ConnExt;
db.db()
.conn()
.execute_raw(SESSIONS_TABLE_SQL, ())
.await
.expect("failed to create sessions table");
for sql in SESSIONS_INDEXES_SQL {
db.db()
.conn()
.execute_raw(sql, ())
.await
.expect("failed to create sessions index");
}
let key = key_from_config(&cookie_config).expect("failed to derive cookie key");
let database = db.db();
let store = SessionStore::new(database.clone(), session_config.clone());
let mut svc_config = session_config.clone();
svc_config.cookie = cookie_config;
let service = CookieSessionService::new(database, svc_config)
.expect("failed to build CookieSessionService");
Self {
store,
key,
session_config,
service,
}
}
pub async fn authenticate(&self, user_id: &str) -> String {
self.authenticate_with(user_id, serde_json::json!({})).await
}
pub async fn authenticate_with(&self, user_id: &str, data: serde_json::Value) -> String {
let meta = SessionMeta::from_headers("127.0.0.1".to_string(), "", "", "");
let (_session_data, token): (crate::auth::session::store::SessionData, _) = self
.store
.create(&meta, user_id, Some(data))
.await
.expect("failed to create test session");
let cookie_name = &self.session_config.cookie_name;
let mut jar = CookieJar::new();
jar.signed_mut(&self.key)
.add(Cookie::new(cookie_name.to_string(), token.as_hex()));
let signed_value = jar
.get(cookie_name)
.expect("cookie was just added")
.value()
.to_string();
format!("{cookie_name}={signed_value}")
}
pub fn layer(&self) -> crate::auth::session::CookieSessionLayer {
self.service.layer()
}
pub fn service(&self) -> &CookieSessionService {
&self.service
}
}