seuss 0.3.1

A batteries-included framework for implementing Redfish-compliant services.
Documentation
use axum::{http::request::Parts, response::Response};
use redfish_codegen::models::{odata_v4, redfish, session::v1_6_0};
use redfish_core::auth::{
    unauthorized, unauthorized_with_error, AuthenticateRequest, AuthenticatedUser,
};

pub trait SessionManagement {
    type Id;
    fn session_is_valid(
        &self,
        token: String,
        origin: Option<String>,
    ) -> Result<AuthenticatedUser, redfish::Error>;
    fn sessions(&self) -> Result<Vec<odata_v4::IdRef>, redfish::Error>;
    fn create_session(
        &mut self,
        session: v1_6_0::Session,
        base_path: String,
    ) -> Result<v1_6_0::Session, redfish::Error>;
    fn get_session(&self, id: Self::Id) -> Result<v1_6_0::Session, redfish::Error>;
    fn delete_session(&mut self, id: Self::Id) -> Result<(), redfish::Error>;
}

pub trait SessionAuthentication {
    fn open_session(
        &self,
        username: String,
        password: String,
    ) -> Result<AuthenticatedUser, redfish::Error>;
    fn close_session(&self) -> Result<(), redfish::Error>;
}

#[derive(Clone)]
pub struct SessionAuthenticationProxy<S>
where
    S: SessionManagement + Clone,
{
    authenticator: S,
}

impl<'a, S> AsRef<dyn AuthenticateRequest + 'a> for SessionAuthenticationProxy<S>
where
    S: SessionManagement + Clone + 'a,
{
    fn as_ref(&self) -> &(dyn AuthenticateRequest + 'a) {
        self
    }
}

impl<S> SessionAuthenticationProxy<S>
where
    S: SessionManagement + Clone,
{
    pub fn new(authenticator: S) -> Self {
        Self { authenticator }
    }
}

impl<S> AuthenticateRequest for SessionAuthenticationProxy<S>
where
    S: SessionManagement + Clone,
{
    fn authenticate_request(
        &self,
        parts: &mut Parts,
    ) -> Result<Option<AuthenticatedUser>, Response> {
        let token = parts
            .headers
            .get("X-Auth-Token")
            .ok_or_else(|| unauthorized(&self.challenge()))?
            .to_str()
            .map_err(|_| unauthorized(&self.challenge()))?
            .to_string();

        let origin = parts
            .headers
            .get("Origin")
            .map(|value| {
                Ok(value
                    .to_str()
                    .map_err(|_| unauthorized(&self.challenge()))?
                    .to_string())
            })
            .transpose()?;

        self.authenticator
            .session_is_valid(token, origin)
            .map(Some)
            .map_err(|error| unauthorized_with_error(error, &self.challenge()))
    }

    fn challenge(&self) -> Vec<&'static str> {
        vec!["Session"]
    }
}