Skip to content

Commit

Permalink
Merge branch 'dermesser:master' into master
Browse files Browse the repository at this point in the history
  • Loading branch information
luketpeterson authored Oct 24, 2022
2 parents 0899b51 + 45631f1 commit 3ecb212
Show file tree
Hide file tree
Showing 6 changed files with 257 additions and 24 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ rustls-pemfile = { version = "0.3", optional = true }
seahash = "4"
serde = {version = "1.0", features = ["derive"]}
serde_json = "1.0"
time = { version = "0.3.7", features = ["local-offset", "serde"] }
time = { version = "0.3.7", features = ["local-offset", "parsing", "serde"] }
tokio = { version = "1.0", features = ["fs", "macros", "io-std", "io-util", "time", "sync", "rt"] }
tower-service = "^0.3.1"
url = "2"
Expand Down
23 changes: 23 additions & 0 deletions examples/service_account_impersonation.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
use yup_oauth2::{read_authorized_user_secret, ServiceAccountImpersonationAuthenticator};

#[tokio::main]
async fn main() {
let svc_email = std::env::args().skip(1).next().unwrap();
let home = std::env::var("HOME").unwrap();

let user_secret =
read_authorized_user_secret(format!("{}/.config/gcloud/application_default_credentials.json", home))
.await
.expect("user secret");

let auth = ServiceAccountImpersonationAuthenticator::builder(user_secret, &svc_email)
.build()
.await
.expect("authenticator");

let scopes = &["https://www.googleapis.com/auth/youtube.readonly"];
match auth.token(scopes).await {
Err(e) => println!("error: {:?}", e),
Ok(t) => println!("token: {:?}", t),
}
}
110 changes: 91 additions & 19 deletions src/authenticator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,18 @@ use crate::device::DeviceFlow;
use crate::error::Error;
use crate::installed::{InstalledFlow, InstalledFlowReturnMethod};
use crate::refresh::RefreshFlow;
use crate::service_account_impersonator::ServiceAccountImpersonationFlow;

#[cfg(feature = "service_account")]
use crate::service_account::{self, ServiceAccountFlow, ServiceAccountFlowOpts, ServiceAccountKey};
use crate::storage::{self, Storage, TokenStorage};
use crate::types::{AccessToken, ApplicationSecret, TokenInfo};
use private::AuthFlow;

use crate::access_token::{AccessTokenFlow};
use crate::access_token::AccessTokenFlow;

use futures::lock::Mutex;
use http::{Uri};
use http::Uri;
use hyper::client::connect::Connection;
use std::borrow::Cow;
use std::error::Error as StdError;
Expand Down Expand Up @@ -428,22 +429,69 @@ pub struct AccessTokenAuthenticator;
impl AccessTokenAuthenticator {
/// the builder pattern for the authenticator
pub fn builder(
access_token: String,
access_token: String,
) -> AuthenticatorBuilder<DefaultHyperClient, AccessTokenFlow> {
Self::with_client(access_token, DefaultHyperClient)
Self::with_client(access_token, DefaultHyperClient)
}
/// Construct a new Authenticator that uses the installed flow and the provided http client.
/// the client itself is not used
pub fn with_client<C>(
access_token: String,
client: C,
access_token: String,
client: C,
) -> AuthenticatorBuilder<C, AccessTokenFlow> {
AuthenticatorBuilder::new(
AccessTokenFlow {
access_token: access_token,
},
client,
)
AuthenticatorBuilder::new(
AccessTokenFlow {
access_token: access_token,
},
client,
)
}
}

/// Create a access token authenticator that uses user secrets to impersonate
/// a service account.
///
/// ```
/// # #[cfg(any(feature = "hyper-rustls", feature = "hyper-tls"))]
/// # async fn foo() {
/// # use yup_oauth2::authenticator::AuthorizedUserAuthenticator;
/// # let secret = yup_oauth2::read_authorized_user_secret("/tmp/foo").await.unwrap();
/// # let email = "[email protected]";
/// let authenticator = yup_oauth2::ServiceAccountImpersonationAuthenticator::builder(secret, email)
/// .build()
/// .await
/// .expect("failed to create authenticator");
/// # }
/// ```
pub struct ServiceAccountImpersonationAuthenticator;
impl ServiceAccountImpersonationAuthenticator {
/// Use the builder pattern to create an Authenticator that uses the device flow.
#[cfg(any(feature = "hyper-rustls", feature = "hyper-tls"))]
#[cfg_attr(
yup_oauth2_docsrs,
doc(cfg(any(feature = "hyper-rustls", feature = "hyper-tls")))
)]
pub fn builder(
authorized_user_secret: AuthorizedUserSecret,
service_account_email: &str,
) -> AuthenticatorBuilder<DefaultHyperClient, ServiceAccountImpersonationFlow> {
Self::with_client(
authorized_user_secret,
service_account_email,
DefaultHyperClient,
)
}

/// Construct a new Authenticator that uses the installed flow and the provided http client.
pub fn with_client<C>(
authorized_user_secret: AuthorizedUserSecret,
service_account_email: &str,
client: C,
) -> AuthenticatorBuilder<C, ServiceAccountImpersonationFlow> {
AuthenticatorBuilder::new(
ServiceAccountImpersonationFlow::new(authorized_user_secret, service_account_email),
client,
)
}
}

Expand Down Expand Up @@ -706,6 +754,22 @@ impl<C> AuthenticatorBuilder<C, AuthorizedUserFlow> {
}
}

/// ## Methods available when building a service account impersonation Authenticator.
impl<C> AuthenticatorBuilder<C, ServiceAccountImpersonationFlow> {
/// Create the authenticator.
pub async fn build(self) -> io::Result<Authenticator<C::Connector>>
where
C: HyperClientBuilder,
{
Self::common_build(
self.hyper_client_builder,
self.storage_type,
AuthFlow::ServiceAccountImpersonationFlow(self.auth_flow),
)
.await
}
}

/// ## Methods available when building an access token flow Authenticator.
impl<C> AuthenticatorBuilder<C, AccessTokenFlow> {
/// Create the authenticator.
Expand All @@ -722,25 +786,27 @@ impl<C> AuthenticatorBuilder<C, AccessTokenFlow> {
}
}
mod private {
use crate::access_token::AccessTokenFlow;
use crate::application_default_credentials::ApplicationDefaultCredentialsFlow;
use crate::authorized_user::AuthorizedUserFlow;
use crate::authenticator::{AsyncRead, AsyncWrite, Connection, Service, StdError, Uri};
use crate::authorized_user::AuthorizedUserFlow;
use crate::device::DeviceFlow;
use crate::error::Error;
use crate::installed::InstalledFlow;
#[cfg(feature = "service_account")]
use crate::service_account::ServiceAccountFlow;
use crate::service_account_impersonator::ServiceAccountImpersonationFlow;
use crate::types::{ApplicationSecret, TokenInfo};
use crate::access_token::AccessTokenFlow;

pub enum AuthFlow {
DeviceFlow(DeviceFlow),
InstalledFlow(InstalledFlow),
#[cfg(feature = "service_account")]
ServiceAccountFlow(ServiceAccountFlow),
ServiceAccountImpersonationFlow(ServiceAccountImpersonationFlow),
ApplicationDefaultCredentialsFlow(ApplicationDefaultCredentialsFlow),
AuthorizedUserFlow(AuthorizedUserFlow),
AccessTokenFlow(AccessTokenFlow),
AccessTokenFlow(AccessTokenFlow),
}

impl AuthFlow {
Expand All @@ -750,9 +816,10 @@ mod private {
AuthFlow::InstalledFlow(installed_flow) => Some(&installed_flow.app_secret),
#[cfg(feature = "service_account")]
AuthFlow::ServiceAccountFlow(_) => None,
AuthFlow::ServiceAccountImpersonationFlow(_) => None,
AuthFlow::ApplicationDefaultCredentialsFlow(_) => None,
AuthFlow::AuthorizedUserFlow(_) => None,
AuthFlow::AccessTokenFlow(_) => None,
AuthFlow::AccessTokenFlow(_) => None,
}
}

Expand All @@ -777,15 +844,20 @@ mod private {
AuthFlow::ServiceAccountFlow(service_account_flow) => {
service_account_flow.token(hyper_client, scopes).await
}
AuthFlow::ServiceAccountImpersonationFlow(service_account_impersonation_flow) => {
service_account_impersonation_flow
.token(hyper_client, scopes)
.await
}
AuthFlow::ApplicationDefaultCredentialsFlow(adc_flow) => {
adc_flow.token(hyper_client, scopes).await
}
AuthFlow::AuthorizedUserFlow(authorized_user_flow) => {
authorized_user_flow.token(hyper_client, scopes).await
}
AuthFlow::AccessTokenFlow(access_token_flow) => {
access_token_flow.token(hyper_client, scopes).await
}
AuthFlow::AccessTokenFlow(access_token_flow) => {
access_token_flow.token(hyper_client, scopes).await
}
}
}
}
Expand Down
9 changes: 9 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,8 @@ pub enum Error {
UserError(String),
/// A lower level IO error.
LowLevelError(io::Error),
/// We required an access token, but received a response that didn't contain one.
MissingAccessToken,
/// Other errors produced by a storage provider
OtherError(anyhow::Error),
}
Expand Down Expand Up @@ -206,6 +208,13 @@ impl fmt::Display for Error {
}
Error::UserError(ref s) => s.fmt(f),
Error::LowLevelError(ref e) => e.fmt(f),
Error::MissingAccessToken => {
write!(
f,
"Expected an access token, but received a response without one"
)?;
Ok(())
}
Error::OtherError(ref e) => e.fmt(f),
}
}
Expand Down
9 changes: 5 additions & 4 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,16 +72,17 @@
#![deny(missing_docs)]
#![cfg_attr(yup_oauth2_docsrs, feature(doc_cfg))]

pub mod access_token;
mod application_default_credentials;
pub mod authenticator;
pub mod authenticator_delegate;
pub mod authorized_user;
pub mod access_token;
mod device;
pub mod error;
mod helper;
mod installed;
mod refresh;
pub mod service_account_impersonator;

#[cfg(feature = "service_account")]
mod service_account;
Expand All @@ -97,9 +98,9 @@ mod types;
pub use crate::authenticator::ServiceAccountAuthenticator;
#[doc(inline)]
pub use crate::authenticator::{
ApplicationDefaultCredentialsAuthenticator, AuthorizedUserAuthenticator,
DeviceFlowAuthenticator, InstalledFlowAuthenticator,
AccessTokenAuthenticator,
AccessTokenAuthenticator, ApplicationDefaultCredentialsAuthenticator,
AuthorizedUserAuthenticator, DeviceFlowAuthenticator, InstalledFlowAuthenticator,
ServiceAccountImpersonationAuthenticator,
};

pub use crate::helper::*;
Expand Down
Loading

0 comments on commit 3ecb212

Please sign in to comment.