use chrono::{DateTime, UTC, TimeZone};
use std::fmt;
use std::str::FromStr;
use hyper;
pub trait Flow {
fn type_id() -> FlowType;
}
#[derive(Deserialize)]
pub struct JsonError {
pub error: String,
pub error_description: Option<String>,
pub error_uri: Option<String>,
}
#[derive(Clone, PartialEq, Debug)]
pub enum TokenType {
Bearer,
}
impl AsRef<str> for TokenType {
fn as_ref(&self) -> &'static str {
match *self {
TokenType::Bearer => "Bearer"
}
}
}
impl FromStr for TokenType {
type Err = ();
fn from_str(s: &str) -> Result<TokenType, ()> {
match s {
"Bearer" => Ok(TokenType::Bearer),
_ => Err(())
}
}
}
#[derive(Clone, PartialEq, Debug)]
pub struct Scheme {
pub token_type: TokenType,
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_ref(), self.access_token)
}
}
impl FromStr for Scheme {
type Err = &'static str;
fn from_str(s: &str) -> Result<Scheme, &'static str> {
let parts: Vec<&str> = s.split(' ').collect();
if parts.len() != 2 {
return Err("Expected two parts: <token_type> <token>")
}
match <TokenType as FromStr>::from_str(parts[0]) {
Ok(t) => Ok(Scheme { token_type: t, access_token: parts[1].to_string() }),
Err(_) => Err("Couldn't parse token type")
}
}
}
#[derive(Clone, PartialEq, Debug, Deserialize, Serialize)]
pub struct Token {
pub access_token: String,
pub refresh_token: String,
pub token_type: String,
pub expires_in: Option<i64>,
pub expires_in_timestamp: Option<i64>,
}
impl Token {
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()
}
pub fn expiry_date(&self) -> DateTime<UTC> {
UTC.timestamp(self.expires_in_timestamp.expect("Tokens without an absolute expiry are invalid"), 0)
}
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
}
}
#[derive(Clone, Copy)]
pub enum FlowType {
Device,
InstalledInteractive,
InstalledRedirect(u32),
}
impl AsRef<str> for FlowType {
fn as_ref(&self) -> &'static str {
match *self {
FlowType::Device => "https://accounts.google.com/o/oauth2/device/code",
FlowType::InstalledInteractive => "https://accounts.google.com/o/oauth2/v2/auth",
FlowType::InstalledRedirect(_) => "https://accounts.google.com/o/oauth2/v2/auth",
}
}
}
#[derive(Deserialize, Serialize, Clone, Default)]
pub struct ApplicationSecret {
pub client_id: String,
pub client_secret: String,
pub token_uri: String,
pub auth_uri: String,
pub redirect_uris: Vec<String>,
pub project_id: Option<String>,
pub client_email: Option<String>,
pub auth_provider_x509_cert_url: Option<String>,
pub client_x509_cert_url: Option<String>
}
#[derive(Deserialize, Serialize, Default)]
pub struct ConsoleApplicationSecret {
pub web: Option<ApplicationSecret>,
pub installed: Option<ApplicationSecret>
}
#[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\"}}";
#[test]
fn console_secret() {
use serde_json as json;
match json::from_str::<ConsoleApplicationSecret>(SECRET) {
Ok(s) => assert!(s.installed.is_some() && s.web.is_none()),
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<Scheme> = 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());
}
}