pooly 0.2.1

A protobuf to Postgres adapter + connection pooling middleware.
Documentation
use std::future::{ready, Ready};
use std::rc::Rc;

use actix_web::{dev::{forward_ready, Service, ServiceRequest, ServiceResponse, Transform}, Error};
use actix_web::body::EitherBody;
use futures_util::future::LocalBoxFuture;
use futures_util::FutureExt;

use crate::models::auth::api_key::InitializeApiKey;
use crate::models::errors::AuthError;

const AUTHORIZATION: &str = "Authorization";

pub struct InitializationApiKeyAuthGuard {

    api_key: InitializeApiKey

}

impl InitializationApiKeyAuthGuard {

    pub fn new(api_key: InitializeApiKey) -> InitializationApiKeyAuthGuard {
        InitializationApiKeyAuthGuard {
            api_key
        }
    }

}

impl<S, B> Transform<S, ServiceRequest> for InitializationApiKeyAuthGuard
    where
        S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error> + 'static,
        S::Future: 'static,
        B: 'static,
{
    type Response = ServiceResponse<EitherBody<B>>;
    type Error = Error;
    type Transform = InitializationGuardMiddleware<S>;
    type InitError = ();
    type Future = Ready<Result<Self::Transform, Self::InitError>>;

    fn new_transform(&self, service: S) -> Self::Future {
        ready(
            Ok(
                InitializationGuardMiddleware {
                    validator: Rc::new(ApiKeyValidator { api_key: self.api_key.clone() }),
                    service: Rc::new(service)
                }
            )
        )
    }
}

pub struct InitializationGuardMiddleware<S> {

    validator: Rc<ApiKeyValidator>,
    service: Rc<S>

}

impl<S, B> Service<ServiceRequest> for InitializationGuardMiddleware<S>
    where
        S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error> + 'static,
        S::Future: 'static,
        B: 'static,
{
    type Response = ServiceResponse<EitherBody<B>>;
    type Error = Error;
    type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;

    forward_ready!(service);

    fn call(&self, req: ServiceRequest) -> Self::Future {
        let service = Rc::clone(&self.service);
        let validator = Rc::clone(&self.validator);

        async move {
            match validator.validate(&req) {
                Ok(_) => service.call(req).await.map(|res| res.map_into_left_body()),
                Err(err) => Ok(req.error_response(err).map_into_right_body())
            }
        }.boxed_local()
    }
}

struct ApiKeyValidator {

    api_key: InitializeApiKey

}

impl ApiKeyValidator {

    fn validate(&self,
                req: &ServiceRequest) -> Result<(), AuthError> {
        match req.headers().get(AUTHORIZATION) {
            None => Err(AuthError::MissingAuthHeader),
            Some(auth_header_value) => {
                if self.api_key
                    .get_value()
                    .ne(auth_header_value.to_str().map_err(|_| AuthError::InvalidHeader)?) {
                    return Err(AuthError::Forbidden);
                }

                Ok(())
            }
        }
    }

}