diff --git a/Cargo.toml b/Cargo.toml index 9f320c4..1f11b39 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "yup-oauth2" -version = "0.3.0" +version = "0.3.2" authors = ["Sebastian Thiel "] repository = "https://github.com/Byron/yup-oauth2" description = "A partial oauth2 implementation, providing the 'device' authorization flow" @@ -20,5 +20,5 @@ rustc-serialize = "*" [dev-dependencies] getopts = "*" -yup-hyper-mock = ">= 0.1.3" +yup-hyper-mock = ">= 0.1.4" open = "*" diff --git a/src/common.rs b/src/common.rs index 39e6db8..a0bf758 100644 --- a/src/common.rs +++ b/src/common.rs @@ -1,11 +1,73 @@ use chrono::{DateTime, UTC, TimeZone}; use std::marker::MarkerTrait; +use std::fmt; +use std::str::FromStr; +use hyper; /// A marker trait for all Flows pub trait Flow : MarkerTrait { fn type_id() -> FlowType; } +/// Represents all implemented token types +#[derive(Clone, PartialEq, Debug)] +pub enum TokenType { + /// Means that whoever bears the access token will be granted access + Bearer, +} + +impl Str for TokenType { + fn as_slice(&self) -> &'static str { + match *self { + TokenType::Bearer => "Bearer" + } + } +} + +impl FromStr for TokenType { + type Err = (); + fn from_str(s: &str) -> Result { + match s { + "Bearer" => Ok(TokenType::Bearer), + _ => Err(()) + } + } +} + + +/// A scheme for use in `hyper::header::Authorization` +#[derive(Clone, PartialEq, Debug)] +pub struct Scheme { + /// The type of our access token + pub token_type: TokenType, + /// The token returned by one of the Authorization Flows + pub access_token: String +} + +impl hyper::header::Scheme for Scheme { + fn scheme() -> Option<&'static str> { + None + } + + fn fmt_scheme(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{} {}", self.token_type.as_slice(), self.access_token) + } +} + +impl FromStr for Scheme { + type Err = &'static str; + fn from_str(s: &str) -> Result { + let parts: Vec<&str> = s.split(' ').collect(); + if parts.len() != 2 { + return Err("Expected two parts: ") + } + match ::from_str(parts[0]) { + Ok(t) => Ok(Scheme { token_type: t, access_token: parts[1].to_string() }), + Err(_) => Err("Couldn't parse token type") + } + } +} + /// Represents a token as returned by OAuth2 servers. /// /// It is produced by all authentication flows. @@ -66,7 +128,7 @@ impl Token { } /// All known authentication types, for suitable constants -#[derive(Copy)] +#[derive(Clone, Copy)] pub enum FlowType { /// [device authentication](https://developers.google.com/youtube/v3/guides/authentication#devices) Device, @@ -116,6 +178,7 @@ pub struct ConsoleApplicationSecret { #[cfg(test)] pub mod tests { use super::*; + use hyper; pub const SECRET: &'static str = "{\"installed\":{\"auth_uri\":\"https://accounts.google.com/o/oauth2/auth\",\"client_secret\":\"UqkDJd5RFwnHoiG5x5Rub8SI\",\"token_uri\":\"https://accounts.google.com/o/oauth2/token\",\"client_email\":\"\",\"redirect_uris\":[\"urn:ietf:wg:oauth:2.0:oob\",\"oob\"],\"client_x509_cert_url\":\"\",\"client_id\":\"14070749909-vgip2f1okm7bkvajhi9jugan6126io9v.apps.googleusercontent.com\",\"auth_provider_x509_cert_url\":\"https://www.googleapis.com/oauth2/v1/certs\"}}"; @@ -127,4 +190,19 @@ pub mod tests { Err(err) => panic!(err), } } + + #[test] + fn schema() { + let s = Scheme {token_type: TokenType::Bearer, access_token: "foo".to_string() }; + let mut headers = hyper::header::Headers::new(); + headers.set(hyper::header::Authorization(s)); + assert_eq!(headers.to_string(), "Authorization: Bearer foo\r\n".to_string()); + } + + #[test] + fn parse_schema() { + let auth: hyper::header::Authorization = hyper::header::Header::parse_header(&[b"Bearer foo".to_vec()]).unwrap(); + assert_eq!(auth.0.token_type, TokenType::Bearer); + assert_eq!(auth.0.access_token, "foo".to_string()); + } } \ No newline at end of file diff --git a/src/device.rs b/src/device.rs index 6ea6fc0..4cb08bd 100644 --- a/src/device.rs +++ b/src/device.rs @@ -1,6 +1,7 @@ use std::iter::IntoIterator; use std::time::Duration; use std::default::Default; +use std::rc::Rc; use hyper; use hyper::header::ContentType; @@ -61,7 +62,7 @@ pub struct PollInformation { #[derive(Clone)] pub enum RequestResult { /// Indicates connection failure - Error(hyper::HttpError), + Error(Rc), /// The OAuth client was not found InvalidClient, /// Some requested scopes were invalid. String contains the scopes as part of @@ -85,7 +86,7 @@ impl RequestResult { #[derive(Clone)] pub enum PollResult { /// Connection failure - retry if you think it's worth it - Error(hyper::HttpError), + Error(Rc), /// See `PollInformation` AuthorizationPending(PollInformation), /// indicates we are expired, including the expiration date @@ -98,7 +99,7 @@ pub enum PollResult { impl Default for PollResult { fn default() -> PollResult { - PollResult::Error(hyper::HttpError::HttpStatusError) + PollResult::Error(Rc::new(hyper::HttpError::HttpStatusError)) } } @@ -166,7 +167,7 @@ impl DeviceFlow .body(req.as_slice()) .send() { Err(err) => { - return RequestResult::Error(err); + return RequestResult::Error(Rc::new(err)); } Ok(mut res) => { @@ -266,7 +267,7 @@ impl DeviceFlow .body(req.as_slice()) .send() { Err(err) => { - return PollResult::Error(err); + return PollResult::Error(Rc::new(err)); } Ok(mut res) => { let mut json_str = String::new(); diff --git a/src/helper.rs b/src/helper.rs index a931031..1652ec9 100644 --- a/src/helper.rs +++ b/src/helper.rs @@ -133,7 +133,7 @@ impl Authenticator &self.secret.client_secret, scopes.iter()); match res { RequestResult::Error(err) => { - match self.delegate.connection_error(err) { + match self.delegate.connection_error(&*err) { Retry::Abort => return None, Retry::After(d) => sleep(d), } @@ -154,7 +154,7 @@ impl Authenticator loop { match flow.poll_token() { PollResult::Error(err) => { - match self.delegate.connection_error(err) { + match self.delegate.connection_error(&*err) { Retry::Abort => return None, Retry::After(d) => sleep(d), } @@ -269,7 +269,7 @@ pub trait AuthenticatorDelegate { /// Called whenever there is an HttpError, usually if there are network problems. /// /// Return retry information. - fn connection_error(&mut self, hyper::HttpError) -> Retry { + fn connection_error(&mut self, &hyper::HttpError) -> Retry { Retry::Abort } diff --git a/src/lib.rs b/src/lib.rs index 69d5251..575cd1c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,4 @@ -#![feature(old_io, std_misc, core, hash)] +#![feature(old_io, std_misc, core)] #![allow(deprecated)] //! This library can be used to acquire oauth2.0 authentication for services. //! At the time of writing, only one way of doing so is implemented, the [device flow](https://developers.google.com/youtube/v3/guides/authentication#devices), along with a flow @@ -68,11 +68,11 @@ extern crate hyper; #[macro_use] extern crate log; #[cfg(test)] #[macro_use] -extern crate "yup-hyper-mock" as hyper_mock; +extern crate yup_hyper_mock as hyper_mock; extern crate mime; extern crate url; extern crate itertools; -extern crate "rustc-serialize" as rustc_serialize; +extern crate rustc_serialize as rustc_serialize; mod device; @@ -82,6 +82,6 @@ mod helper; pub use device::{DeviceFlow, PollInformation, PollResult}; pub use refresh::{RefreshFlow, RefreshResult}; -pub use common::{Token, FlowType, ApplicationSecret, ConsoleApplicationSecret}; +pub use common::{Token, FlowType, ApplicationSecret, ConsoleApplicationSecret, Scheme, TokenType}; pub use helper::{TokenStorage, NullStorage, MemoryStorage, Authenticator, AuthenticatorDelegate, Retry, DefaultAuthenticatorDelegate, GetToken};