Skip to main content

rama_net/user/layer/
username_parser.rs

1use core::{fmt, marker::PhantomData};
2
3use crate::user::UserId;
4
5use rama_core::{
6    Layer, Service,
7    extensions::{Extensions, ExtensionsRef},
8    telemetry::tracing,
9    username::{UsernameLabelParser, parse_username},
10};
11/// Layer which can be used to add parser capabilities to any service
12/// stack which injects a [`UserId`] into the input.
13///
14/// For most use-cases you do not need this layer at all.
15/// Http and socks5 support by rama already can handle parsers out of the box:
16///
17/// - for the http proxy you can do it directly within the proxy acceptor layer;
18/// - for the socks5 proxy you would do the parsing as part of your authorizer implementation.
19///
20/// If this is not the case you will have to add username label capabilities
21/// to your authorizer. Sadly not all authorizer traits allow
22/// adding extensions. This is probably a shortcoming which should be fixed at some point.
23/// Feel free to feature request this.
24#[derive(Default)]
25pub struct UsernameLabelParserLayer<P> {
26    _parser: PhantomData<fn() -> P>,
27}
28
29impl<P> UsernameLabelParserLayer<P> {
30    #[inline(always)]
31    #[must_use]
32    pub fn new() -> Self {
33        Self {
34            _parser: PhantomData,
35        }
36    }
37}
38
39impl<P> fmt::Debug for UsernameLabelParserLayer<P> {
40    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
41        f.debug_struct("UsernameLabelParserLayer")
42            .field("parser", &core::any::type_name::<P>())
43            .finish()
44    }
45}
46
47impl<P> Clone for UsernameLabelParserLayer<P> {
48    fn clone(&self) -> Self {
49        Self {
50            _parser: PhantomData,
51        }
52    }
53}
54
55impl<S, P> Layer<S> for UsernameLabelParserLayer<P> {
56    type Service = UsernameLabelParserService<S, P>;
57
58    fn layer(&self, inner: S) -> Self::Service {
59        Self::Service {
60            inner,
61            _parser: PhantomData,
62        }
63    }
64}
65
66/// [`Service`] which can be used to add parser capabilities to any service
67/// stack which injects a [`UserId`] into the input.
68///
69/// See [`UsernameLabelParserLayer`] for more info.
70pub struct UsernameLabelParserService<S, P> {
71    inner: S,
72    _parser: PhantomData<fn() -> P>,
73}
74
75impl<S: fmt::Debug, P> fmt::Debug for UsernameLabelParserService<S, P> {
76    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
77        f.debug_struct("UsernameLabelParserService")
78            .field("inner", &self.inner)
79            .field("parser", &core::any::type_name::<P>())
80            .finish()
81    }
82}
83
84impl<S: Clone, P> Clone for UsernameLabelParserService<S, P> {
85    fn clone(&self) -> Self {
86        Self {
87            inner: self.inner.clone(),
88            _parser: PhantomData,
89        }
90    }
91}
92
93impl<S, P, Input> Service<Input> for UsernameLabelParserService<S, P>
94where
95    S: Service<Input>,
96    P: UsernameLabelParser,
97    Input: ExtensionsRef,
98{
99    type Output = S::Output;
100    type Error = S::Error;
101
102    fn serve(
103        &self,
104        input: Input,
105    ) -> impl Future<Output = Result<Self::Output, Self::Error>> + Send + '_ {
106        let extensions = input.extensions();
107        match extensions.get_ref() {
108            Some(UserId::Username(username)) => {
109                let label_extensions = Extensions::new();
110                match parse_username(&label_extensions, P::default(), username) {
111                    Ok(new_username) => {
112                        tracing::debug!(
113                            "username label parser: success: overwrite id username '{username}' with '{new_username}'"
114                        );
115                        extensions.insert(UserId::Username(new_username));
116                        extensions.extend(&label_extensions);
117                    }
118                    Err(err) => {
119                        tracing::debug!(
120                            "failed to parse username labels, keep existing username: '{username}'; err = {err}"
121                        );
122                    }
123                }
124            }
125            Some(UserId::Token(_)) => {
126                tracing::debug!("no parsing to do, incompatible user id in input: token");
127            }
128            None | Some(UserId::Anonymous) => {
129                tracing::debug!("no parsing to do, incompatible user id in input: none/anonymous");
130            }
131        }
132
133        self.inner.serve(input)
134    }
135}