#![deny(deprecated, drop_with_repr_extern, improper_ctypes,
non_shorthand_field_patterns, overflowing_literals, plugin_as_library,
private_no_mangle_fns, private_no_mangle_statics, stable_features, unconditional_recursion,
unknown_lints, unused, unused_allocation, unused_attributes,
unused_comparisons, unused_features, unused_parens, while_true)]
#![warn(missing_docs, trivial_casts, trivial_numeric_casts, unused, unused_extern_crates,
unused_import_braces, unused_qualifications, unused_results, variant_size_differences)]
extern crate hyper;
extern crate chrono;
extern crate rustc_serialize;
extern crate fractal_dto as dto;
use std::time::Duration;
use std::io::Read;
use hyper::Client as HyperClient;
use hyper::client::IntoUrl;
use hyper::header::{Headers, Authorization, Accept, qitem};
use hyper::mime::{Mime, TopLevel, SubLevel, Attr, Value};
use hyper::status::StatusCode;
use rustc_serialize::base64::FromBase64;
use rustc_serialize::json;
use dto::FromDTO;
pub mod error;
pub mod types;
pub mod oauth;
use error::{Result, Error};
use types::User;
use oauth::{AccessToken, Scope};
pub const SECRET_LEN: usize = 32;
const FRACTAL_SERVER: &'static str = "https://api.fractal.global/";
const FRACTAL_DEV_SERVER: &'static str = "https://dev.fractal.global/";
pub struct ClientV1 {
client: HyperClient,
url: String,
}
impl ClientV1 {
pub fn new() -> ClientV1 {
ClientV1 {
client: hyper::Client::new(),
url: format!("{}/v1/", FRACTAL_SERVER),
}
}
pub fn new_with_url<S: AsRef<str>>(url: S) -> ClientV1 {
ClientV1 {
client: hyper::Client::new(),
url: format!("{}/v1/", url.as_ref()),
}
}
pub fn new_dev() -> ClientV1 {
ClientV1 {
client: hyper::Client::new(),
url: format!("{}/v1/", FRACTAL_DEV_SERVER),
}
}
pub fn set_read_timeout(&mut self, timeout: Option<Duration>) {
self.client.set_read_timeout(timeout);
}
pub fn set_write_timeout(&mut self, timeout: Option<Duration>) {
self.client.set_write_timeout(timeout);
}
pub fn token<S: AsRef<str>>(&self, app_id: S, secret: S) -> Result<AccessToken> {
match secret.as_ref().from_base64() {
Ok(b) => {
if b.len() == SECRET_LEN {
let mut headers = Headers::new();
headers.set(
Accept(vec![
qitem(Mime(TopLevel::Application, SubLevel::Json,
vec![(Attr::Charset, Value::Utf8)])),
])
);
headers.set(
Authorization(format!("Basic {}:{}", app_id.as_ref(), secret.as_ref()))
);
let mut response = try!(self.client
.get(&format!("{}/token", self.url))
.body("grant_type=client_credentials")
.headers(headers).send());
match response.status {
StatusCode::Ok => {
let mut response_str = String::new();
try!(response.read_to_string(&mut response_str));
Ok(try!(AccessToken::from_dto(try!(json::decode(&response_str)))))
},
_ => Err(Error::Unauthorized),
}
} else {
Err(Error::InvalidSecret)
}
},
Err(_) => Err(Error::InvalidSecret),
}
}
pub fn get_all_users(&self, access_token: AccessToken) -> Result<Vec<User>> {
if access_token.scopes().any(|s| s == &Scope::Admin) && !access_token.has_expired() {
let response = try!(self.client
.get(&format!("{}/get_all_users", self.url))
.header(Authorization(format!("{} {}",
access_token.get_token_type(),
access_token.as_str())))
.send());
unimplemented!()
} else {
Err(Error::Unauthorized)
}
}
}