work started on adc implementation

This commit is contained in:
Antti Peltonen
2021-06-09 11:44:04 +00:00
committed by Lukas Winkler
parent 5d0e431772
commit 7638946508
7 changed files with 158 additions and 4 deletions

View File

@@ -53,7 +53,7 @@ webbrowser = "0.5"
hyper-rustls = "0.22.1"
[workspace]
members = ["examples/test-installed/", "examples/test-svc-acct/", "examples/test-device/", "examples/service_account", "examples/drive_example"]
members = ["examples/test-installed/", "examples/test-svc-acct/", "examples/test-device/", "examples/service_account", "examples/drive_example", "examples/test-adc"]
[package.metadata.docs.rs]
all-features = true

View File

@@ -0,0 +1,9 @@
[package]
name = "test-adc"
version = "0.1.0"
authors = ["Antti Peltonen <antti.peltonen@iki.fi>"]
edition = "2018"
[dependencies]
yup-oauth2 = { path = "../../" }
tokio = { version = "1.0", features = ["macros", "rt-multi-thread"] }

View File

@@ -0,0 +1,14 @@
use yup_oauth2::ApplicationDefaultCredentialsAuthenticator;
#[tokio::main]
async fn main() {
let auth = ApplicationDefaultCredentialsAuthenticator::builder()
.await
.build()
.await
.unwrap();
let scopes = &["https://www.googleapis.com/auth/pubsub"];
let tok = auth.token(scopes).await.unwrap();
println!("token is: {:?}", tok);
}

View File

@@ -0,0 +1,36 @@
use crate::error::Error;
use crate::types::TokenInfo;
pub struct ApplicationDefaultCredentialsFlowOpts;
/// ServiceAccountFlow can fetch oauth tokens using a service account.
pub struct ApplicationDefaultCredentialsFlow;
impl ApplicationDefaultCredentialsFlow {
pub(crate) fn new(_opts: ApplicationDefaultCredentialsFlowOpts) -> Self {
ApplicationDefaultCredentialsFlow {}
}
pub(crate) async fn token<C, T>(
&self,
hyper_client: &hyper::Client<C>,
scopes: &[T],
) -> Result<TokenInfo, Error>
where
T: AsRef<str>,
C: hyper::client::connect::Connect + Clone + Send + Sync + 'static,
{
let scope = crate::helper::join(scopes, ",");
let token_uri = format!("http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token?scopes={}", scope);
let request = hyper::Request::get(token_uri)
.header("Metadata-Flavor", "Google")
.body(hyper::Body::from(String::new())) // why body is needed?
.unwrap();
log::debug!("requesting token from metadata server: {:?}", request);
let (head, body) = hyper_client.request(request).await?.into_parts();
let body = hyper::body::to_bytes(body).await?;
log::debug!("received response; head: {:?}, body: {:?}", head, body);
TokenInfo::from_json(&body)
}
}
// eof

View File

@@ -1,4 +1,7 @@
//! Module contianing the core functionality for OAuth2 Authentication.
use crate::application_default_credentials::{
ApplicationDefaultCredentialsFlow, ApplicationDefaultCredentialsFlowOpts,
};
use crate::authenticator_delegate::{DeviceFlowDelegate, InstalledFlowDelegate};
use crate::device::DeviceFlow;
use crate::error::Error;
@@ -254,6 +257,40 @@ impl ServiceAccountAuthenticator {
}
}
/// Create an authenticator that uses a application default credentials.
/// ```
/// # async fn foo() {
/// let authenticator = yup_oauth2::ApplicationDefaultCredentialsAuthenticator::builder()
/// .await
/// .build()
/// .await
/// .expect("failed to create authenticator");
/// # }
/// ```
pub struct ApplicationDefaultCredentialsAuthenticator;
impl ApplicationDefaultCredentialsAuthenticator {
/// Use the builder pattern to create an Authenticator that uses a service account.
pub async fn builder(
) -> AuthenticatorBuilder<DefaultHyperClient, ApplicationDefaultCredentialsFlowOpts> {
match std::env::var("GOOGLE_APPLICATION_CREDENTIALS") {
Ok(_path) => {
todo!()
// # here we would need to do something like this:
// let service_account_key = crate::read_service_account_key(path).await.unwrap();
// AuthenticatorBuilder::<DefaultHyperClient, _>::with_auth_flow(
// ServiceAccountFlowOpts {
// key: service_account_key,
// subject: None,
// },
// )
}
Err(_e) => AuthenticatorBuilder::<DefaultHyperClient, _>::with_auth_flow(
ApplicationDefaultCredentialsFlowOpts {},
),
}
}
}
/// ## Methods available when building any Authenticator.
/// ```
/// # #[cfg(any(feature = "hyper-rustls", feature = "hyper-tls"))]
@@ -479,7 +516,25 @@ impl<C> AuthenticatorBuilder<C, ServiceAccountFlowOpts> {
}
}
impl<C> AuthenticatorBuilder<C, ApplicationDefaultCredentialsFlowOpts> {
/// Create the authenticator.
pub async fn build(self) -> io::Result<Authenticator<C::Connector>>
where
C: HyperClientBuilder,
{
let application_default_credential_flow =
ApplicationDefaultCredentialsFlow::new(self.auth_flow);
Self::common_build(
self.hyper_client_builder,
self.storage_type,
AuthFlow::ApplicationDefaultCredentialsFlow(application_default_credential_flow),
)
.await
}
}
mod private {
use crate::application_default_credentials::ApplicationDefaultCredentialsFlow;
use crate::device::DeviceFlow;
use crate::error::Error;
use crate::installed::InstalledFlow;
@@ -490,6 +545,7 @@ mod private {
DeviceFlow(DeviceFlow),
InstalledFlow(InstalledFlow),
ServiceAccountFlow(ServiceAccountFlow),
ApplicationDefaultCredentialsFlow(ApplicationDefaultCredentialsFlow),
}
impl AuthFlow {
@@ -498,6 +554,7 @@ mod private {
AuthFlow::DeviceFlow(device_flow) => Some(&device_flow.app_secret),
AuthFlow::InstalledFlow(installed_flow) => Some(&installed_flow.app_secret),
AuthFlow::ServiceAccountFlow(_) => None,
AuthFlow::ApplicationDefaultCredentialsFlow(_) => None,
}
}
@@ -518,6 +575,9 @@ mod private {
AuthFlow::ServiceAccountFlow(service_account_flow) => {
service_account_flow.token(hyper_client, scopes).await
}
AuthFlow::ApplicationDefaultCredentialsFlow(service_account_flow) => {
service_account_flow.token(hyper_client, scopes).await
}
}
}
}

View File

@@ -72,6 +72,7 @@
#![deny(missing_docs)]
#![cfg_attr(yup_oauth2_docsrs, feature(doc_cfg))]
mod application_default_credentials;
pub mod authenticator;
pub mod authenticator_delegate;
mod device;
@@ -89,7 +90,8 @@ mod types;
#[doc(inline)]
pub use crate::authenticator::{
DeviceFlowAuthenticator, InstalledFlowAuthenticator, ServiceAccountAuthenticator,
ApplicationDefaultCredentialsAuthenticator, DeviceFlowAuthenticator,
InstalledFlowAuthenticator, ServiceAccountAuthenticator,
};
pub use crate::helper::*;

View File

@@ -2,8 +2,9 @@ use yup_oauth2::{
authenticator::{DefaultAuthenticator, DefaultHyperClient, HyperClientBuilder},
authenticator_delegate::{DeviceAuthResponse, DeviceFlowDelegate, InstalledFlowDelegate},
error::{AuthError, AuthErrorCode},
ApplicationSecret, DeviceFlowAuthenticator, Error, InstalledFlowAuthenticator,
InstalledFlowReturnMethod, ServiceAccountAuthenticator, ServiceAccountKey,
ApplicationDefaultCredentialsAuthenticator, ApplicationSecret, DeviceFlowAuthenticator, Error,
InstalledFlowAuthenticator, InstalledFlowReturnMethod, ServiceAccountAuthenticator,
ServiceAccountKey,
};
use std::future::Future;
@@ -596,3 +597,35 @@ async fn test_disk_storage() {
assert_eq!(token1.as_str(), "accesstoken");
assert_eq!(token1, token2);
}
#[tokio::test]
async fn test_default_application_credentials() {
let _ = env_logger::try_init();
let server = Server::run();
server.expect(
// TODO: this does not work.
Expectation::matching(all_of![
request::method_path("GET", "/token"),
request::query(url_decoded(all_of![contains((
"scopes",
"https://googleapis.com/some/scope"
))]))
])
.respond_with(json_encoded(serde_json::json!({
"access_token": "accesstoken",
"refresh_token": "refreshtoken",
"token_type": "Bearer",
"expires_in": 12345678,
}))),
);
let authenticator = ApplicationDefaultCredentialsAuthenticator::builder()
.await
.build()
.await
.unwrap();
let token = authenticator
.token(&["https://googleapis.com/some/scope"])
.await
.unwrap();
assert_ne!(token.as_str(), "accesstoken");
}