Files
hyper/src/header/common/authorization.rs
2017-08-15 16:04:56 -07:00

296 lines
8.9 KiB
Rust

use std::any::Any;
use std::fmt::{self, Display};
use std::str::{FromStr, from_utf8};
use std::ops::{Deref, DerefMut};
use base64::{encode, decode};
use header::{Header, Raw};
/// `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
/// ```plain
/// Authorization = credentials
/// ```
///
/// # Example values
/// * `Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==`
/// * `Bearer fpKL54jvWmEGVoRdCNjG`
///
/// # Examples
/// ```
/// use hyper::header::{Headers, Authorization};
///
/// let mut headers = Headers::new();
/// headers.set(Authorization("let me in".to_owned()));
/// ```
/// ```
/// use hyper::header::{Headers, Authorization, Basic};
///
/// let mut headers = Headers::new();
/// headers.set(
/// Authorization(
/// Basic {
/// username: "Aladdin".to_owned(),
/// password: Some("open sesame".to_owned())
/// }
/// )
/// );
/// ```
/// ```
/// use hyper::header::{Headers, Authorization, Bearer};
///
/// let mut headers = Headers::new();
/// headers.set(
/// Authorization(
/// Bearer {
/// token: "QWxhZGRpbjpvcGVuIHNlc2FtZQ".to_owned()
/// }
/// )
/// );
/// ```
#[derive(Clone, PartialEq, Debug)]
pub struct Authorization<S: Scheme>(pub S);
impl<S: Scheme> Deref for Authorization<S> {
type Target = S;
fn deref(&self) -> &S {
&self.0
}
}
impl<S: Scheme> DerefMut for Authorization<S> {
fn deref_mut(&mut self) -> &mut S {
&mut self.0
}
}
impl<S: Scheme + Any> Header for Authorization<S> where <S as FromStr>::Err: 'static {
fn header_name() -> &'static str {
static NAME: &'static str = "Authorization";
NAME
}
fn parse_header(raw: &Raw) -> ::Result<Authorization<S>> {
if let Some(line) = raw.one() {
let header = try!(from_utf8(line));
if let Some(scheme) = <S as Scheme>::scheme() {
if header.starts_with(scheme) && header.len() > scheme.len() + 1 {
match header[scheme.len() + 1..].parse::<S>().map(Authorization) {
Ok(h) => Ok(h),
Err(_) => Err(::Error::Header)
}
} else {
Err(::Error::Header)
}
} else {
match header.parse::<S>().map(Authorization) {
Ok(h) => Ok(h),
Err(_) => Err(::Error::Header)
}
}
} else {
Err(::Error::Header)
}
}
fn fmt_header(&self, f: &mut ::header::Formatter) -> fmt::Result {
f.fmt_line(self)
}
}
impl<S: Scheme> fmt::Display for Authorization<S> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if let Some(scheme) = <S as Scheme>::scheme() {
try!(write!(f, "{} ", scheme))
};
self.0.fmt_scheme(f)
}
}
/// An Authorization scheme to be used in the header.
pub trait Scheme: FromStr + fmt::Debug + Clone + Send + Sync {
/// An optional Scheme name.
///
/// Will be replaced with an associated constant once available.
fn scheme() -> Option<&'static str>;
/// Format the Scheme data into a header value.
fn fmt_scheme(&self, &mut fmt::Formatter) -> fmt::Result;
}
impl Scheme for String {
fn scheme() -> Option<&'static str> {
None
}
fn fmt_scheme(&self, f: &mut fmt::Formatter) -> fmt::Result {
Display::fmt(self, f)
}
}
/// Credential holder for Basic Authentication
#[derive(Clone, PartialEq, Debug)]
pub struct Basic {
/// The username as a possibly empty string
pub username: String,
/// The password. `None` if the `:` delimiter character was not
/// part of the parsed input. Note: A compliant client MUST
/// always send a password (which may be the empty string).
pub password: Option<String>
}
impl Scheme for Basic {
fn scheme() -> Option<&'static str> {
Some("Basic")
}
fn fmt_scheme(&self, f: &mut fmt::Formatter) -> fmt::Result {
//FIXME: serialize::base64 could use some Debug implementation, so
//that we don't have to allocate a new string here just to write it
//to the formatter.
let mut text = self.username.clone();
text.push(':');
if let Some(ref pass) = self.password {
text.push_str(&pass[..]);
}
f.write_str(&encode(&text))
}
}
impl FromStr for Basic {
type Err = ::Error;
fn from_str(s: &str) -> ::Result<Basic> {
match decode(s) {
Ok(decoded) => match String::from_utf8(decoded) {
Ok(text) => {
let parts = &mut text.split(':');
let user = match parts.next() {
Some(part) => part.to_owned(),
None => return Err(::Error::Header)
};
let password = match parts.next() {
Some(part) => Some(part.to_owned()),
None => None
};
Ok(Basic {
username: user,
password: password
})
},
Err(_) => {
debug!("Basic::from_str utf8 error");
Err(::Error::Header)
}
},
Err(_) => {
debug!("Basic::from_str base64 error");
Err(::Error::Header)
}
}
}
}
#[derive(Clone, PartialEq, Debug)]
///Token holder for Bearer Authentication, most often seen with oauth
pub struct Bearer {
///Actual bearer token as a string
pub token: String
}
impl Scheme for Bearer {
fn scheme() -> Option<&'static str> {
Some("Bearer")
}
fn fmt_scheme(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.token)
}
}
impl FromStr for Bearer {
type Err = ::Error;
fn from_str(s: &str) -> ::Result<Bearer> {
Ok(Bearer { token: s.to_owned()})
}
}
#[cfg(test)]
mod tests {
use super::{Authorization, Basic, Bearer};
use super::super::super::{Headers, Header};
#[test]
fn test_raw_auth() {
let mut headers = Headers::new();
headers.set(Authorization("foo bar baz".to_owned()));
assert_eq!(headers.to_string(), "Authorization: foo bar baz\r\n".to_owned());
}
#[test]
fn test_raw_auth_parse() {
let header: Authorization<String> = Header::parse_header(&b"foo bar baz".as_ref().into()).unwrap();
assert_eq!(header.0, "foo bar baz");
}
#[test]
fn test_basic_auth() {
let mut headers = Headers::new();
headers.set(Authorization(
Basic { username: "Aladdin".to_owned(), password: Some("open sesame".to_owned()) }));
assert_eq!(
headers.to_string(),
"Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==\r\n".to_owned());
}
#[test]
fn test_basic_auth_no_password() {
let mut headers = Headers::new();
headers.set(Authorization(Basic { username: "Aladdin".to_owned(), password: None }));
assert_eq!(headers.to_string(), "Authorization: Basic QWxhZGRpbjo=\r\n".to_owned());
}
#[test]
fn test_basic_auth_parse() {
let auth: Authorization<Basic> = Header::parse_header(
&b"Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==".as_ref().into()).unwrap();
assert_eq!(auth.0.username, "Aladdin");
assert_eq!(auth.0.password, Some("open sesame".to_owned()));
}
#[test]
fn test_basic_auth_parse_no_password() {
let auth: Authorization<Basic> = Header::parse_header(
&b"Basic QWxhZGRpbjo=".as_ref().into()).unwrap();
assert_eq!(auth.0.username, "Aladdin");
assert_eq!(auth.0.password, Some("".to_owned()));
}
#[test]
fn test_bearer_auth() {
let mut headers = Headers::new();
headers.set(Authorization(
Bearer { token: "fpKL54jvWmEGVoRdCNjG".to_owned() }));
assert_eq!(
headers.to_string(),
"Authorization: Bearer fpKL54jvWmEGVoRdCNjG\r\n".to_owned());
}
#[test]
fn test_bearer_auth_parse() {
let auth: Authorization<Bearer> = Header::parse_header(
&b"Bearer fpKL54jvWmEGVoRdCNjG".as_ref().into()).unwrap();
assert_eq!(auth.0.token, "fpKL54jvWmEGVoRdCNjG");
}
}
bench_header!(raw, Authorization<String>, { vec![b"foo bar baz".to_vec()] });
bench_header!(basic, Authorization<Basic>, { vec![b"Basic QWxhZGRpbjpuIHNlc2FtZQ==".to_vec()] });
bench_header!(bearer, Authorization<Bearer>, { vec![b"Bearer fpKL54jvWmEGVoRdCNjG".to_vec()] });