fistinc-auth 0.1.0

Paging types for Fist Inc bank
Documentation
use std::cell::RefCell;
use std::pin::Pin;
use std::rc::Rc;
use std::sync::Arc;
use std::task::{Context, Poll};

use actix::dev::fut::Ready;
use actix_web::{Error, HttpResponse};
use actix_web::dev::{Service, ServiceRequest, ServiceResponse, Transform};
use futures::{Future, future};

use crate::api::models::UserIdentity;
use crate::domain::services::{UserSecurityService, UserService};

pub struct Auth {
    pub user_security_service: Arc<dyn UserSecurityService>,
    pub user_service: Arc<dyn UserService>,
}

impl Auth {
    pub fn new(
        user_security_service: Arc<dyn UserSecurityService>,
        user_service: Arc<dyn UserService>,
    ) -> Self {
        Auth { user_security_service, user_service }
    }
}

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

    fn new_transform(&self, service: S) -> Self::Future {
        future::ok(AuthMiddleWare {
            service: Rc::new(RefCell::new(service)),
            user_security_service: self.user_security_service.clone(),
            user_service: self.user_service.clone(),
        })
    }
}

pub struct AuthMiddleWare<S> {
    service: Rc<RefCell<S>>,
    user_security_service: Arc<dyn UserSecurityService>,
    user_service: Arc<dyn UserService>,
}

impl<S, B> Service for AuthMiddleWare<S>
    where
        S: Service<Request=ServiceRequest, Response=ServiceResponse<B>, Error=Error> + 'static,
        S::Future: 'static,
        B: 'static,
{
    type Request = ServiceRequest;
    type Response = ServiceResponse<B>;
    type Error = Error;
    type Future = Pin<Box<dyn Future<Output=Result<Self::Response, Self::Error>>>>;

    fn poll_ready(&mut self, ctx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
        self.service.poll_ready(ctx)
    }

    fn call(&mut self, req: Self::Request) -> Self::Future {
        let user_security_service = self.user_security_service.clone();
        let user_service = self.user_service.clone();
        let mut service = self.service.clone();
        Box::pin(async move {
            let authorization = req
                .headers()
                .get("authorization")
                .map(|v| v.to_str())
                .unwrap_or(Ok(""))
                .unwrap();
            let parts: Vec<&str> = authorization.split(' ').collect();

            //Validation
            if !(
                parts.len() == 2 &&
                    !parts.is_empty() &&
                    parts[0].to_lowercase() == "bearer"
            ) {
                return Ok(
                    req.into_response(
                        HttpResponse::Unauthorized()
                            .finish()
                            .into_body()
                    )
                );
            }
            let token = parts[1];

            if let Ok(token) = user_security_service.decode_token(token).await {
                if let Ok(Some(user)) = user_service.find_by_email(&token.email).await {
                    let identity = UserIdentity {
                        email: user.email,
                        role: user.role,
                        user_id: user.id,
                    };
                    req.head()
                        .extensions_mut()
                        .insert(identity);
                    let res = service.call(req).await?;
                    return Ok(res);
                }
            }

            Ok(
                req.into_response(
                    HttpResponse::Unauthorized()
                        .finish()
                        .into_body()
                )
            )
        })
    }
}