//! The admin-session ABSOLUTE TTL cap (audit 2026-07-11 C1, migration 0070). //! //! `PICLOUD_SESSION_ABSOLUTE_TTL_HOURS` is the whole mitigation for a stolen-but- //! warm admin token — the highest-value credential in the system. A session's //! sliding `expires_at` is bumped on every use, but it is clamped at //! `login_time + absolute_ttl`, so even a continuously-used or stolen token self- //! expires. Nothing tested it (grepping `absolute_expires_at` across all tests //! returned zero hits). //! //! The AUTHORITATIVE enforcement is the lookup filter: `admin_session_repo`'s //! `lookup` selects `... AND absolute_expires_at > NOW()`, so a row past its cap //! is invisible no matter how far into the future `touch` has pushed `expires_at`. //! The middleware's `sliding.min(absolute_expires_at)` clamp is defense-in-depth //! on top; the lookup filter is what actually expires the token, and it is what //! these tests pin. Deleting the `absolute_expires_at > NOW()` predicate — the //! plausible regression — makes every past-cap assertion below fail. //! //! Skips cleanly when `DATABASE_URL` is unset. use chrono::{Duration, Utc}; use picloud_manager_core::admin_session_repo::{ AdminSessionRepository, PostgresAdminSessionRepository, }; use picloud_shared::AdminUserId; use uuid::Uuid; async fn mk_admin(pool: &sqlx::PgPool) -> AdminUserId { let uniq = Uuid::new_v4().simple().to_string(); let (id,): (Uuid,) = sqlx::query_as( "INSERT INTO admin_users (username, password_hash) VALUES ($1, 'x') RETURNING id", ) .bind(format!("sat-{uniq}")) .fetch_one(pool) .await .expect("insert admin"); id.into() } /// A session within BOTH windows resolves; one past its absolute cap does NOT — /// even though its sliding `expires_at` is still in the future. That gap (future /// `expires_at`, past `absolute_expires_at`) is exactly the stolen-but-warm token /// the cap exists to kill. #[tokio::test] async fn a_session_past_its_absolute_cap_does_not_resolve() { let Some(pool) = picloud_test_support::test_pool("session_absolute_ttl").await else { return; }; let repo = PostgresAdminSessionRepository::new(pool.clone()); let user = mk_admin(&pool).await; let now = Utc::now(); // Warm and within its cap — resolves. repo.create( user, "hash-live", now + Duration::hours(1), now + Duration::hours(24), ) .await .expect("create live"); let live = repo.lookup("hash-live").await.expect("lookup live"); assert!(live.is_some(), "a session within both windows must resolve"); assert_eq!(live.unwrap().user_id, user); // Sliding window still open (expires_at in the future), but the ABSOLUTE cap // has passed. Must NOT resolve — this is the whole guarantee. repo.create( user, "hash-capped", now + Duration::hours(1), now - Duration::minutes(1), ) .await .expect("create capped"); assert!( repo.lookup("hash-capped") .await .expect("lookup capped") .is_none(), "a warm session (future expires_at) past its absolute cap must self-expire" ); } /// `touch` cannot resurrect a past-cap session. Even after the sliding window is /// bumped far into the future, the lookup's absolute filter keeps the row hidden — /// so a continuously-used stolen token cannot slide past its cap. #[tokio::test] async fn touch_cannot_extend_a_session_past_its_absolute_cap() { let Some(pool) = picloud_test_support::test_pool("session_absolute_ttl").await else { return; }; let repo = PostgresAdminSessionRepository::new(pool.clone()); let user = mk_admin(&pool).await; let now = Utc::now(); // Absolute cap already passed; sliding window open. repo.create( user, "hash-warm", now + Duration::hours(1), now - Duration::minutes(1), ) .await .expect("create warm"); // Simulate the sliding-window bump with an UNCLAMPED value a year out — i.e. // the exact thing the middleware clamp is meant to prevent. Even so: repo.touch("hash-warm", now + Duration::days(365)) .await .expect("touch"); assert!( repo.lookup("hash-warm").await.expect("lookup").is_none(), "even an unclamped touch a year out cannot resurrect a past-cap session — \ the lookup's absolute_expires_at filter is the backstop" ); }