hyper_sync/header/common/
authorization.rs

1use base64::{encode, decode};
2use std::any::Any;
3use std::fmt::{self, Display};
4use std::str::{FromStr, from_utf8};
5use std::ops::{Deref, DerefMut};
6
7use header::{Header, Raw};
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/// use hyper_sync::header::{Headers, Authorization};
31///
32/// let mut headers = Headers::new();
33/// headers.set(Authorization("let me in".to_owned()));
34/// ```
35/// ```
36/// use hyper_sync::header::{Headers, Authorization, Basic};
37///
38/// let mut headers = Headers::new();
39/// headers.set(
40///    Authorization(
41///        Basic {
42///            username: "Aladdin".to_owned(),
43///            password: Some("open sesame".to_owned())
44///        }
45///    )
46/// );
47/// ```
48///
49/// ```
50/// use hyper_sync::header::{Headers, Authorization, Bearer};
51///
52/// let mut headers = Headers::new();
53/// headers.set(
54///    Authorization(
55///        Bearer {
56///            token: "QWxhZGRpbjpvcGVuIHNlc2FtZQ".to_owned()
57///        }
58///    )
59/// );
60/// ```
61#[derive(Clone, PartialEq, Debug)]
62pub struct Authorization<S: Scheme>(pub S);
63
64impl<S: Scheme> Deref for Authorization<S> {
65    type Target = S;
66
67    fn deref(&self) -> &S {
68        &self.0
69    }
70}
71
72impl<S: Scheme> DerefMut for Authorization<S> {
73    fn deref_mut(&mut self) -> &mut S {
74        &mut self.0
75    }
76}
77
78impl<S: Scheme + Any> Header for Authorization<S> where <S as FromStr>::Err: 'static {
79    fn header_name() -> &'static str {
80        static NAME: &'static str = "Authorization";
81        NAME
82    }
83
84    fn parse_header(raw: &Raw) -> ::Result<Authorization<S>> {
85        if let Some(line) = raw.one() {
86            let header = try!(from_utf8(line));
87            if let Some(scheme) = <S as Scheme>::scheme() {
88                if header.starts_with(scheme) && header.len() > scheme.len() + 1 {
89                    match header[scheme.len() + 1..].parse::<S>().map(Authorization) {
90                        Ok(h) => Ok(h),
91                        Err(_) => Err(::Error::Header)
92                    }
93                } else {
94                    Err(::Error::Header)
95                }
96            } else {
97                match header.parse::<S>().map(Authorization) {
98                    Ok(h) => Ok(h),
99                    Err(_) => Err(::Error::Header)
100                }
101            }
102        } else {
103            Err(::Error::Header)
104        }
105    }
106
107    fn fmt_header(&self, f: &mut ::header::Formatter) -> fmt::Result {
108        f.fmt_line(self)
109    }
110}
111
112impl<S: Scheme> fmt::Display for Authorization<S> {
113    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
114        if let Some(scheme) = <S as Scheme>::scheme() {
115            try!(write!(f, "{} ", scheme))
116        };
117        self.0.fmt_scheme(f)
118    }
119}
120
121/// An Authorization scheme to be used in the header.
122pub trait Scheme: FromStr + fmt::Debug + Clone + Send + Sync {
123    /// An optional Scheme name.
124    ///
125    /// Will be replaced with an associated constant once available.
126    fn scheme() -> Option<&'static str>;
127    /// Format the Scheme data into a header value.
128    fn fmt_scheme(&self, &mut fmt::Formatter) -> fmt::Result;
129}
130
131impl Scheme for String {
132    fn scheme() -> Option<&'static str> {
133        None
134    }
135
136    fn fmt_scheme(&self, f: &mut fmt::Formatter) -> fmt::Result {
137        Display::fmt(self, f)
138    }
139}
140
141/// Credential holder for Basic Authentication
142#[derive(Clone, PartialEq, Debug)]
143pub struct Basic {
144    /// The username as a possibly empty string
145    pub username: String,
146    /// The password. `None` if the `:` delimiter character was not
147    /// part of the parsed input. Note: A compliant client MUST
148    /// always send a password (which may be the empty string).
149    pub password: Option<String>
150}
151
152impl Scheme for Basic {
153    fn scheme() -> Option<&'static str> {
154        Some("Basic")
155    }
156
157    fn fmt_scheme(&self, f: &mut fmt::Formatter) -> fmt::Result {
158        //FIXME: serialize::base64 could use some Debug implementation, so
159        //that we don't have to allocate a new string here just to write it
160        //to the formatter.
161        let mut text = self.username.clone();
162        text.push(':');
163        if let Some(ref pass) = self.password {
164            text.push_str(&pass[..]);
165        }
166
167        f.write_str(&encode(&text))
168    }
169}
170
171/// creates a Basic from a base-64 encoded, `:`-delimited utf-8 string
172impl FromStr for Basic {
173    type Err = ::Error;
174    fn from_str(s: &str) -> ::Result<Basic> {
175        match decode(s) {
176            Ok(decoded) => match String::from_utf8(decoded) {
177                Ok(text) => {
178                    let parts = &mut text.split(':');
179                    let user = match parts.next() {
180                        Some(part) => part.to_owned(),
181                        None => return Err(::Error::Header)
182                    };
183                    let password = match parts.next() {
184                        Some(part) => Some(part.to_owned()),
185                        None => None
186                    };
187                    Ok(Basic {
188                        username: user,
189                        password: password
190                    })
191                },
192                Err(_) => {
193                    debug!("Basic::from_str utf8 error");
194                    Err(::Error::Header)
195                }
196            },
197            Err(_) => {
198                debug!("Basic::from_str base64 error");
199                Err(::Error::Header)
200            }
201        }
202    }
203}
204
205#[derive(Clone, PartialEq, Debug)]
206///Token holder for Bearer Authentication, most often seen with oauth
207pub struct Bearer {
208    ///Actual bearer token as a string
209    pub token: String
210}
211
212impl Scheme for Bearer {
213    fn scheme() -> Option<&'static str> {
214        Some("Bearer")
215    }
216
217    fn fmt_scheme(&self, f: &mut fmt::Formatter) -> fmt::Result {
218        write!(f, "{}", self.token)
219    }
220}
221
222impl FromStr for Bearer {
223    type Err = ::Error;
224    fn from_str(s: &str) -> ::Result<Bearer> {
225        Ok(Bearer { token: s.to_owned()})
226    }
227}
228
229#[cfg(test)]
230mod tests {
231    use super::{Authorization, Basic, Bearer};
232    use super::super::super::{Headers, Header};
233
234    #[test]
235    fn test_raw_auth() {
236        let mut headers = Headers::new();
237        headers.set(Authorization("foo bar baz".to_owned()));
238        assert_eq!(headers.to_string(), "Authorization: foo bar baz\r\n".to_owned());
239    }
240
241    #[test]
242    fn test_raw_auth_parse() {
243        let header: Authorization<String> = Header::parse_header(&b"foo bar baz".as_ref().into()).unwrap();
244        assert_eq!(header.0, "foo bar baz");
245    }
246
247    #[test]
248    fn test_basic_auth() {
249        let mut headers = Headers::new();
250        headers.set(Authorization(
251            Basic { username: "Aladdin".to_owned(), password: Some("open sesame".to_owned()) }));
252        assert_eq!(
253            headers.to_string(),
254            "Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==\r\n".to_owned());
255    }
256
257    #[test]
258    fn test_basic_auth_no_password() {
259        let mut headers = Headers::new();
260        headers.set(Authorization(Basic { username: "Aladdin".to_owned(), password: None }));
261        assert_eq!(headers.to_string(), "Authorization: Basic QWxhZGRpbjo=\r\n".to_owned());
262    }
263
264    #[test]
265    fn test_basic_auth_parse() {
266        let auth: Authorization<Basic> = Header::parse_header(
267            &b"Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==".as_ref().into()).unwrap();
268        assert_eq!(auth.0.username, "Aladdin");
269        assert_eq!(auth.0.password, Some("open sesame".to_owned()));
270    }
271
272    #[test]
273    fn test_basic_auth_parse_no_password() {
274        let auth: Authorization<Basic> = Header::parse_header(
275            &b"Basic QWxhZGRpbjo=".as_ref().into()).unwrap();
276        assert_eq!(auth.0.username, "Aladdin");
277        assert_eq!(auth.0.password, Some("".to_owned()));
278    }
279
280    #[test]
281    fn test_bearer_auth() {
282        let mut headers = Headers::new();
283        headers.set(Authorization(
284            Bearer { token: "fpKL54jvWmEGVoRdCNjG".to_owned() }));
285        assert_eq!(
286            headers.to_string(),
287            "Authorization: Bearer fpKL54jvWmEGVoRdCNjG\r\n".to_owned());
288    }
289
290    #[test]
291    fn test_bearer_auth_parse() {
292        let auth: Authorization<Bearer> = Header::parse_header(
293            &b"Bearer fpKL54jvWmEGVoRdCNjG".as_ref().into()).unwrap();
294        assert_eq!(auth.0.token, "fpKL54jvWmEGVoRdCNjG");
295    }
296}
297
298bench_header!(raw, Authorization<String>, { vec![b"foo bar baz".to_vec()] });
299bench_header!(basic, Authorization<Basic>, { vec![b"Basic QWxhZGRpbjpuIHNlc2FtZQ==".to_vec()] });
300bench_header!(bearer, Authorization<Bearer>, { vec![b"Bearer fpKL54jvWmEGVoRdCNjG".to_vec()] });