Skip to main content

rama_http/layer/
proxy_auth.rs

1//! Middleware that validates if a request has the appropriate Proxy Authorisation.
2//!
3//! If the request is not authorized a `407 Proxy Authentication Required` response will be sent.
4
5use crate::header::PROXY_AUTHENTICATE;
6use crate::headers::authorization::Authority;
7use crate::headers::{HeaderMapExt, ProxyAuthorization, authorization::Credentials};
8use crate::{Request, Response, StatusCode};
9use rama_core::error::{BoxError, ErrorContext as _};
10use rama_core::extensions::{Extension, ExtensionsRef};
11use rama_core::{Layer, Service};
12use rama_http_types::body::OptionalBody;
13use rama_net::user::UserId;
14use rama_utils::macros::define_inner_service_accessors;
15use std::fmt;
16use std::marker::PhantomData;
17
18/// Layer that applies the [`ProxyAuthService`] middleware which apply a timeout to requests.
19///
20/// See the [module docs](super) for an example.
21pub struct ProxyAuthLayer<A, C, L = ()> {
22    proxy_auth: A,
23    allow_anonymous: bool,
24    _phantom: PhantomData<fn(C, L) -> ()>,
25}
26
27impl<A: fmt::Debug, C, L> fmt::Debug for ProxyAuthLayer<A, C, L> {
28    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
29        f.debug_struct("ProxyAuthLayer")
30            .field("proxy_auth", &self.proxy_auth)
31            .field(
32                "_phantom",
33                &format_args!("{}", std::any::type_name::<fn(C, L) -> ()>()),
34            )
35            .finish()
36    }
37}
38
39impl<A: Clone, C, L> Clone for ProxyAuthLayer<A, C, L> {
40    fn clone(&self) -> Self {
41        Self {
42            proxy_auth: self.proxy_auth.clone(),
43            allow_anonymous: self.allow_anonymous,
44            _phantom: PhantomData,
45        }
46    }
47}
48
49impl<A, C> ProxyAuthLayer<A, C, ()> {
50    /// Creates a new [`ProxyAuthLayer`].
51    pub const fn new(proxy_auth: A) -> Self {
52        Self {
53            proxy_auth,
54            allow_anonymous: false,
55            _phantom: PhantomData,
56        }
57    }
58
59    rama_utils::macros::generate_set_and_with! {
60        /// Allow anonymous requests.
61        pub fn allow_anonymous(mut self, allow_anonymous: bool) -> Self {
62            self.allow_anonymous = allow_anonymous;
63            self
64        }
65    }
66}
67
68impl<A, C, L> ProxyAuthLayer<A, C, L> {
69    /// Overwrite the Labels extract type
70    ///
71    /// This is used if the username contains labels that you need to extract out.
72    /// Example implementation is the [`UsernameOpaqueLabelParser`].
73    ///
74    /// You can provide your own extractor by implementing the [`UsernameLabelParser`] trait.
75    ///
76    /// [`UsernameOpaqueLabelParser`]: rama_core::username::UsernameOpaqueLabelParser
77    /// [`UsernameLabelParser`]: rama_core::username::UsernameLabelParser
78    pub fn with_labels<L2>(self) -> ProxyAuthLayer<A, C, L2> {
79        ProxyAuthLayer {
80            proxy_auth: self.proxy_auth,
81            allow_anonymous: self.allow_anonymous,
82            _phantom: PhantomData,
83        }
84    }
85}
86
87impl<A, C, L, S> Layer<S> for ProxyAuthLayer<A, C, L>
88where
89    A: Authority<C, L> + Clone,
90    C: Credentials + Clone + Send + Sync + 'static,
91{
92    type Service = ProxyAuthService<A, C, S, L>;
93
94    fn layer(&self, inner: S) -> Self::Service {
95        ProxyAuthService::new(self.proxy_auth.clone(), inner)
96            .with_allow_anonymous(self.allow_anonymous)
97    }
98
99    fn into_layer(self, inner: S) -> Self::Service {
100        ProxyAuthService::new(self.proxy_auth, inner).with_allow_anonymous(self.allow_anonymous)
101    }
102}
103
104/// Middleware that validates if a request has the appropriate Proxy Authorisation.
105///
106/// If the request is not authorized a `407 Proxy Authentication Required` response will be sent.
107/// If `allow_anonymous` is set to `true` then requests without a Proxy Authorization header will be
108/// allowed and the user will be authoized as [`UserId::Anonymous`].
109///
110/// See the [module docs](self) for an example.
111pub struct ProxyAuthService<A, C, S, L = ()> {
112    proxy_auth: A,
113    allow_anonymous: bool,
114    inner: S,
115    _phantom: PhantomData<fn(C, L) -> ()>,
116}
117
118impl<A, C, S, L> ProxyAuthService<A, C, S, L> {
119    /// Creates a new [`ProxyAuthService`].
120    pub const fn new(proxy_auth: A, inner: S) -> Self {
121        Self {
122            proxy_auth,
123            allow_anonymous: false,
124            inner,
125            _phantom: PhantomData,
126        }
127    }
128
129    rama_utils::macros::generate_set_and_with! {
130        /// Allow anonymous requests.
131        pub fn allow_anonymous(mut self, allow_anonymous: bool) -> Self {
132            self.allow_anonymous = allow_anonymous;
133            self
134        }
135    }
136
137    define_inner_service_accessors!();
138}
139
140impl<A: fmt::Debug, C, S: fmt::Debug, L> fmt::Debug for ProxyAuthService<A, C, S, L> {
141    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
142        f.debug_struct("ProxyAuthService")
143            .field("proxy_auth", &self.proxy_auth)
144            .field("allow_anonymous", &self.allow_anonymous)
145            .field("inner", &self.inner)
146            .field(
147                "_phantom",
148                &format_args!("{}", std::any::type_name::<fn(C, L) -> ()>()),
149            )
150            .finish()
151    }
152}
153
154impl<A: Clone, C, S: Clone, L> Clone for ProxyAuthService<A, C, S, L> {
155    fn clone(&self) -> Self {
156        Self {
157            proxy_auth: self.proxy_auth.clone(),
158            allow_anonymous: self.allow_anonymous,
159            inner: self.inner.clone(),
160            _phantom: PhantomData,
161        }
162    }
163}
164
165impl<A, C, L, S, ReqBody, ResBody> Service<Request<ReqBody>> for ProxyAuthService<A, C, S, L>
166where
167    A: Authority<C, L>,
168    C: Credentials + Extension + Clone,
169    S: Service<Request<ReqBody>, Output = Response<ResBody>, Error: Into<BoxError>>,
170    L: 'static,
171    ReqBody: Send + 'static,
172    ResBody: Send + 'static,
173{
174    type Output = Response<OptionalBody<ResBody>>;
175    type Error = BoxError;
176
177    async fn serve(&self, req: Request<ReqBody>) -> Result<Self::Output, Self::Error> {
178        if let Some(credentials) = req
179            .headers()
180            .typed_get::<ProxyAuthorization<C>>()
181            .map(|h| h.0)
182            .or_else(|| req.extensions().get_ref::<C>().cloned())
183        {
184            if let Some(ext) = self.proxy_auth.authorized(credentials).await {
185                req.extensions().extend(&ext);
186                Ok(self
187                    .inner
188                    .serve(req)
189                    .await
190                    .into_box_error()?
191                    .map(OptionalBody::some))
192            } else {
193                Ok(Response::builder()
194                    .status(StatusCode::PROXY_AUTHENTICATION_REQUIRED)
195                    .header(PROXY_AUTHENTICATE, C::SCHEME)
196                    .body(OptionalBody::none())
197                    .context("create auth-required response")?)
198            }
199        } else if self.allow_anonymous {
200            req.extensions().insert(UserId::Anonymous);
201            Ok(self
202                .inner
203                .serve(req)
204                .await
205                .into_box_error()?
206                .map(OptionalBody::some))
207        } else {
208            Ok(Response::builder()
209                .status(StatusCode::PROXY_AUTHENTICATION_REQUIRED)
210                .header(PROXY_AUTHENTICATE, C::SCHEME)
211                .body(OptionalBody::none())
212                .context("create auth-required response")?)
213        }
214    }
215}