rama_net/user/credentials/
proxy.rs

1use super::{Basic, Bearer};
2use rama_core::error::{ErrorContext, OpaqueError};
3use std::borrow::Cow;
4
5#[cfg(feature = "http")]
6use rama_http_types::HeaderValue;
7
8#[derive(Debug, Clone, PartialEq, Eq)]
9/// Proxy credentials.
10pub enum ProxyCredential {
11    /// [`Basic`]` credentials.
12    Basic(Basic),
13    /// [`Bearer`] credentials.
14    Bearer(Bearer),
15}
16
17impl From<Basic> for ProxyCredential {
18    fn from(basic: Basic) -> Self {
19        Self::Basic(basic)
20    }
21}
22
23impl From<Bearer> for ProxyCredential {
24    fn from(bearer: Bearer) -> Self {
25        Self::Bearer(bearer)
26    }
27}
28
29impl ProxyCredential {
30    /// Try to create a [`ProxyCredential`] from a header string,
31    /// which is expected to be either a [`Basic`] or [`Bearer`] credential.
32    pub fn try_from_header_str(value: impl AsRef<str>) -> Result<Self, OpaqueError> {
33        let value = value.as_ref();
34        Basic::try_from_header_str(value)
35            .map(Into::into)
36            .or_else(|_| Bearer::try_from_header_str(value).map(Into::into))
37            .context("try to construct proxy credentials from header str")
38    }
39
40    /// Try to create a [`Bearer`] from a [`&'static str`][str] or [`String`].
41    pub fn try_from_clear_str(s: impl Into<Cow<'static, str>>) -> Result<Self, OpaqueError> {
42        let s: Cow<'static, str> = s.into();
43        if s.contains(':') {
44            Basic::try_from_clear_str(s.into_owned()).map(Into::into)
45        } else {
46            Bearer::try_from_clear_str(s).map(Into::into)
47        }
48    }
49
50    /// Serialize this [`ProxyCredential`] credential as a header string.
51    pub fn as_header_string(&self) -> String {
52        match self {
53            Self::Basic(basic) => basic.as_header_string(),
54            Self::Bearer(bearer) => bearer.as_header_string(),
55        }
56    }
57
58    #[cfg(feature = "http")]
59    /// View this [`ProxyCredential`] as a [`HeaderValue`]
60    pub fn as_header_value(&self) -> HeaderValue {
61        match self {
62            Self::Basic(basic) => basic.as_header_value(),
63            Self::Bearer(bearer) => bearer.as_header_value(),
64        }
65    }
66
67    /// Serialize this [`ProxyCredential`] credential as a clear (not encoded) string.
68    pub fn as_clear_string(&self) -> String {
69        match self {
70            Self::Basic(basic) => basic.as_clear_string(),
71            Self::Bearer(bearer) => bearer.as_clear_string(),
72        }
73    }
74}
75
76#[cfg(test)]
77mod tests {
78    use super::*;
79
80    #[test]
81    fn proxy_credentials_clear_invalid() {
82        assert!(
83            ProxyCredential::try_from_clear_str("").is_err(),
84            "parse: empty",
85        );
86    }
87
88    enum Is {
89        Basic(&'static str),
90        Bearer(&'static str),
91    }
92
93    fn assert_is(proxy: ProxyCredential, expected: Is) {
94        match expected {
95            Is::Basic(value) => match proxy {
96                ProxyCredential::Bearer(other) => panic!(
97                    "expected proxy bearer {} to be the basic credential: {}",
98                    other.as_clear_string(),
99                    value
100                ),
101                ProxyCredential::Basic(other) => assert_eq!(other.as_clear_string(), value),
102            },
103            Is::Bearer(value) => match proxy {
104                ProxyCredential::Basic(other) => panic!(
105                    "expected proxy basic {} to be the bearer: {}",
106                    other.as_clear_string(),
107                    value
108                ),
109                ProxyCredential::Bearer(other) => assert_eq!(other.as_clear_string(), value),
110            },
111        }
112    }
113
114    #[test]
115    fn proxy_credentials_header_valid() {
116        for (s, expected) in [
117            (
118                "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==",
119                Is::Basic("Aladdin:open sesame"),
120            ),
121            ("Basic QWxhZGRpbjo=", Is::Basic("Aladdin:")),
122            ("Bearer QWxhZGRpbjo=", Is::Bearer("QWxhZGRpbjo=")),
123            ("Bearer foobar", Is::Bearer("foobar")),
124        ] {
125            let credential = ProxyCredential::try_from_header_str(s)
126                .unwrap_or_else(|_| panic!("invalid proxy credential header str: {s}"));
127            assert_eq!(s, credential.as_header_string());
128            assert_is(credential, expected);
129        }
130    }
131
132    #[test]
133    fn proxy_credentials_clear_valid() {
134        for (s, expected) in [
135            ("Aladdin:open sesame", Is::Basic("Aladdin:open sesame")),
136            ("Aladdin:", Is::Basic("Aladdin:")),
137            ("QWxhZGRpbjo=", Is::Bearer("QWxhZGRpbjo=")),
138            ("foobar", Is::Bearer("foobar")),
139        ] {
140            let credential = ProxyCredential::try_from_clear_str(s)
141                .unwrap_or_else(|_| panic!("invalid proxy credential clear str: {s}"));
142            assert_eq!(s, credential.as_clear_string());
143            assert_is(credential, expected);
144        }
145    }
146}