rauthy-client 0.14.0

Client for the Rauthy OIDC IAM project
use crate::scim::types::{
    ScimError, ScimGroup, ScimListQuery, ScimListResponse, ScimPatchOp, ScimToken, ScimUser,
    SCIM_SCHEMA_GROUP, SCIM_SCHEMA_PATCH_OP, SCIM_SCHEMA_USER,
};
use crate::scim::{is_content_type_scim, validate_scim_token, CONTENT_TYPE_SCIM};
use axum::extract::{FromRequest, FromRequestParts, Query, Request};
use axum::response::IntoResponse;
use axum::response::Response;
use http::header::CONTENT_TYPE;
use http::request::Parts;
use tracing::error;

impl IntoResponse for ScimError {
    #[inline]
    fn into_response(self) -> Response {
        Response::builder()
            .status(self.status)
            .header(CONTENT_TYPE, CONTENT_TYPE_SCIM)
            .body(serde_json::to_string(&self).unwrap())
            .unwrap()
            .into_response()
    }
}

impl IntoResponse for ScimListResponse {
    #[inline]
    fn into_response(self) -> Response {
        Response::builder()
            .status(200)
            .header(CONTENT_TYPE, CONTENT_TYPE_SCIM)
            .body(serde_json::to_string(&self).unwrap())
            .unwrap()
            .into_response()
    }
}

impl IntoResponse for ScimGroup {
    #[inline]
    fn into_response(self) -> Response {
        Response::builder()
            .status(200)
            .header(CONTENT_TYPE, CONTENT_TYPE_SCIM)
            .body(serde_json::to_string(&self).unwrap())
            .unwrap()
            .into_response()
    }
}

impl IntoResponse for ScimUser {
    #[inline]
    fn into_response(self) -> Response {
        Response::builder()
            .status(200)
            .header(CONTENT_TYPE, CONTENT_TYPE_SCIM)
            .body(serde_json::to_string(&self).unwrap())
            .unwrap()
            .into_response()
    }
}

impl<S> FromRequest<S> for ScimUser
where
    S: Send + Sync,
{
    type Rejection = ScimError;

    #[inline]
    async fn from_request(req: Request, state: &S) -> Result<Self, Self::Rejection> {
        validate_scim_token(req.headers())?;
        is_content_type_scim(req.headers())?;

        match axum::extract::Json::<ScimUser>::from_request(req, state).await {
            Ok(user) => {
                if user.schemas.iter().any(|s| s == SCIM_SCHEMA_USER) {
                    Ok(user.0)
                } else {
                    Err(ScimError::new(
                        400,
                        Some("Invalid Schema for ScimUser".into()),
                    ))
                }
            }
            Err(err) => {
                error!("Cannot extract ScimUser from request body: {:?}", err);
                Err(ScimError::new(
                    400,
                    Some(format!("Cannot extract ScimUser from request body: {:?}", err).into()),
                ))
            }
        }
    }
}

impl<S> FromRequest<S> for ScimGroup
where
    S: Send + Sync,
{
    type Rejection = ScimError;

    #[inline]
    async fn from_request(req: Request, state: &S) -> Result<Self, Self::Rejection> {
        validate_scim_token(req.headers())?;
        is_content_type_scim(req.headers())?;

        match axum::extract::Json::<ScimGroup>::from_request(req, state).await {
            Ok(group) => {
                if group.schemas.iter().any(|s| s == SCIM_SCHEMA_GROUP) {
                    Ok(group.0)
                } else {
                    Err(ScimError::new(
                        400,
                        Some("Invalid Schema for ScimGroup".into()),
                    ))
                }
            }
            Err(err) => {
                error!("Cannot extract ScimGroup from request body: {:?}", err);
                Err(ScimError::new(
                    400,
                    Some(format!("Cannot extract ScimGroup from request body: {:?}", err).into()),
                ))
            }
        }
    }
}

impl<S> FromRequest<S> for ScimPatchOp
where
    S: Send + Sync,
{
    type Rejection = ScimError;

    #[inline]
    async fn from_request(req: Request, state: &S) -> Result<Self, Self::Rejection> {
        println!("in from request for ScimPatchOp");

        validate_scim_token(req.headers())?;
        is_content_type_scim(req.headers())?;

        match axum::extract::Json::<ScimPatchOp>::from_request(req, state).await {
            Ok(op) => {
                if op.schemas.iter().any(|s| s == SCIM_SCHEMA_PATCH_OP) {
                    Ok(op.0)
                } else {
                    Err(ScimError::new(
                        400,
                        Some("Invalid Schema for ScimPatchOp".into()),
                    ))
                }
            }
            Err(err) => {
                error!("Cannot extract ScimPatchOp from request body: {:?}", err);
                Err(ScimError::new(
                    400,
                    Some(format!("Cannot extract ScimPatchOp from request body: {:?}", err).into()),
                ))
            }
        }
    }
}

impl<S> FromRequestParts<S> for ScimListQuery
where
    S: Send + Sync,
{
    type Rejection = ScimError;

    #[inline]
    async fn from_request_parts(parts: &mut Parts, state: &S) -> Result<Self, Self::Rejection> {
        validate_scim_token(&parts.headers)?;

        let params = match Query::<ScimListQuery>::from_request_parts(parts, state).await {
            Ok(query) => query.0,
            Err(_) => ScimListQuery::default(),
        };
        Ok(params)
    }
}

impl<S> FromRequestParts<S> for ScimToken
where
    S: Send + Sync,
{
    type Rejection = ScimError;

    #[inline]
    async fn from_request_parts(parts: &mut Parts, _: &S) -> Result<Self, Self::Rejection> {
        validate_scim_token(&parts.headers)?;
        Ok(ScimToken)
    }
}