Skip to content

Commit

Permalink
Merge branch 'next'
Browse files Browse the repository at this point in the history
  • Loading branch information
Byron committed Mar 7, 2015
2 parents fda2d62 + 437a609 commit bddf5d9
Show file tree
Hide file tree
Showing 7 changed files with 89 additions and 67 deletions.
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]

name = "yup-oauth2"
version = "0.2.0"
version = "0.2.2"
authors = ["Sebastian Thiel <byronimo@gmail.com>"]
repository = "https://github.com/Byron/yup-oauth2"
description = "A partial oauth2 implementation, providing the 'device' authorization flow"
Expand All @@ -20,5 +20,5 @@ rustc-serialize = "*"

[dev-dependencies]
getopts = "*"
yup-hyper-mock = "*"
yup-hyper-mock = ">= 0.1.3"
open = "*"
1 change: 1 addition & 0 deletions examples/auth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ extern crate chrono;
extern crate getopts;
extern crate open;

use oauth2::GetToken;
use chrono::{Local};
use getopts::{HasArg,Options,Occur,Fail};
use std::env;
Expand Down
4 changes: 2 additions & 2 deletions src/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ impl Str for FlowType {

/// Represents either 'installed' or 'web' applications in a json secrets file.
/// See `ConsoleApplicationSecret` for more information
#[derive(RustcDecodable, RustcEncodable, Clone)]
#[derive(RustcDecodable, RustcEncodable, Clone, Default)]
pub struct ApplicationSecret {
/// The client ID.
pub client_id: String,
Expand All @@ -106,7 +106,7 @@ pub struct ApplicationSecret {

/// A type to facilitate reading and writing the json secret file
/// as returned by the [google developer console](https://code.google.com/apis/console)
#[derive(RustcDecodable, RustcEncodable)]
#[derive(RustcDecodable, RustcEncodable, Default)]
pub struct ConsoleApplicationSecret {
pub web: Option<ApplicationSecret>,
pub installed: Option<ApplicationSecret>
Expand Down
8 changes: 6 additions & 2 deletions src/device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use rustc_serialize::json;
use chrono::{DateTime,UTC};
use std::borrow::BorrowMut;
use std::marker::PhantomData;
use std::io::Read;

use common::{Token, FlowType, Flow};

Expand Down Expand Up @@ -192,7 +193,8 @@ impl<C, NC> DeviceFlow<C, NC>
// json::Json::from_reader(&mut res)
// .ok()
// .expect("decode must work!"))).unwrap();
let json_str = String::from_utf8(res.read_to_end().unwrap()).unwrap();
let mut json_str = String::new();
res.read_to_string(&mut json_str).ok().expect("string decode must work");

// check for error
match json::decode::<JsonError>(&json_str) {
Expand Down Expand Up @@ -267,7 +269,9 @@ impl<C, NC> DeviceFlow<C, NC>
return PollResult::Error(err);
}
Ok(mut res) => {
String::from_utf8(res.read_to_end().unwrap()).unwrap()
let mut json_str = String::new();
res.read_to_string(&mut json_str).ok().expect("string decode must work");
json_str
}
};

Expand Down
128 changes: 71 additions & 57 deletions src/helper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,13 @@ pub struct Authenticator<D, S, C, NC> {
_m: PhantomData<NC>
}

/// A provider for authorization tokens, yielding tokens valid for a given scope.
pub trait GetToken {
fn token<'b, I, T>(&mut self, scopes: I) -> Option<Token>
where T: Str + Ord,
I: IntoIterator<Item=&'b T>;
}

impl<D, S, C, NC> Authenticator<D, S, C, NC>
where D: AuthenticatorDelegate,
S: TokenStorage,
Expand Down Expand Up @@ -113,11 +120,74 @@ impl<D, S, C, NC> Authenticator<D, S, C, NC>
}
}

fn retrieve_device_token(&mut self, scopes: &Vec<&str>) -> Option<Token> {
let mut flow = DeviceFlow::new(self.client.borrow_mut());

// PHASE 1: REQUEST CODE
loop {
let res = flow.request_code(&self.secret.client_id,
&self.secret.client_secret, scopes.iter());
match res {
RequestResult::Error(err) => {
match self.delegate.connection_error(err) {
Retry::Abort => return None,
Retry::After(d) => sleep(d),
}
},
RequestResult::InvalidClient
|RequestResult::InvalidScope(_) => {
self.delegate.request_failure(res);
return None
}
RequestResult::ProceedWithPolling(pi) => {
self.delegate.present_user_code(pi);
break
}
}
}

// PHASE 1: POLL TOKEN
loop {
match flow.poll_token() {
PollResult::Error(err) => {
match self.delegate.connection_error(err) {
Retry::Abort => return None,
Retry::After(d) => sleep(d),
}
},
PollResult::Expired(t) => {
self.delegate.expired(t);
return None
},
PollResult::AccessDenied => {
self.delegate.denied();
return None
},
PollResult::AuthorizationPending(pi) => {
match self.delegate.pending(&pi) {
Retry::Abort => return None,
Retry::After(d) => sleep(min(d, pi.interval)),
}
},
PollResult::AccessGranted(token) => {
return Some(token)
},
}
}
}
}

impl<D, S, C, NC> GetToken for Authenticator<D, S, C, NC>
where D: AuthenticatorDelegate,
S: TokenStorage,
NC: hyper::net::NetworkConnector,
C: BorrowMut<hyper::Client<NC>> {

/// Blocks until a token was retrieved from storage, from the server, or until the delegate
/// decided to abort the attempt, or the user decided not to authorize the application.
/// In any failure case, the returned token will be None, otherwise it is guaranteed to be
/// valid for the given scopes.
pub fn token<'b, I, T>(&mut self, scopes: I) -> Option<Token>
fn token<'b, I, T>(&mut self, scopes: I) -> Option<Token>
where T: Str + Ord,
I: IntoIterator<Item=&'b T> {
let (scope_key, scopes) = {
Expand Down Expand Up @@ -175,62 +245,6 @@ impl<D, S, C, NC> Authenticator<D, S, C, NC>
},
}
}

fn retrieve_device_token(&mut self, scopes: &Vec<&str>) -> Option<Token> {
let mut flow = DeviceFlow::new(self.client.borrow_mut());

// PHASE 1: REQUEST CODE
loop {
let res = flow.request_code(&self.secret.client_id,
&self.secret.client_secret, scopes.iter());
match res {
RequestResult::Error(err) => {
match self.delegate.connection_error(err) {
Retry::Abort => return None,
Retry::After(d) => sleep(d),
}
},
RequestResult::InvalidClient
|RequestResult::InvalidScope(_) => {
self.delegate.request_failure(res);
return None
}
RequestResult::ProceedWithPolling(pi) => {
self.delegate.present_user_code(pi);
break
}
}
}

// PHASE 1: POLL TOKEN
loop {
match flow.poll_token() {
PollResult::Error(err) => {
match self.delegate.connection_error(err) {
Retry::Abort => return None,
Retry::After(d) => sleep(d),
}
},
PollResult::Expired(t) => {
self.delegate.expired(t);
return None
},
PollResult::AccessDenied => {
self.delegate.denied();
return None
},
PollResult::AuthorizationPending(pi) => {
match self.delegate.pending(&pi) {
Retry::Abort => return None,
Retry::After(d) => sleep(min(d, pi.interval)),
}
},
PollResult::AccessGranted(token) => {
return Some(token)
},
}
}
}
}


Expand Down
6 changes: 3 additions & 3 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#![feature(old_io, std_misc, core, hash)]
#![feature(io, old_io, std_misc, core, hash)]
//! 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
//! for [refreshing tokens](https://developers.google.com/youtube/v3/guides/authentication#devices)
Expand All @@ -18,7 +18,7 @@
//! extern crate "yup-oauth2" as oauth2;
//! extern crate "rustc-serialize" as rustc_serialize;
//!
//! use oauth2::{Authenticator, DefaultAuthenticatorDelegate, PollInformation, ConsoleApplicationSecret, MemoryStorage};
//! use oauth2::{Authenticator, DefaultAuthenticatorDelegate, PollInformation, ConsoleApplicationSecret, MemoryStorage, GetToken};
//! use rustc_serialize::json;
//! use std::default::Default;
//! # 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\"}}";
Expand Down Expand Up @@ -83,4 +83,4 @@ pub use device::{DeviceFlow, PollInformation, PollResult};
pub use refresh::{RefreshFlow, RefreshResult};
pub use common::{Token, FlowType, ApplicationSecret, ConsoleApplicationSecret};
pub use helper::{TokenStorage, NullStorage, MemoryStorage, Authenticator,
AuthenticatorDelegate, Retry, DefaultAuthenticatorDelegate};
AuthenticatorDelegate, Retry, DefaultAuthenticatorDelegate, GetToken};
5 changes: 4 additions & 1 deletion src/refresh.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use url::form_urlencoded;
use super::Token;
use std::borrow::BorrowMut;
use std::marker::PhantomData;
use std::io::Read;

/// Implements the [Outh2 Refresh Token Flow](https://developers.google.com/youtube/v3/guides/authentication#devices).
///
Expand Down Expand Up @@ -82,7 +83,9 @@ impl<C, NC> RefreshFlow<C, NC>
return &self.result;
}
Ok(mut res) => {
String::from_utf8(res.read_to_end().unwrap()).unwrap()
let mut json_str = String::new();
res.read_to_string(&mut json_str).ok().expect("string decode must work");
json_str
}
};

Expand Down

0 comments on commit bddf5d9

Please sign in to comment.