test(auth): Authenticator test

Also showed that for some reason, BorrowMut<TokenStorage> can't really
be passed, even though it works for Client struct, which is as
simple as well.
This commit is contained in:
Sebastian Thiel
2015-02-28 11:09:23 +01:00
parent c227c161fd
commit 374804331a
4 changed files with 48 additions and 32 deletions

View File

@@ -108,16 +108,16 @@ pub struct ApplicationSecret {
/// as returned by the [google developer console](https://code.google.com/apis/console)
#[derive(RustcDecodable, RustcEncodable)]
pub struct ConsoleApplicationSecret {
web: Option<ApplicationSecret>,
installed: Option<ApplicationSecret>
pub web: Option<ApplicationSecret>,
pub installed: Option<ApplicationSecret>
}
#[cfg(test)]
mod tests {
pub mod tests {
use super::*;
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\"}}";
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() {

View File

@@ -442,7 +442,7 @@ pub trait DeviceFlowHelperDelegate {
#[cfg(test)]
mod tests {
pub mod tests {
use super::*;
use std::default::Default;
use hyper;
@@ -505,24 +505,4 @@ mod tests {
// As our mock has only 3 items, we would panic on this call
flow.poll_token();
}
#[test]
fn authenticator() {
struct TestHandler;
impl DeviceFlowHelperDelegate for TestHandler {
fn present_user_code(&mut self, pi: PollInformation) {
println!("{:?}", pi);
}
}
if let Some(t) = DeviceFlowHelper::new(&mut TestHandler)
.retrieve_token(hyper::Client::with_connector(
<MockGoogleAuth as Default>::default()),
"bogus_client_id",
"bogus_secret",
&["https://www.googleapis.com/auth/youtube.upload"]) {
assert_eq!(t.access_token, "1/fFAGRNJru1FTz70BzhT3Zg")
} else {
panic!("Expected to retrieve token in one go");
}
}
}

View File

@@ -24,6 +24,7 @@ pub trait TokenStorage {
}
/// A storage that remembers nothing.
#[derive(Default)]
pub struct NullStorage;
impl TokenStorage for NullStorage {
@@ -32,6 +33,7 @@ impl TokenStorage for NullStorage {
}
/// A storage that remembers values for one session only.
#[derive(Default)]
pub struct MemoryStorage {
pub tokens: HashMap<u64, Token>
}
@@ -68,7 +70,7 @@ pub struct Authenticator<D, S, C, NC> {
impl<D, S, C, NC> Authenticator<D, S, C, NC>
where D: AuthenticatorDelegate,
S: BorrowMut<TokenStorage>,
S: TokenStorage,
NC: hyper::net::NetworkConnector,
C: BorrowMut<hyper::Client<NC>> {
@@ -86,7 +88,7 @@ impl<D, S, C, NC> Authenticator<D, S, C, NC>
/// * `flow_type` - the kind of authentication to use to obtain a token for the
/// required scopes. If unset, it will be derived from the secret.
/// [dev-con]: https://console.developers.google.com
fn new(secret: &ApplicationSecret,
pub fn new(secret: &ApplicationSecret,
delegate: D, client: C, storage: S, flow_type: Option<FlowType>)
-> Authenticator<D, S, C, NC> {
Authenticator {
@@ -103,7 +105,7 @@ impl<D, S, C, NC> Authenticator<D, S, C, NC>
/// 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.
fn token<'b, I, T>(&mut self, scopes: I) -> Option<Token>
pub fn token<'b, I, T>(&mut self, scopes: I) -> Option<Token>
where T: Str + Ord,
I: IntoIterator<Item=&'b T> {
let (scope_key, scope, scopes) = {
@@ -120,7 +122,7 @@ impl<D, S, C, NC> Authenticator<D, S, C, NC>
};
// Get cached token. Yes, let's do an explicit return
return match self.storage.borrow().get(scope_key) {
return match self.storage.get(scope_key) {
Some(mut t) => {
// t needs refresh ?
if t.expired() {
@@ -142,7 +144,7 @@ impl<D, S, C, NC> Authenticator<D, S, C, NC>
},
RefreshResult::Success(ref new_t) => {
t = new_t.clone();
self.storage.borrow_mut().set(scope_key, Some(t.clone()));
self.storage.set(scope_key, Some(t.clone()));
}
}// RefreshResult handling
}// refresh loop
@@ -156,7 +158,7 @@ impl<D, S, C, NC> Authenticator<D, S, C, NC>
};
// store it, no matter what. If tokens have become invalid, it's ok
// to indicate that to the storage.
self.storage.borrow_mut().set(scope_key, ot.clone());
self.storage.set(scope_key, ot.clone());
ot
},
}
@@ -271,3 +273,37 @@ pub enum Retry {
/// Signals you want to retry after the given duration
After(Duration)
}
#[cfg(test)]
mod tests {
use super::*;
use super::super::device::tests::MockGoogleAuth;
use super::super::common::tests::SECRET;
use super::super::common::{ConsoleApplicationSecret, Token};
use super::super::device::PollInformation;
use std::default::Default;
use hyper;
#[test]
fn flow() {
use rustc_serialize::json;
struct TestHandler;
impl AuthenticatorDelegate for TestHandler {
fn present_user_code(&mut self, pi: PollInformation) {
println!("{:?}", pi);
}
}
let secret = json::decode::<ConsoleApplicationSecret>(SECRET).unwrap().installed.unwrap();
let res = Authenticator::new(&secret, TestHandler,
hyper::Client::with_connector(<MockGoogleAuth as Default>::default()),
<MemoryStorage as Default>::default(), None)
.token(&["https://www.googleapis.com/auth/youtube.upload"]);
match res {
Some(t) => assert_eq!(t.access_token, "1/fFAGRNJru1FTz70BzhT3Zg"),
_ => panic!("Expected to retrieve token in one go"),
}
}
}

View File

@@ -1,4 +1,4 @@
#![feature(old_io, std_misc, core)]
#![feature(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)