Skip to content

Commit

Permalink
fix(API): overall improved error handling
Browse files Browse the repository at this point in the history
* Return `Result<Token, Box<Error>>` type wherever feasible
* increment version to 0.3.5
* remove all usages of depreceated std items
  • Loading branch information
Byron committed Apr 23, 2015
1 parent 84454d1 commit 2cdf8bb
Show file tree
Hide file tree
Showing 7 changed files with 303 additions and 108 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]

name = "yup-oauth2"
version = "0.3.4"
version = "0.3.5"
authors = ["Sebastian Thiel <byronimo@gmail.com>"]
repository = "https://github.com/Byron/yup-oauth2"
description = "A partial oauth2 implementation, providing the 'device' authorization flow"
Expand Down
18 changes: 10 additions & 8 deletions examples/auth.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#![feature(collections, old_io, std_misc, exit_status)]
#![feature(collections, thread_sleep, std_misc, exit_status)]
#![allow(deprecated)]
extern crate yup_oauth2 as oauth2;
extern crate yup_hyper_mock as mock;
Expand All @@ -13,7 +13,7 @@ use getopts::{HasArg,Options,Occur,Fail};
use std::env;
use std::default::Default;
use std::time::Duration;
use std::old_io::timer::sleep;
use std::thread::sleep;


fn usage(program: &str, opts: &Options, err: Option<Fail>) {
Expand Down Expand Up @@ -82,15 +82,17 @@ fn main() {
connector: hyper::net::HttpConnector(None)
});

if let Some(t) = oauth2::Authenticator::new(&secret, StdoutHandler, client,
oauth2::NullStorage, None)
.token(&m.free) {
match oauth2::Authenticator::new(&secret, StdoutHandler, client,
oauth2::NullStorage, None).token(&m.free) {
Ok(t) => {
println!("Authentication granted !");
println!("You should store the following information for use, or revoke it.");
println!("All dates are given in UTC.");
println!("{:?}", t);
} else {
println!("Invalid client id, invalid scope, user denied access or request expired");
env::set_exit_status(10);
},
Err(err) => {
println!("Access token wasn't obtained: {}", err);
env::set_exit_status(10);
}
}
}
6 changes: 6 additions & 0 deletions src/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ pub trait Flow : MarkerTrait {
fn type_id() -> FlowType;
}

#[derive(RustcDecodable)]
pub struct JsonError {
pub error: String,
pub error_description: Option<String>,
}

/// Represents all implemented token types
#[derive(Clone, PartialEq, Debug)]
pub enum TokenType {
Expand Down
71 changes: 55 additions & 16 deletions src/device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use std::iter::IntoIterator;
use std::time::Duration;
use std::default::Default;
use std::rc::Rc;
use std::fmt;

use hyper;
use hyper::header::ContentType;
Expand All @@ -12,7 +13,7 @@ use chrono::{DateTime,UTC};
use std::borrow::BorrowMut;
use std::io::Read;

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

pub const GOOGLE_TOKEN_URL: &'static str = "https://accounts.google.com/o/oauth2/token";

Expand Down Expand Up @@ -55,6 +56,12 @@ pub struct PollInformation {
pub server_message: String,
}

impl fmt::Display for PollInformation {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
writeln!(f, "Poll result was: '{}'", self.server_message)
}
}

/// Encapsulates all possible results of the `request_token(...)` operation
#[derive(Clone)]
pub enum RequestResult {
Expand All @@ -65,22 +72,47 @@ pub enum RequestResult {
/// Some requested scopes were invalid. String contains the scopes as part of
/// the server error message
InvalidScope(String),
/// A 'catch-all' variant containing the server error and description
/// First string is the error code, the second may be a more detailed description
NegativeServerResponse(String, Option<String>),
/// Indicates we may enter the next phase
ProceedWithPolling(PollInformation),
}

impl RequestResult {
fn from_server_message(msg: &str, desc: &str) -> RequestResult {
match msg {
impl From<JsonError> for RequestResult {
fn from(value: JsonError) -> RequestResult {
match &*value.error {
"invalid_client" => RequestResult::InvalidClient,
"invalid_scope" => RequestResult::InvalidScope(desc.to_string()),
_ => panic!("'{}' not understood", msg)
"invalid_scope" => RequestResult::InvalidScope(
value.error_description.unwrap_or("no description provided".to_string())
),
_ => RequestResult::NegativeServerResponse(value.error, value.error_description),
}
}
}

impl fmt::Display for RequestResult {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
match *self {
RequestResult::Error(ref err) => err.fmt(f),
RequestResult::InvalidClient => "Invalid Client".fmt(f),
RequestResult::InvalidScope(ref scope)
=> writeln!(f, "Invalid Scope: '{}'", scope),
RequestResult::NegativeServerResponse(ref error, ref desc) => {
try!(error.fmt(f));
if let &Some(ref desc) = desc {
try!(write!(f, ": {}", desc));
}
"\n".fmt(f)
},
RequestResult::ProceedWithPolling(ref pi)
=> write!(f, "Proceed with polling: {}", pi),
}
}
}

/// Encapsulates all possible results of a `poll_token(...)` operation
#[derive(Clone)]
#[derive(Clone, Debug)]
pub enum PollResult {
/// Connection failure - retry if you think it's worth it
Error(Rc<hyper::HttpError>),
Expand All @@ -94,12 +126,27 @@ pub enum PollResult {
AccessGranted(Token),
}

impl fmt::Display for PollResult {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
match *self {
PollResult::Error(ref err) => err.fmt(f),
PollResult::AuthorizationPending(ref pi) => pi.fmt(f),
PollResult::Expired(ref date)
=> writeln!(f, "Authentication expired at {}", date),
PollResult::AccessDenied => "Access denied by user".fmt(f),
PollResult::AccessGranted(ref token)
=> writeln!(f, "Access granted by user, expires at {}", token.expiry_date()),
}
}
}

impl Default for PollResult {
fn default() -> PollResult {
PollResult::Error(Rc::new(hyper::HttpError::HttpStatusError))
}
}


impl<C> DeviceFlow<C>
where C: BorrowMut<hyper::Client> {

Expand Down Expand Up @@ -176,13 +223,6 @@ impl<C> DeviceFlow<C>
interval: i64,
}


#[derive(RustcDecodable)]
struct JsonError {
error: String,
error_description: String
}

// This will work once hyper uses std::io::Reader
// let decoded: JsonData = rustc_serialize::Decodable::decode(
// &mut json::Decoder::new(
Expand All @@ -196,8 +236,7 @@ impl<C> DeviceFlow<C>
match json::decode::<JsonError>(&json_str) {
Err(_) => {}, // ignore, move on
Ok(res) => {
return RequestResult::from_server_message(&res.error,
&res.error_description)
return RequestResult::from(res)
}
}

Expand Down
Loading

0 comments on commit 2cdf8bb

Please sign in to comment.