1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
use std::{
    fmt,
    future::{self, Ready},
    task::{self, Poll},
};

use futures_util::{
    future::{Either, MapErr},
    TryFutureExt as _,
};
use hyper::Request;

use crate::{
    auth::{self, Auth, Config},
    credentials::Credentials,
};

/// Represents an inner service error or Google authentication error.
#[derive(thiserror::Error, Debug)]
pub enum Error<E> {
    #[error("inner service error: {0}")]
    Service(E),
    #[error("google authentication error: {0}")]
    GoogleAuthz(auth::Error),
}

pub struct Builder<S> {
    config: Config,
    credentials: Option<Credentials>,
    service: S,
}

impl Builder<()> {
    pub fn new<S>(service: S) -> Builder<S> {
        Builder { config: Default::default(), credentials: Default::default(), service }
    }
}

impl<S> Builder<S> {
    pub fn enforce_https(mut self, enforce_https: bool) -> Self {
        self.config.enforce_https = enforce_https;
        self
    }

    pub fn credentials(mut self, credentials: impl Into<Option<Credentials>>) -> Self {
        self.credentials = credentials.into();
        self
    }

    pub async fn build<B>(self) -> GoogleAuthz<S>
    where
        S: tower_service::Service<Request<B>>,
    {
        let Builder { config, credentials, service } = self;
        let credentials = match credentials {
            Some(credentials) => credentials,
            None => Credentials::new().await,
        };
        GoogleAuthz { auth: Auth::new(credentials, config), service }
    }
}

pub struct GoogleAuthz<S> {
    auth: Auth,
    service: S,
}

impl GoogleAuthz<()> {
    pub async fn new<S, B>(service: S) -> GoogleAuthz<S>
    where
        S: tower_service::Service<Request<B>>,
    {
        Self::builder(service).build().await
    }

    pub fn builder<S>(service: S) -> Builder<S> {
        Builder::new(service)
    }
}

impl<S: Clone> Clone for GoogleAuthz<S> {
    fn clone(&self) -> Self {
        Self { auth: self.auth.clone(), service: self.service.clone() }
    }
}

impl<S: fmt::Debug> fmt::Debug for GoogleAuthz<S> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("GoogleAuthz")
            .field("auth", &self.auth)
            .field("service", &self.service)
            .finish()
    }
}

impl<S, B> tower_service::Service<Request<B>> for GoogleAuthz<S>
where
    S: tower_service::Service<Request<B>>,
{
    type Response = S::Response;
    type Error = Error<S::Error>;
    #[allow(clippy::type_complexity)]
    type Future = Either<
        MapErr<S::Future, fn(S::Error) -> Self::Error>,
        Ready<Result<Self::Response, Self::Error>>,
    >;

    fn poll_ready(&mut self, cx: &mut task::Context<'_>) -> Poll<Result<(), Self::Error>> {
        match self.auth.poll_ready(cx) {
            Poll::Ready(Ok(())) => self.service.poll_ready(cx).map_err(Error::Service),
            Poll::Ready(Err(err)) => Poll::Ready(Err(Error::GoogleAuthz(err))),
            Poll::Pending => Poll::Pending,
        }
    }

    fn call(&mut self, req: Request<B>) -> Self::Future {
        match self.auth.call(req) {
            Ok(req) => Either::Left(self.service.call(req).map_err(Error::Service)),
            Err(err) => Either::Right(future::ready(Err(Error::GoogleAuthz(err)))),
        }
    }
}