use std::marker::PhantomData; use hyper::header; use rustc_serialize::json::Json; use serde::{Serialize, Serializer, Deserialize, Deserializer}; use serde::{ser, de}; use super::{Token, Lifetime}; use client::response::{FromResponse, ParseError, JsonHelper}; /// The bearer token type. /// /// See [RFC 6750](http://tools.ietf.org/html/rfc6750). #[derive(Debug, Clone, PartialEq, Eq, RustcEncodable, RustcDecodable)] pub struct Bearer { access_token: String, scope: Option, lifetime: L, } impl Token for Bearer { fn access_token(&self) -> &str { &self.access_token } fn scope(&self) -> Option<&str> { self.scope.as_ref().map(|s| &s[..]) } fn lifetime(&self) -> &L { &self.lifetime } } impl<'a, L: Lifetime> Into> for &'a Bearer { fn into(self) -> header::Authorization { header::Authorization(header::Bearer { token: self.access_token.clone() }) } } impl Bearer { fn from_response_and_lifetime(json: &Json, lifetime: L) -> Result { let obj = try!(JsonHelper(json).as_object()); let token_type = try!(obj.get_string("token_type")); if token_type != "Bearer" && token_type != "bearer" { return Err(ParseError::ExpectedFieldValue("token_type", "Bearer")); } let access_token = try!(obj.get_string("access_token")); let scope = obj.get_string_option("scope"); Ok(Bearer { access_token: access_token.into(), scope: scope.map(Into::into), lifetime: lifetime, }) } } impl FromResponse for Bearer { fn from_response(json: &Json) -> Result { let lifetime = try!(FromResponse::from_response(json)); Bearer::from_response_and_lifetime(json, lifetime) } fn from_response_inherit(json: &Json, prev: &Self) -> Result { let lifetime = try!(FromResponse::from_response_inherit(json, &prev.lifetime)); Bearer::from_response_and_lifetime(json, lifetime) } } impl Serialize for Bearer { fn serialize(&self, serializer: &mut S) -> Result<(), S::Error> { serializer.serialize_struct("Bearer", SerVisitor(self, 0)) } } struct SerVisitor<'a, L: Lifetime + Serialize + 'a>(&'a Bearer, u8); impl<'a, L: Lifetime + Serialize + 'a> ser::MapVisitor for SerVisitor<'a, L> { fn visit(&mut self, serializer: &mut S) -> Result, S::Error> { self.1 += 1; match self.1 { 1 => serializer.serialize_struct_elt("access_token", &self.0.access_token).map(Some), 2 => serializer.serialize_struct_elt("scope", &self.0.scope).map(Some), 3 => serializer.serialize_struct_elt("lifetime", &self.0.lifetime).map(Some), _ => Ok(None), } } fn len(&self) -> Option { Some(3) } } impl Deserialize for Bearer { fn deserialize(deserializer: &mut D) -> Result { static FIELDS: &'static [&'static str] = &["access_token", "scope", "lifetime"]; deserializer.deserialize_struct("Bearer", FIELDS, DeVisitor(PhantomData)) } } struct DeVisitor(PhantomData); impl de::Visitor for DeVisitor { type Value = Bearer; fn visit_map(&mut self, mut visitor: V) -> Result, V::Error> { let mut access_token = None; let mut scope = None; let mut lifetime = None; loop { match try!(visitor.visit_key()) { Some(Field::AccessToken) => access_token = Some(try!(visitor.visit_value())), Some(Field::Scope) => scope = Some(try!(visitor.visit_value())), Some(Field::Lifetime) => lifetime = Some(try!(visitor.visit_value())), None => break, } } let access_token = match access_token { Some(s) => s, None => return visitor.missing_field("access_token"), }; let lifetime = match lifetime { Some(l) => l, None => return visitor.missing_field("lifetime"), }; try!(visitor.end()); Ok(Bearer { access_token: access_token, scope: scope, lifetime: lifetime, }) } } enum Field { AccessToken, Scope, Lifetime, } impl Deserialize for Field { fn deserialize(deserializer: &mut D) -> Result { deserializer.deserialize(FieldVisitor) } } struct FieldVisitor; impl de::Visitor for FieldVisitor { type Value = Field; fn visit_str(&mut self, value: &str) -> Result { match value { "access_token" => Ok(Field::AccessToken), "scope" => Ok(Field::Scope), "lifetime" => Ok(Field::Lifetime), _ => Err(de::Error::custom("expected access_token, scope or lifetime")), } } } #[cfg(test)] mod tests { use chrono::{UTC, Duration}; use rustc_serialize::json::Json; use serde_json; use client::response::{FromResponse, ParseError}; use token::{Static, Expiring}; use super::Bearer; #[test] fn from_response_with_invalid_token_type() { let json = Json::from_str(r#"{"token_type":"MAC","access_token":"aaaaaaaa"}"#).unwrap(); assert_eq!( ParseError::ExpectedFieldValue("token_type", "Bearer"), Bearer::::from_response(&json).unwrap_err() ); } #[test] fn from_response_capital_b() { let json = Json::from_str(r#"{"token_type":"Bearer","access_token":"aaaaaaaa"}"#).unwrap(); assert_eq!( Bearer { access_token: String::from("aaaaaaaa"), scope: None, lifetime: Static, }, Bearer::::from_response(&json).unwrap() ); } #[test] fn from_response_little_b() { let json = Json::from_str(r#"{"token_type":"bearer","access_token":"aaaaaaaa"}"#).unwrap(); assert_eq!( Bearer { access_token: String::from("aaaaaaaa"), scope: None, lifetime: Static, }, Bearer::::from_response(&json).unwrap() ); } #[test] fn from_response_with_scope() { let json = Json::from_str( r#"{"token_type":"Bearer","access_token":"aaaaaaaa","scope":"foo"}"# ).unwrap(); assert_eq!( Bearer { access_token: String::from("aaaaaaaa"), scope: Some(String::from("foo")), lifetime: Static, }, Bearer::::from_response(&json).unwrap() ); } #[test] fn from_response_expiring() { let json = Json::from_str(r#" { "token_type":"Bearer", "access_token":"aaaaaaaa", "expires_in":3600, "refresh_token":"bbbbbbbb" } "#).unwrap(); let bearer = Bearer::::from_response(&json).unwrap(); assert_eq!("aaaaaaaa", bearer.access_token); assert_eq!(None, bearer.scope); let expiring = bearer.lifetime; assert_eq!("bbbbbbbb", expiring.refresh_token()); assert!(expiring.expires() > &UTC::now()); assert!(expiring.expires() <= &(UTC::now() + Duration::seconds(3600))); } #[test] fn from_response_inherit_expiring() { let json = Json::from_str(r#" { "token_type":"Bearer", "access_token":"aaaaaaaa", "expires_in":3600, "refresh_token":"bbbbbbbb" } "#).unwrap(); let prev = Bearer::::from_response(&json).unwrap(); let json = Json::from_str(r#" { "token_type":"Bearer", "access_token":"cccccccc", "expires_in":3600 } "#).unwrap(); let bearer = Bearer::::from_response_inherit(&json, &prev).unwrap(); assert_eq!("cccccccc", bearer.access_token); assert_eq!(None, bearer.scope); let expiring = bearer.lifetime; assert_eq!("bbbbbbbb", expiring.refresh_token()); assert!(expiring.expires() > &UTC::now()); assert!(expiring.expires() <= &(UTC::now() + Duration::seconds(3600))); } #[test] fn serialize_deserialize() { let original = Bearer { access_token: String::from("foo"), scope: Some(String::from("bar")), lifetime: Static, }; let serialized = serde_json::to_value(&original); let deserialized = serde_json::from_value(serialized).unwrap(); assert_eq!(original, deserialized); } }