headers_ext/common/
authorization.rs

1//! Authorization header and types.
2
3use base64;
4use bytes::Bytes;
5
6use {HeaderValue};
7use util::HeaderValueString;
8
9/// `Authorization` header, defined in [RFC7235](https://tools.ietf.org/html/rfc7235#section-4.2)
10///
11/// The `Authorization` header field allows a user agent to authenticate
12/// itself with an origin server -- usually, but not necessarily, after
13/// receiving a 401 (Unauthorized) response.  Its value consists of
14/// credentials containing the authentication information of the user
15/// agent for the realm of the resource being requested.
16///
17/// # ABNF
18///
19/// ```text
20/// Authorization = credentials
21/// ```
22///
23/// # Example values
24/// * `Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==`
25/// * `Bearer fpKL54jvWmEGVoRdCNjG`
26///
27/// # Examples
28///
29/// ```
30/// # extern crate headers_ext as headers;
31/// use headers::Authorization;
32///
33/// let basic = Authorization::basic("Aladdin", "open sesame");
34/// let bearer = Authorization::bearer("some-opaque-token").unwrap();
35/// ```
36///
37#[derive(Clone, PartialEq, Debug)]
38pub struct Authorization<C: Credentials>(pub C);
39
40impl Authorization<Basic> {
41    /// Create a `Basic` authorization header.
42    pub fn basic(username: &str, password: &str) -> Self {
43        let colon_pos = username.len();
44        let decoded = format!("{}:{}", username, password);
45
46        Authorization(Basic {
47            decoded,
48            colon_pos,
49        })
50    }
51}
52
53impl Authorization<Bearer> {
54    /// Try to create a `Bearer` authorization header.
55    pub fn bearer(token: &str) -> Result<Self, InvalidBearerToken>  {
56        HeaderValueString::from_string(format!("Bearer {}", token))
57            .map(|val| Authorization(Bearer(val)))
58            .ok_or_else(|| InvalidBearerToken { _inner: () })
59    }
60}
61
62impl<C: Credentials> ::Header for Authorization<C> {
63    const NAME: &'static ::HeaderName = &::http::header::AUTHORIZATION;
64
65    fn decode<'i, I: Iterator<Item = &'i HeaderValue>>(values: &mut I) -> Result<Self, ::Error> {
66        values
67            .next()
68            .and_then(|val| {
69                let slice = val.as_bytes();
70                if slice.starts_with(C::SCHEME.as_bytes())
71                    && slice.len() > C::SCHEME.len()
72                    && slice[C::SCHEME.len()] == b' ' {
73                    C::decode(val)
74                        .map(Authorization)
75                } else {
76                    None
77                }
78            })
79            .ok_or_else(::Error::invalid)
80    }
81
82    fn encode<E: Extend<::HeaderValue>>(&self, values: &mut E) {
83        let value = self.0.encode();
84        debug_assert!(
85            value.as_bytes().starts_with(C::SCHEME.as_bytes()),
86            "Credentials::encode should include its scheme: scheme = {:?}, encoded = {:?}",
87            C::SCHEME,
88            value,
89        );
90
91        values.extend(::std::iter::once(value));
92    }
93}
94
95/// Credentials to be used in the `Authorization` header.
96pub trait Credentials: Sized {
97    /// The scheme identify the format of these credentials.
98    ///
99    /// This is the static string that always prefixes the actual credentials,
100    /// like `"Basic"` in basic authorization.
101    const SCHEME: &'static str;
102
103    /// Try to decode the credentials from the `HeaderValue`.
104    ///
105    /// The `SCHEME` will be the first part of the `value`.
106    fn decode(value: &HeaderValue) -> Option<Self>;
107
108    /// Encode the credentials to a `HeaderValue`.
109    ///
110    /// The `SCHEME` must be the first part of the `value`.
111    fn encode(&self) -> HeaderValue;
112}
113
114/// Credential holder for Basic Authentication
115#[derive(Clone, PartialEq, Debug)]
116pub struct Basic {
117    decoded: String,
118    colon_pos: usize,
119}
120
121impl Basic {
122    /// View the decoded username.
123    pub fn username(&self) -> &str {
124        &self.decoded[..self.colon_pos]
125    }
126
127    /// View the decoded password.
128    pub fn password(&self) -> &str {
129        &self.decoded[self.colon_pos + 1..]
130    }
131}
132
133impl Credentials for Basic {
134    const SCHEME: &'static str = "Basic";
135
136    fn decode(value: &HeaderValue) -> Option<Self> {
137        debug_assert!(
138            value.as_bytes().starts_with(b"Basic "),
139            "HeaderValue to decode should start with \"Basic ..\", received = {:?}",
140            value,
141        );
142
143        let bytes = base64::decode(&value.as_bytes()["Basic ".len()..]).ok()?;
144        let decoded = String::from_utf8(bytes).ok()?;
145
146        let colon_pos = decoded.find(':')?;
147
148        Some(Basic {
149            decoded,
150            colon_pos,
151        })
152    }
153
154    fn encode(&self) -> HeaderValue {
155        let mut encoded = String::from("Basic ");
156        base64::encode_config_buf(&self.decoded, base64::STANDARD, &mut encoded);
157
158        let bytes = Bytes::from(encoded);
159        HeaderValue::from_shared(bytes)
160            .expect("base64 encoding is always a valid HeaderValue")
161    }
162}
163
164#[derive(Clone, PartialEq, Debug)]
165/// Token holder for Bearer Authentication, most often seen with oauth
166pub struct Bearer(HeaderValueString);
167
168impl Bearer {
169    /// View the token part as a `&str`.
170    pub fn token(&self) -> &str {
171        &self.0.as_str()["Bearer ".len() ..]
172    }
173}
174
175impl Credentials for Bearer {
176    const SCHEME: &'static str = "Bearer";
177
178    fn decode(value: &HeaderValue) -> Option<Self> {
179        debug_assert!(
180            value.as_bytes().starts_with(b"Bearer "),
181            "HeaderValue to decode should start with \"Bearer ..\", received = {:?}",
182            value,
183        );
184
185        HeaderValueString::from_val(value)
186            .ok()
187            .map(Bearer)
188    }
189
190    fn encode(&self) -> HeaderValue {
191        (&self.0).into()
192    }
193}
194
195error_type!(InvalidBearerToken);
196
197
198#[cfg(test)]
199mod tests {
200    use headers_core::HeaderMapExt;
201    use http::header::HeaderMap;
202    use super::{Authorization, Basic, Bearer};
203    use super::super::{test_decode, test_encode};
204
205    #[test]
206    fn basic_encode() {
207        let auth = Authorization::basic("Aladdin", "open sesame");
208        let headers = test_encode(auth);
209
210        assert_eq!(
211            headers["authorization"],
212            "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==",
213        );
214    }
215
216    #[test]
217    fn basic_roundtrip() {
218        let auth = Authorization::basic("Aladdin", "open sesame");
219        let mut h = HeaderMap::new();
220        h.typed_insert(auth.clone());
221        assert_eq!(h.typed_get(), Some(auth));
222    }
223
224    #[test]
225    fn basic_encode_no_password() {
226        let auth = Authorization::basic("Aladdin", "");
227        let headers = test_encode(auth);
228
229        assert_eq!(
230            headers["authorization"],
231            "Basic QWxhZGRpbjo=",
232        );
233    }
234
235    #[test]
236    fn basic_decode() {
237        let auth: Authorization<Basic> = test_decode(&["Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=="]).unwrap();
238        assert_eq!(auth.0.username(), "Aladdin");
239        assert_eq!(auth.0.password(), "open sesame");
240    }
241
242    #[test]
243    fn basic_decode_no_password() {
244        let auth: Authorization<Basic> = test_decode(&["Basic QWxhZGRpbjo="]).unwrap();
245        assert_eq!(auth.0.username(), "Aladdin");
246        assert_eq!(auth.0.password(), "");
247    }
248
249    #[test]
250    fn bearer_encode() {
251        let auth = Authorization::bearer("fpKL54jvWmEGVoRdCNjG").unwrap();
252
253        let headers = test_encode(auth);
254
255        assert_eq!(
256            headers["authorization"],
257            "Bearer fpKL54jvWmEGVoRdCNjG",
258        );
259    }
260
261    #[test]
262    fn bearer_decode() {
263        let auth: Authorization<Bearer> = test_decode(&["Bearer fpKL54jvWmEGVoRdCNjG"]).unwrap();
264        assert_eq!(auth.0.token().as_bytes(), b"fpKL54jvWmEGVoRdCNjG");
265    }
266}
267
268//bench_header!(raw, Authorization<String>, { vec![b"foo bar baz".to_vec()] });
269//bench_header!(basic, Authorization<Basic>, { vec![b"Basic QWxhZGRpbjpuIHNlc2FtZQ==".to_vec()] });
270//bench_header!(bearer, Authorization<Bearer>, { vec![b"Bearer fpKL54jvWmEGVoRdCNjG".to_vec()] });