//! Authorization header and types. use base64; use bytes::Bytes; use util::HeaderValueString; use HeaderValue; /// `Authorization` header, defined in [RFC7235](https://tools.ietf.org/html/rfc7235#section-4.2) /// /// The `Authorization` header field allows a user agent to authenticate /// itself with an origin server -- usually, but not necessarily, after /// receiving a 401 (Unauthorized) response. Its value consists of /// credentials containing the authentication information of the user /// agent for the realm of the resource being requested. /// /// # ABNF /// /// ```text /// Authorization = credentials /// ``` /// /// # Example values /// * `Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==` /// * `Bearer fpKL54jvWmEGVoRdCNjG` /// /// # Examples /// /// ``` /// # extern crate headers; /// use headers::Authorization; /// /// let basic = Authorization::basic("Aladdin", "open sesame"); /// let bearer = Authorization::bearer("some-opaque-token").unwrap(); /// ``` /// #[derive(Clone, PartialEq, Debug)] pub struct Authorization(pub C); impl Authorization { /// Create a `Basic` authorization header. pub fn basic(username: &str, password: &str) -> Self { let colon_pos = username.len(); let decoded = format!("{}:{}", username, password); Authorization(Basic { decoded, colon_pos }) } /// View the decoded username. pub fn username(&self) -> &str { self.0.username() } /// View the decoded password. pub fn password(&self) -> &str { self.0.password() } } impl Authorization { /// Try to create a `Bearer` authorization header. pub fn bearer(token: &str) -> Result { HeaderValueString::from_string(format!("Bearer {}", token)) .map(|val| Authorization(Bearer(val))) .ok_or_else(|| InvalidBearerToken { _inner: () }) } /// View the token part as a `&str`. pub fn token(&self) -> &str { self.0.token() } } impl ::Header for Authorization { fn name() -> &'static ::HeaderName { &::http::header::AUTHORIZATION } fn decode<'i, I: Iterator>(values: &mut I) -> Result { values .next() .and_then(|val| { let slice = val.as_bytes(); if slice.starts_with(C::SCHEME.as_bytes()) && slice.len() > C::SCHEME.len() && slice[C::SCHEME.len()] == b' ' { C::decode(val).map(Authorization) } else { None } }) .ok_or_else(::Error::invalid) } fn encode>(&self, values: &mut E) { let value = self.0.encode(); debug_assert!( value.as_bytes().starts_with(C::SCHEME.as_bytes()), "Credentials::encode should include its scheme: scheme = {:?}, encoded = {:?}", C::SCHEME, value, ); values.extend(::std::iter::once(value)); } } /// Credentials to be used in the `Authorization` header. pub trait Credentials: Sized { /// The scheme identify the format of these credentials. /// /// This is the static string that always prefixes the actual credentials, /// like `"Basic"` in basic authorization. const SCHEME: &'static str; /// Try to decode the credentials from the `HeaderValue`. /// /// The `SCHEME` will be the first part of the `value`. fn decode(value: &HeaderValue) -> Option; /// Encode the credentials to a `HeaderValue`. /// /// The `SCHEME` must be the first part of the `value`. fn encode(&self) -> HeaderValue; } /// Credential holder for Basic Authentication #[derive(Clone, PartialEq, Debug)] pub struct Basic { decoded: String, colon_pos: usize, } impl Basic { /// View the decoded username. pub fn username(&self) -> &str { &self.decoded[..self.colon_pos] } /// View the decoded password. pub fn password(&self) -> &str { &self.decoded[self.colon_pos + 1..] } } impl Credentials for Basic { const SCHEME: &'static str = "Basic"; fn decode(value: &HeaderValue) -> Option { debug_assert!( value.as_bytes().starts_with(b"Basic "), "HeaderValue to decode should start with \"Basic ..\", received = {:?}", value, ); let bytes = &value.as_bytes()["Basic ".len()..]; let non_space_pos = bytes.iter().position(|b| *b != b' ')?; let bytes = &bytes[non_space_pos..]; let bytes = base64::decode(bytes).ok()?; let decoded = String::from_utf8(bytes).ok()?; let colon_pos = decoded.find(':')?; Some(Basic { decoded, colon_pos }) } fn encode(&self) -> HeaderValue { let mut encoded = String::from("Basic "); base64::encode_config_buf(&self.decoded, base64::STANDARD, &mut encoded); let bytes = Bytes::from(encoded); HeaderValue::from_maybe_shared(bytes).expect("base64 encoding is always a valid HeaderValue") } } #[derive(Clone, PartialEq, Debug)] /// Token holder for Bearer Authentication, most often seen with oauth pub struct Bearer(HeaderValueString); impl Bearer { /// View the token part as a `&str`. pub fn token(&self) -> &str { &self.0.as_str()["Bearer ".len()..] } } impl Credentials for Bearer { const SCHEME: &'static str = "Bearer"; fn decode(value: &HeaderValue) -> Option { debug_assert!( value.as_bytes().starts_with(b"Bearer "), "HeaderValue to decode should start with \"Bearer ..\", received = {:?}", value, ); HeaderValueString::from_val(value).ok().map(Bearer) } fn encode(&self) -> HeaderValue { (&self.0).into() } } error_type!(InvalidBearerToken); #[cfg(test)] mod tests { use super::super::{test_decode, test_encode}; use super::{Authorization, Basic, Bearer}; use http::header::HeaderMap; use HeaderMapExt; #[test] fn basic_encode() { let auth = Authorization::basic("Aladdin", "open sesame"); let headers = test_encode(auth); assert_eq!( headers["authorization"], "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==", ); } #[test] fn basic_roundtrip() { let auth = Authorization::basic("Aladdin", "open sesame"); let mut h = HeaderMap::new(); h.typed_insert(auth.clone()); assert_eq!(h.typed_get(), Some(auth)); } #[test] fn basic_encode_no_password() { let auth = Authorization::basic("Aladdin", ""); let headers = test_encode(auth); assert_eq!(headers["authorization"], "Basic QWxhZGRpbjo=",); } #[test] fn basic_decode() { let auth: Authorization = test_decode(&["Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=="]).unwrap(); assert_eq!(auth.0.username(), "Aladdin"); assert_eq!(auth.0.password(), "open sesame"); } #[test] fn basic_decode_no_password() { let auth: Authorization = test_decode(&["Basic QWxhZGRpbjo="]).unwrap(); assert_eq!(auth.0.username(), "Aladdin"); assert_eq!(auth.0.password(), ""); } #[test] fn bearer_encode() { let auth = Authorization::bearer("fpKL54jvWmEGVoRdCNjG").unwrap(); let headers = test_encode(auth); assert_eq!(headers["authorization"], "Bearer fpKL54jvWmEGVoRdCNjG",); } #[test] fn bearer_decode() { let auth: Authorization = test_decode(&["Bearer fpKL54jvWmEGVoRdCNjG"]).unwrap(); assert_eq!(auth.0.token().as_bytes(), b"fpKL54jvWmEGVoRdCNjG"); } } //bench_header!(raw, Authorization, { vec![b"foo bar baz".to_vec()] }); //bench_header!(basic, Authorization, { vec![b"Basic QWxhZGRpbjpuIHNlc2FtZQ==".to_vec()] }); //bench_header!(bearer, Authorization, { vec![b"Bearer fpKL54jvWmEGVoRdCNjG".to_vec()] });