mirror of
https://github.com/OMGeeky/yup-oauth2.git
synced 2026-01-24 10:50:24 +01:00
feat(common): Token type serialization support
This works by storing only timestamps. Some convenience is provided as well.
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
#![feature(env, collections)]
|
||||
extern crate "yup-oauth2" as oauth2;
|
||||
extern crate "yup-hyper-mock" as mock;
|
||||
extern crate hyper;
|
||||
@@ -6,14 +7,12 @@ extern crate getopts;
|
||||
|
||||
use chrono::{Local};
|
||||
use getopts::{HasArg,Options,Occur,Fail};
|
||||
use std::os;
|
||||
use std::old_io::{File, FileMode, FileAccess};
|
||||
use std::old_path::Path;
|
||||
use std::env;
|
||||
|
||||
fn usage(program: &str, opts: &Options, err: Option<Fail>) {
|
||||
if err.is_some() {
|
||||
println!("{}", err.unwrap());
|
||||
os::set_exit_status(1);
|
||||
env::set_exit_status(1);
|
||||
}
|
||||
println!("{}", opts.short_usage(program) + " SCOPE [SCOPE ...]");
|
||||
println!("{}", opts.usage("A program to authenticate against oauthv2 services.\n\
|
||||
@@ -22,7 +21,7 @@ fn usage(program: &str, opts: &Options, err: Option<Fail>) {
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let args = os::args();
|
||||
let args: Vec<String> = env::args().collect();
|
||||
let prog = args[0].clone();
|
||||
|
||||
let mut opts = Options::new();
|
||||
@@ -70,6 +69,6 @@ fn main() {
|
||||
println!("{:?}", t);
|
||||
} else {
|
||||
println!("Invalid client id, invalid scope, user denied access or request expired");
|
||||
os::set_exit_status(10);
|
||||
env::set_exit_status(10);
|
||||
}
|
||||
}
|
||||
@@ -1,20 +1,70 @@
|
||||
use chrono::{DateTime, UTC};
|
||||
use chrono::{DateTime, UTC, TimeZone};
|
||||
|
||||
/// Represents a token as returned by OAuth2 servers.
|
||||
///
|
||||
/// It is produced by all authentication flows.
|
||||
/// It authenticates certain operations, and must be refreshed once
|
||||
/// it reached it's expirey date.
|
||||
#[derive(Clone, PartialEq, Debug)]
|
||||
/// it reached it's expiry date.
|
||||
///
|
||||
/// The type is tuned to be suitable for direct de-serialization from server
|
||||
/// replies, as well as for serialization for later reuse. This is the reason
|
||||
/// for the two fields dealing with expiry - once in relative in and once in
|
||||
/// absolute terms.
|
||||
///
|
||||
/// Utility methods make common queries easier, see `invalid()` or `expired()`.
|
||||
#[derive(Clone, PartialEq, Debug, RustcDecodable, RustcEncodable)]
|
||||
pub struct Token {
|
||||
/// used when authenticating calls to oauth2 enabled services
|
||||
/// used when authenticating calls to oauth2 enabled services.
|
||||
pub access_token: String,
|
||||
/// used to refresh an expired access_token
|
||||
/// used to refresh an expired access_token.
|
||||
pub refresh_token: String,
|
||||
/// The token type as string - usually 'Bearer'
|
||||
/// The token type as string - usually 'Bearer'.
|
||||
pub token_type: String,
|
||||
/// access_token is not valid for use after this date
|
||||
pub expires_at: DateTime<UTC>
|
||||
/// access_token will expire after this amount of time.
|
||||
/// Prefer using expiry_date()
|
||||
pub expires_in: Option<i64>,
|
||||
|
||||
/// timestamp is seconds since epoch indicating when the token will expire in absolute terms.
|
||||
/// use expiry_date() to convert to DateTime.
|
||||
pub expires_in_timestamp: Option<i64>,
|
||||
}
|
||||
|
||||
impl Token {
|
||||
|
||||
/// Returns true if the access token is expired or unset.
|
||||
pub fn invalid(&self) -> bool {
|
||||
self.access_token.len() == 0
|
||||
|| self.refresh_token.len() == 0
|
||||
|| self.expired()
|
||||
}
|
||||
|
||||
/// Returns true if we are expired.
|
||||
///
|
||||
/// # Panics
|
||||
/// * if our access_token is unset
|
||||
pub fn expired(&self) -> bool {
|
||||
if self.access_token.len() == 0 || self.refresh_token.len() == 0 {
|
||||
panic!("called expired() on unset token");
|
||||
}
|
||||
self.expiry_date() <= UTC::now()
|
||||
}
|
||||
|
||||
/// Returns a DateTime object representing our expiry date.
|
||||
pub fn expiry_date(&self) -> DateTime<UTC> {
|
||||
UTC.timestamp(self.expires_in_timestamp.unwrap(), 0)
|
||||
}
|
||||
|
||||
/// Adjust our stored expiry format to be absolute, using the current time.
|
||||
pub fn set_expiry_absolute(&mut self) -> &mut Token {
|
||||
if self.expires_in_timestamp.is_some() {
|
||||
assert!(self.expires_in.is_none());
|
||||
return self
|
||||
}
|
||||
|
||||
self.expires_in_timestamp = Some(UTC::now().timestamp() + self.expires_in.unwrap());
|
||||
self.expires_in = None;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// All known authentication types, for suitable constants
|
||||
@@ -74,7 +124,7 @@ mod tests {
|
||||
fn console_secret() {
|
||||
use rustc_serialize::json;
|
||||
match json::decode::<ConsoleApplicationSecret>(SECRET) {
|
||||
Ok(s) => assert!(s.installed.is_some()),
|
||||
Ok(s) => assert!(s.installed.is_some() && s.web.is_none()),
|
||||
Err(err) => panic!(err),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -261,14 +261,6 @@ impl<NC> DeviceFlow<NC>
|
||||
}
|
||||
};
|
||||
|
||||
#[derive(RustcDecodable)]
|
||||
struct JsonToken {
|
||||
access_token: String,
|
||||
refresh_token: String,
|
||||
token_type: String,
|
||||
expires_in: i64,
|
||||
}
|
||||
|
||||
#[derive(RustcDecodable)]
|
||||
struct JsonError {
|
||||
error: String
|
||||
@@ -288,13 +280,9 @@ impl<NC> DeviceFlow<NC>
|
||||
}
|
||||
|
||||
// yes, we expect that !
|
||||
let t: JsonToken = json::decode(&json_str).unwrap();
|
||||
self.state = PollResult::AccessGranted(Token {
|
||||
access_token: t.access_token,
|
||||
refresh_token: t.refresh_token,
|
||||
token_type: t.token_type,
|
||||
expires_at: UTC::now() + Duration::seconds(t.expires_in)
|
||||
});
|
||||
let mut t: Token = json::decode(&json_str).unwrap();
|
||||
t.set_expiry_absolute();
|
||||
self.state = PollResult::AccessGranted(t);
|
||||
},
|
||||
// In any other state, we will bail out and do nothing
|
||||
_ => {}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
use common::AuthenticationType;
|
||||
|
||||
use std::time::Duration;
|
||||
use chrono::UTC;
|
||||
use hyper;
|
||||
use hyper::header::ContentType;
|
||||
@@ -106,7 +105,8 @@ impl<NC> RefreshFlow<NC>
|
||||
access_token: t.access_token,
|
||||
token_type: t.token_type,
|
||||
refresh_token: refresh_token.to_string(),
|
||||
expires_at: UTC::now() + Duration::seconds(t.expires_in)
|
||||
expires_in: None,
|
||||
expires_in_timestamp: Some(UTC::now().timestamp() + t.expires_in),
|
||||
});
|
||||
|
||||
&self.result
|
||||
@@ -142,8 +142,10 @@ mod tests {
|
||||
|
||||
match *flow.refresh_token(AuthenticationType::Device,
|
||||
"bogus", "secret", "bogus_refresh_token") {
|
||||
RefreshResult::Success(ref t) => assert_eq!(t.access_token,
|
||||
"1/fFAGRNJru1FTz70BzhT3Zg"),
|
||||
RefreshResult::Success(ref t) => {
|
||||
assert_eq!(t.access_token, "1/fFAGRNJru1FTz70BzhT3Zg");
|
||||
assert!(!t.expired() && !t.invalid());
|
||||
},
|
||||
_ => unreachable!()
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user