http_types/auth/
authorization.rs

1use crate::auth::AuthenticationScheme;
2use crate::bail_status as bail;
3use crate::headers::{HeaderName, HeaderValue, Headers, AUTHORIZATION};
4
5/// Credentials to authenticate a user agent with a server.
6///
7/// # Specifications
8///
9/// - [RFC 7235, section 4.2: Authorization](https://tools.ietf.org/html/rfc7235#section-4.2)
10///
11/// # Examples
12///
13/// ```
14/// # fn main() -> http_types::Result<()> {
15/// #
16/// use http_types::Response;
17/// use http_types::auth::{AuthenticationScheme, Authorization};
18///
19/// let scheme = AuthenticationScheme::Basic;
20/// let credentials = "0xdeadbeef202020";
21/// let authz = Authorization::new(scheme, credentials.into());
22///
23/// let mut res = Response::new(200);
24/// authz.apply(&mut res);
25///
26/// let authz = Authorization::from_headers(res)?.unwrap();
27///
28/// assert_eq!(authz.scheme(), AuthenticationScheme::Basic);
29/// assert_eq!(authz.credentials(), credentials);
30/// #
31/// # Ok(()) }
32/// ```
33#[derive(Debug)]
34pub struct Authorization {
35    scheme: AuthenticationScheme,
36    credentials: String,
37}
38
39impl Authorization {
40    /// Create a new instance of `Authorization`.
41    pub fn new(scheme: AuthenticationScheme, credentials: String) -> Self {
42        Self {
43            scheme,
44            credentials,
45        }
46    }
47
48    /// Create a new instance from headers.
49    pub fn from_headers(headers: impl AsRef<Headers>) -> crate::Result<Option<Self>> {
50        let headers = match headers.as_ref().get(AUTHORIZATION) {
51            Some(headers) => headers,
52            None => return Ok(None),
53        };
54
55        // If we successfully parsed the header then there's always at least one
56        // entry. We want the last entry.
57        let value = headers.iter().last().unwrap();
58
59        let mut iter = value.as_str().splitn(2, ' ');
60        let scheme = iter.next();
61        let credential = iter.next();
62        let (scheme, credentials) = match (scheme, credential) {
63            (None, _) => bail!(400, "Could not find scheme"),
64            (Some(_), None) => bail!(400, "Could not find credentials"),
65            (Some(scheme), Some(credentials)) => (scheme.parse()?, credentials.to_owned()),
66        };
67
68        Ok(Some(Self {
69            scheme,
70            credentials,
71        }))
72    }
73
74    /// Sets the header.
75    pub fn apply(&self, mut headers: impl AsMut<Headers>) {
76        headers.as_mut().insert(self.name(), self.value());
77    }
78
79    /// Get the `HeaderName`.
80    pub fn name(&self) -> HeaderName {
81        AUTHORIZATION
82    }
83
84    /// Get the `HeaderValue`.
85    pub fn value(&self) -> HeaderValue {
86        let output = format!("{} {}", self.scheme, self.credentials);
87
88        // SAFETY: the internal string is validated to be ASCII.
89        unsafe { HeaderValue::from_bytes_unchecked(output.into()) }
90    }
91
92    /// Get the authorization scheme.
93    pub fn scheme(&self) -> AuthenticationScheme {
94        self.scheme
95    }
96
97    /// Set the authorization scheme.
98    pub fn set_scheme(&mut self, scheme: AuthenticationScheme) {
99        self.scheme = scheme;
100    }
101
102    /// Get the authorization credentials.
103    pub fn credentials(&self) -> &str {
104        self.credentials.as_str()
105    }
106
107    /// Set the authorization credentials.
108    pub fn set_credentials(&mut self, credentials: String) {
109        self.credentials = credentials;
110    }
111}
112
113#[cfg(test)]
114mod test {
115    use super::*;
116    use crate::headers::Headers;
117
118    #[test]
119    fn smoke() -> crate::Result<()> {
120        let scheme = AuthenticationScheme::Basic;
121        let credentials = "0xdeadbeef202020";
122        let authz = Authorization::new(scheme, credentials.into());
123
124        let mut headers = Headers::new();
125        authz.apply(&mut headers);
126
127        let authz = Authorization::from_headers(headers)?.unwrap();
128
129        assert_eq!(authz.scheme(), AuthenticationScheme::Basic);
130        assert_eq!(authz.credentials(), credentials);
131        Ok(())
132    }
133
134    #[test]
135    fn bad_request_on_parse_error() {
136        let mut headers = Headers::new();
137        headers.insert(AUTHORIZATION, "<nori ate the tag. yum.>");
138        let err = Authorization::from_headers(headers).unwrap_err();
139        assert_eq!(err.status(), 400);
140    }
141}