use async_session::{Result, Session, SessionStore}; use async_trait::async_trait; use shared_svc::cache::{CacheKey, RedisManager, redis::AsyncCommands}; use sqlx::{PgPool, Postgres, Transaction}; use tracing::{debug, instrument}; use crate::Provider; #[async_trait] pub trait AccountMgr { async fn get_apid_by_email(&self, email: &str) -> Result>; async fn create_account(&self, provider: Provider, provider_user_id: &str, ap_id: &str); async fn create_account_step( &self, provider: Provider, provider_user_id: &str, ap_id: &str, email: &str, transaction: &mut Transaction<'_, Postgres>, ) -> Result; async fn persist_session(&self); } #[derive(Debug, Clone)] pub struct AuthService { cache: RedisManager, database: PgPool, } #[async_trait] impl AccountMgr for AuthService { #[instrument(skip(self))] async fn get_apid_by_email(&self, email: &str) -> Result> { todo!() } #[instrument(skip(self))] async fn create_account(&self, provider: Provider, provider_user_id: &str, ap_id: &str) { todo!() } #[instrument(skip(self, transaction))] async fn create_account_step( &self, provider: Provider, provider_user_id: &str, ap_id: &str, email: &str, transaction: &mut Transaction<'_, Postgres>, ) -> Result { sqlx::query!( "insert into account (provider_id, provider_user_id, email, ap_id) values ($1, $2, $3, $4) ", "", provider_user_id, "", ap_id ) .execute(&mut **transaction) .await?; todo!() } #[instrument(skip(self))] async fn persist_session(&self) {} } impl AuthService { pub fn new(cache: &RedisManager, database: &PgPool) -> Self { Self { cache: cache.clone(), database: database.clone(), } } } #[async_trait] impl SessionStore for AuthService { #[doc = " Get a session from the storage backend."] #[doc = ""] #[doc = " The input is expected to be the value of an identifying"] #[doc = " cookie. This will then be parsed by the session middleware"] #[doc = " into a session if possible"] #[instrument(skip(self, cookie_value))] async fn load_session(&self, cookie_value: String) -> Result> { debug!("getting session"); let id = Session::id_from_cookie_value(&cookie_value)?; let mut client = self.cache.get().await?; let session = client .get::<_, Option>>(CacheKey::Session(&id)) .await?; match session { Some(value) => Ok(Some(serde_json::from_slice(&value)?)), None => Ok(None), } } #[doc = " Store a session on the storage backend."] #[doc = ""] #[doc = " The return value is the value of the cookie to store for the"] #[doc = " user that represents this session"] #[instrument(err(Debug), skip(self, session), fields(id = session.id()))] async fn store_session(&self, session: Session) -> Result> { debug!("storing session"); let mut client = self.cache.get().await?; let bytes = serde_json::to_vec(&session)?; client .set_ex::<_, _, ()>(CacheKey::Session(session.id()), bytes, 3600) .await?; Ok(session.into_cookie_value()) } #[doc = " Remove a session from the session store"] #[instrument(err(Debug), skip(self, session), fields(id = session.id()))] async fn destroy_session(&self, session: Session) -> Result { debug!("destroying session"); let mut client = self.cache.get().await?; client.del::<_, ()>(CacheKey::Session(session.id())).await?; Ok(()) } #[doc = " Empties the entire store, destroying all sessions"] #[instrument(skip(self))] async fn clear_store(&self) -> Result { debug!("clearing store"); let mut client = self.cache.get().await?; client.del::<_, ()>(CacheKey::Session("").key()).await?; Ok(()) } }