From 732e5949624ddf151586becab475e66dcf82be61 Mon Sep 17 00:00:00 2001 From: Lewin Bormann Date: Wed, 12 Jun 2019 14:40:08 +0200 Subject: [PATCH] refactor(InstalledFlow): Implement GetToken for InstalledFlow --- examples/test-installed/src/main.rs | 16 +++++---- src/authenticator_delegate.rs | 3 +- src/installed.rs | 52 +++++++++++++++++++++++------ src/service_account.rs | 4 +-- src/types.rs | 2 +- 5 files changed, 55 insertions(+), 22 deletions(-) diff --git a/examples/test-installed/src/main.rs b/examples/test-installed/src/main.rs index 2e99263..47a8215 100644 --- a/examples/test-installed/src/main.rs +++ b/examples/test-installed/src/main.rs @@ -1,6 +1,6 @@ -use yup_oauth2::InstalledFlow; - use futures::prelude::*; +use yup_oauth2::GetToken; +use yup_oauth2::InstalledFlow; use hyper::client::Client; use hyper_tls::HttpsConnector; @@ -10,17 +10,19 @@ use std::path::Path; fn main() { let https = HttpsConnector::new(1).expect("tls"); let client = Client::builder().build::<_, hyper::Body>(https); - let mut inf = InstalledFlow::new( - client, - yup_oauth2::InstalledFlowReturnMethod::HTTPRedirect(8081), - ); let ad = yup_oauth2::DefaultAuthenticatorDelegate; let secret = yup_oauth2::read_application_secret(Path::new("clientsecret.json")) .expect("clientsecret.json"); + let mut inf = InstalledFlow::new( + client, + ad, + secret, + yup_oauth2::InstalledFlowReturnMethod::HTTPRedirect(8081), + ); let s = "https://www.googleapis.com/auth/drive.file".to_string(); let scopes = vec![s]; - let tok = inf.obtain_token(ad, secret, scopes.clone()); + let tok = inf.token(scopes.iter()); let fut = tok.map_err(|e| println!("error: {:?}", e)).and_then(|t| { println!("The token is {:?}", t); Ok(()) diff --git a/src/authenticator_delegate.rs b/src/authenticator_delegate.rs index 150f186..6d0de4f 100644 --- a/src/authenticator_delegate.rs +++ b/src/authenticator_delegate.rs @@ -70,7 +70,7 @@ impl fmt::Display for PollError { /// /// The only method that needs to be implemented manually is `present_user_code(...)`, /// as no assumptions are made on how this presentation should happen. -pub trait AuthenticatorDelegate { +pub trait AuthenticatorDelegate: Clone { /// Called whenever there is an client, usually if there are network problems. /// /// Return retry information. @@ -194,5 +194,6 @@ pub trait AuthenticatorDelegate { /// Uses all default implementations by AuthenticatorDelegate, and makes the trait's /// implementation usable in the first place. +#[derive(Clone)] pub struct DefaultAuthenticatorDelegate; impl AuthenticatorDelegate for DefaultAuthenticatorDelegate {} diff --git a/src/installed.rs b/src/installed.rs index a4cd61a..7e32c5c 100644 --- a/src/installed.rs +++ b/src/installed.rs @@ -17,7 +17,7 @@ use url::form_urlencoded; use url::percent_encoding::{percent_encode, QUERY_ENCODE_SET}; use crate::authenticator_delegate::AuthenticatorDelegate; -use crate::types::{ApplicationSecret, Token}; +use crate::types::{ApplicationSecret, GetToken, Token}; const OOB_REDIRECT_URI: &'static str = "urn:ietf:wg:oauth:2.0:oob"; @@ -61,9 +61,31 @@ where }) } -pub struct InstalledFlow { +impl< + AD: AuthenticatorDelegate + 'static + Send + Clone, + C: hyper::client::connect::Connect + 'static, + > GetToken for InstalledFlow +{ + fn token<'b, I, T>( + &mut self, + scopes: I, + ) -> Box> + Send> + where + T: AsRef + Ord + 'b, + I: Iterator, + { + Box::new(self.obtain_token(scopes.into_iter().map(|s| s.as_ref().to_string()).collect())) + } + fn api_key(&mut self) -> Option { + None + } +} + +pub struct InstalledFlow { method: InstalledFlowReturnMethod, client: hyper::client::Client, + ad: AD, + appsecret: ApplicationSecret, } /// cf. https://developers.google.com/identity/protocols/OAuth2InstalledApp#choosingredirecturi @@ -77,16 +99,25 @@ pub enum InstalledFlowReturnMethod { HTTPRedirect(u16), } -impl<'c, C: 'c + hyper::client::connect::Connect> InstalledFlow { +impl< + 'c, + AD: 'static + AuthenticatorDelegate + Clone + Send, + C: 'c + hyper::client::connect::Connect, + > InstalledFlow +{ /// Starts a new Installed App auth flow. /// If HTTPRedirect is chosen as method and the server can't be started, the flow falls /// back to Interactive. pub fn new( client: hyper::client::Client, + ad: AD, + secret: ApplicationSecret, method: InstalledFlowReturnMethod, - ) -> InstalledFlow { + ) -> InstalledFlow { InstalledFlow { method: method, + ad: ad, + appsecret: secret, client: client, } } @@ -97,13 +128,11 @@ impl<'c, C: 'c + hyper::client::connect::Connect> InstalledFlow { /// . Return that token /// /// It's recommended not to use the DefaultAuthenticatorDelegate, but a specialized one. - pub fn obtain_token<'a, AD: 'a + AuthenticatorDelegate + Send>( + pub fn obtain_token<'a>( &mut self, - auth_delegate: AD, - appsecret: ApplicationSecret, scopes: Vec, // Note: I haven't found a better way to give a list of strings here, due to ownership issues with futures. ) -> impl 'a + Future> + Send { - let rduri = auth_delegate.redirect_uri(); + let rduri = self.ad.redirect_uri(); // Start server on localhost to accept auth code. let server = if let InstalledFlowReturnMethod::HTTPRedirect(port) = self.method { match InstalledFlowServer::new(port) { @@ -119,7 +148,8 @@ impl<'c, C: 'c + hyper::client::connect::Connect> InstalledFlow { None }; let client = self.client.clone(); - let appsecclone = appsecret.clone(); + let (appsecclone, appsecclone2) = (self.appsecret.clone(), self.appsecret.clone()); + let auth_delegate = self.ad.clone(); server .into_future() // First: Obtain authorization code from user. @@ -129,7 +159,7 @@ impl<'c, C: 'c + hyper::client::connect::Connect> InstalledFlow { // Exchange the authorization code provided by Google for a refresh and an access // token. .and_then(move |authcode| { - let request = Self::request_token(appsecret, authcode, rduri, port); + let request = Self::request_token(appsecclone2, authcode, rduri, port); let result = client.request(request); // Handle result here, it makes ownership tracking easier. result @@ -186,7 +216,7 @@ impl<'c, C: 'c + hyper::client::connect::Connect> InstalledFlow { }) } - fn ask_authorization_code<'a, AD: AuthenticatorDelegate, S, T>( + fn ask_authorization_code<'a, S, T>( server: Option, mut auth_delegate: AD, appsecret: &ApplicationSecret, diff --git a/src/service_account.rs b/src/service_account.rs index 01cbd6d..9539f1d 100644 --- a/src/service_account.rs +++ b/src/service_account.rs @@ -317,7 +317,7 @@ where ) -> Box> + Send> where T: AsRef + Ord + 'b, - I: IntoIterator, + I: Iterator, { let (hash, scps) = hash_scopes(scopes); @@ -387,7 +387,7 @@ mod tests { let mut acc = ServiceAccountAccess::new(key, client); println!( "{:?}", - acc.token(vec![&"https://www.googleapis.com/auth/pubsub"]) + acc.token(vec!["https://www.googleapis.com/auth/pubsub"].iter()) .wait() ); } diff --git a/src/types.rs b/src/types.rs index 93e3dcc..520388d 100644 --- a/src/types.rs +++ b/src/types.rs @@ -191,7 +191,7 @@ pub trait GetToken { ) -> Box> + Send> where T: AsRef + Ord + 'b, - I: IntoIterator; + I: Iterator; fn api_key(&mut self) -> Option; }