From 2628759a2e4334af3b2a6af9a1f68778bc41f25b Mon Sep 17 00:00:00 2001 From: Curtis McEnroe Date: Wed, 23 Dec 2015 23:46:21 -0500 Subject: [PATCH] Implement FromResponse for tokens --- src/client/response.rs | 10 ++++++++++ src/token/bearer.rs | 34 ++++++++++++++++++++++++++++++++++ src/token/expiring.rs | 33 ++++++++++++++++++++++++++++++++- src/token/mod.rs | 6 ++++-- src/token/statik.rs | 13 +++++++++++++ 5 files changed, 93 insertions(+), 3 deletions(-) diff --git a/src/client/response.rs b/src/client/response.rs index 43c5e23..47a54cc 100644 --- a/src/client/response.rs +++ b/src/client/response.rs @@ -82,4 +82,14 @@ impl<'a> JsonObjectHelper<'a> { pub fn get_string(&self, key: &'static str) -> Result<&'a str, ParseError> { self.get_string_option(key).ok_or(ParseError::ExpectedFieldType(key, "string")) } + + /// Gets a field as an i64 or returns `None`. + pub fn get_i64_option(&self, key: &'static str) -> Option { + self.0.get(key).and_then(Json::as_i64) + } + + /// Gets a field as an i64 or fails with `ParseError::ExpectedFieldType`. + pub fn get_i64(&self, key: &'static str) -> Result { + self.get_i64_option(key).ok_or(ParseError::ExpectedFieldType(key, "i64")) + } } diff --git a/src/token/bearer.rs b/src/token/bearer.rs index 3775b0f..2d8485c 100644 --- a/src/token/bearer.rs +++ b/src/token/bearer.rs @@ -1,6 +1,8 @@ use hyper::header; +use rustc_serialize::json::Json; use super::{Token, Lifetime}; +use client::response::{FromResponse, ParseError, JsonHelper}; /// The bearer token type. /// @@ -23,3 +25,35 @@ impl<'a, L: Lifetime> Into> for &'a Bearer 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) + } +} diff --git a/src/token/expiring.rs b/src/token/expiring.rs index fe88947..74422ed 100644 --- a/src/token/expiring.rs +++ b/src/token/expiring.rs @@ -1,6 +1,8 @@ -use chrono::{DateTime, UTC}; +use chrono::{DateTime, UTC, Duration}; +use rustc_serialize::json::Json; use super::Lifetime; +use client::response::{FromResponse, ParseError, JsonHelper}; /// An expiring token. #[derive(Debug)] @@ -22,3 +24,32 @@ impl Expiring { impl Lifetime for Expiring { fn expired(&self) -> bool { self.expires < UTC::now() } } + +impl FromResponse for Expiring { + fn from_response(json: &Json) -> Result { + let obj = try!(JsonHelper(json).as_object()); + + let refresh_token = try!(obj.get_string("refresh_token")); + let expires_in = try!(obj.get_i64("expires_in")); + + Ok(Expiring { + refresh_token: refresh_token.into(), + expires: UTC::now() + Duration::seconds(expires_in), + }) + } + + fn from_response_inherit(json: &Json, prev: &Self) -> Result { + let obj = try!(JsonHelper(json).as_object()); + + let refresh_token = try! { + obj.get_string("refresh_token") + .or(Ok(&prev.refresh_token)) + }; + let expires_in = try!(obj.get_i64("expires_in")); + + Ok(Expiring { + refresh_token: refresh_token.into(), + expires: UTC::now() + Duration::seconds(expires_in), + }) + } +} diff --git a/src/token/mod.rs b/src/token/mod.rs index 5952333..1771a95 100644 --- a/src/token/mod.rs +++ b/src/token/mod.rs @@ -5,10 +5,12 @@ //! //! Expiring and non-expiring tokens are abstracted through the `Lifetime` trait. +use client::response::FromResponse; + /// OAuth 2.0 tokens. /// /// See [RFC 6749, section 5](http://tools.ietf.org/html/rfc6749#section-5). -pub trait Token { +pub trait Token: FromResponse { /// Returns the access token. /// /// See [RF C6749, section 1.4](http://tools.ietf.org/html/rfc6749#section-1.4). @@ -22,7 +24,7 @@ pub trait Token { } /// OAuth 2.0 token lifetimes. -pub trait Lifetime { +pub trait Lifetime: FromResponse { /// Returns true if the access token is no longer valid. fn expired(&self) -> bool; } diff --git a/src/token/statik.rs b/src/token/statik.rs index 63655fc..4e64fad 100644 --- a/src/token/statik.rs +++ b/src/token/statik.rs @@ -1,4 +1,7 @@ +use rustc_serialize::json::Json; + use super::Lifetime; +use client::response::{FromResponse, ParseError, JsonHelper}; /// A static, non-expiring token. #[derive(Debug)] @@ -7,3 +10,13 @@ pub struct Static; impl Lifetime for Static { fn expired(&self) -> bool { false } } + +impl FromResponse for Static { + fn from_response(json: &Json) -> Result { + let obj = try!(JsonHelper(json).as_object()); + if obj.0.contains_key("expires_in") { + return Err(ParseError::UnexpectedField("expires_in")); + } + Ok(Static) + } +}