axum-security 0.0.2

A security toolbox for the Axum library
Documentation
mod memory;

use std::{pin::Pin, sync::Arc};

use axum::response::{IntoResponse, Response};
pub use memory::MemStore;

use crate::cookie::{CookieSession, SessionId};

pub trait CookieStore: Send + Sync + 'static {
    type State: Send + Sync + 'static;
    type Error: IntoResponse + Send + 'static;

    fn spawn_maintenance_task(&self) -> bool {
        true
    }

    fn store_session(
        &self,
        session: CookieSession<Self::State>,
    ) -> impl Future<Output = Result<(), Self::Error>> + Send;

    fn remove_session(
        &self,
        id: &SessionId,
    ) -> impl Future<Output = Result<Option<CookieSession<Self::State>>, Self::Error>> + Send;

    fn load_session(
        &self,
        id: &SessionId,
    ) -> impl Future<Output = Result<Option<CookieSession<Self::State>>, Self::Error>> + Send;

    fn remove_before(&self, deadline: u64) -> impl Future<Output = Result<(), Self::Error>> + Send;
}

#[allow(clippy::type_complexity)]
trait DynStore<S>: Send + Sync + 'static {
    fn spawn_maintenance_task(&self) -> bool;

    fn store_session(
        &self,
        session: CookieSession<S>,
    ) -> Pin<Box<dyn Future<Output = Result<(), Response>> + Send + '_>>;

    fn remove_session<'a>(
        &'a self,
        id: &'a SessionId,
    ) -> Pin<Box<dyn Future<Output = Result<Option<CookieSession<S>>, Response>> + Send + 'a>>;

    fn load_session<'a>(
        &'a self,
        id: &'a SessionId,
    ) -> Pin<Box<dyn Future<Output = Result<Option<CookieSession<S>>, Response>> + Send + 'a>>;

    fn remove_before(
        &self,
        deadline: u64,
    ) -> Pin<Box<dyn Future<Output = Result<(), Response>> + Send + '_>>;
}

impl<T> DynStore<T::State> for T
where
    T: CookieStore,
{
    fn spawn_maintenance_task(&self) -> bool {
        <T as CookieStore>::spawn_maintenance_task(self)
    }

    fn store_session(
        &self,
        session: CookieSession<T::State>,
    ) -> Pin<Box<dyn Future<Output = Result<(), Response>> + Send + '_>> {
        Box::pin(async move {
            <T as CookieStore>::store_session(self, session)
                .await
                .map_err(IntoResponse::into_response)
        })
    }

    fn remove_session<'a>(
        &'a self,
        id: &'a SessionId,
    ) -> Pin<Box<dyn Future<Output = Result<Option<CookieSession<T::State>>, Response>> + Send + 'a>>
    {
        Box::pin(async move {
            <T as CookieStore>::remove_session(self, id)
                .await
                .map_err(IntoResponse::into_response)
        })
    }

    fn load_session<'a>(
        &'a self,
        id: &'a SessionId,
    ) -> Pin<Box<dyn Future<Output = Result<Option<CookieSession<T::State>>, Response>> + Send + 'a>>
    {
        Box::pin(async move {
            <T as CookieStore>::load_session(self, id)
                .await
                .map_err(IntoResponse::into_response)
        })
    }

    fn remove_before(
        &self,
        deadline: u64,
    ) -> Pin<Box<dyn Future<Output = Result<(), Response>> + Send + '_>> {
        Box::pin(async move {
            <T as CookieStore>::remove_before(self, deadline)
                .await
                .map_err(IntoResponse::into_response)
        })
    }
}

pub(crate) struct ErasedStore<S>(Arc<dyn DynStore<S>>);

impl<S: 'static> ErasedStore<S> {
    pub fn new(store: S) -> ErasedStore<S::State>
    where
        S: CookieStore,
    {
        ErasedStore(Arc::new(store))
    }

    pub fn spawn_maintenance_task(&self) -> bool {
        self.0.spawn_maintenance_task()
    }

    pub async fn store_session(&self, session: CookieSession<S>) -> Result<(), Response> {
        self.0.store_session(session).await
    }

    pub async fn remove_session(
        &self,
        id: &SessionId,
    ) -> Result<Option<CookieSession<S>>, Response> {
        self.0.remove_session(id).await
    }

    pub async fn load_session(&self, id: &SessionId) -> Result<Option<CookieSession<S>>, Response> {
        self.0.load_session(id).await
    }

    pub async fn remove_before(&self, deadline: u64) -> Result<(), Response> {
        self.0.remove_before(deadline).await
    }
}

impl<S> Clone for ErasedStore<S> {
    fn clone(&self) -> Self {
        ErasedStore(self.0.clone())
    }
}