cogo_http/header/common/
authorization.rs

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