Microservice to bring 2FA to self hosted PDSes
at feature/admin-rbac 79 lines 2.2 kB view raw
1use anyhow::Result; 2use chrono::Utc; 3use sqlx::SqlitePool; 4use uuid::Uuid; 5 6#[derive(Debug, Clone, sqlx::FromRow)] 7pub struct AdminSessionRow { 8 pub session_id: String, 9 pub did: String, 10 pub handle: String, 11 pub oauth_session_id: String, 12 pub created_at: String, 13 pub expires_at: String, 14} 15 16/// Creates a new admin session and returns the generated session_id. 17pub async fn create_session( 18 pool: &SqlitePool, 19 did: &str, 20 handle: &str, 21 oauth_session_id: &str, 22 ttl_hours: u64, 23) -> Result<String> { 24 let session_id = Uuid::new_v4().to_string(); 25 let now = Utc::now(); 26 let created_at = now.to_rfc3339(); 27 let expires_at = (now + chrono::Duration::hours(ttl_hours as i64)).to_rfc3339(); 28 29 sqlx::query( 30 "INSERT INTO admin_sessions (session_id, did, handle, oauth_session_id, created_at, expires_at) VALUES (?, ?, ?, ?, ?, ?)", 31 ) 32 .bind(&session_id) 33 .bind(did) 34 .bind(handle) 35 .bind(oauth_session_id) 36 .bind(&created_at) 37 .bind(&expires_at) 38 .execute(pool) 39 .await?; 40 41 Ok(session_id) 42} 43 44/// Looks up a session by ID. Returns None if the session does not exist or has expired. 45pub async fn get_session(pool: &SqlitePool, session_id: &str) -> Result<Option<AdminSessionRow>> { 46 let now = Utc::now().to_rfc3339(); 47 48 let row = sqlx::query_as::<_, AdminSessionRow>( 49 "SELECT session_id, did, handle, oauth_session_id, created_at, expires_at FROM admin_sessions WHERE session_id = ? AND expires_at > ?", 50 ) 51 .bind(session_id) 52 .bind(&now) 53 .fetch_optional(pool) 54 .await?; 55 56 Ok(row) 57} 58 59/// Deletes a session by ID. 60pub async fn delete_session(pool: &SqlitePool, session_id: &str) -> Result<()> { 61 sqlx::query("DELETE FROM admin_sessions WHERE session_id = ?") 62 .bind(session_id) 63 .execute(pool) 64 .await?; 65 66 Ok(()) 67} 68 69/// Deletes all expired sessions and returns the number of rows removed. 70pub async fn cleanup_expired_sessions(pool: &SqlitePool) -> Result<u64> { 71 let now = Utc::now().to_rfc3339(); 72 73 let result = sqlx::query("DELETE FROM admin_sessions WHERE expires_at <= ?") 74 .bind(&now) 75 .execute(pool) 76 .await?; 77 78 Ok(result.rows_affected()) 79}