Skip to content

Commit

Permalink
feat: Add GCS support for rattler auth (#605)
Browse files Browse the repository at this point in the history
  • Loading branch information
clement-chaneching authored Apr 25, 2024
1 parent 926a876 commit a4e3bd2
Show file tree
Hide file tree
Showing 5 changed files with 76 additions and 6 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ futures-util = "0.3.30"
fxhash = "0.2.1"
getrandom = { version = "0.2.14", default-features = false }
glob = "0.3.1"
google-cloud-auth = { version = "0.13.2", default-features = false}
hex = "0.4.3"
hex-literal = "0.4.1"
http = "1.1"
Expand Down
6 changes: 4 additions & 2 deletions crates/rattler_networking/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@ readme.workspace = true

[features]
default = ["native-tls"]
native-tls = ['reqwest/native-tls']
rustls-tls = ['reqwest/rustls-tls']
native-tls = ['reqwest/native-tls', "google-cloud-auth/default-tls"]
rustls-tls = ['reqwest/rustls-tls', "google-cloud-auth/rustls-tls"]
gcs = ["google-cloud-auth"]

[dependencies]
anyhow = { workspace = true }
Expand All @@ -34,6 +35,7 @@ serde_json = { workspace = true }
thiserror = { workspace = true }
tracing = { workspace = true }
url = { workspace = true }
google-cloud-auth = { workspace = true, default-features = false, optional = true }

[target.'cfg( target_arch = "wasm32" )'.dependencies]
getrandom = { workspace = true, features = ["js"] }
Expand Down
6 changes: 2 additions & 4 deletions crates/rattler_networking/src/authentication_middleware.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
//! `reqwest` middleware that authenticates requests with data from the `AuthenticationStorage`

use crate::{Authentication, AuthenticationStorage};
use async_trait::async_trait;
use base64::prelude::BASE64_STANDARD;
Expand All @@ -9,7 +8,6 @@ use reqwest_middleware::{Middleware, Next};
use std::path::{Path, PathBuf};
use std::sync::OnceLock;
use url::Url;

/// `reqwest` middleware to authenticate requests
#[derive(Clone, Default)]
pub struct AuthenticationMiddleware {
Expand Down Expand Up @@ -37,7 +35,7 @@ impl Middleware for AuthenticationMiddleware {
let mut req = req;
*req.url_mut() = url;

let req = Self::authenticate_request(req, &auth)?;
let req = Self::authenticate_request(req, &auth).await?;
next.run(req, extensions).await
}
}
Expand Down Expand Up @@ -73,7 +71,7 @@ impl AuthenticationMiddleware {
}

/// Authenticate the given request with the given authentication information
fn authenticate_request(
async fn authenticate_request(
mut req: reqwest::Request,
auth: &Option<Authentication>,
) -> reqwest_middleware::Result<reqwest::Request> {
Expand Down
63 changes: 63 additions & 0 deletions crates/rattler_networking/src/gcs_middleware.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
//! Middleware to handle `gcs://` URLs to pull artifacts from an GCS
use async_trait::async_trait;
use google_cloud_auth::project::{create_token_source, Config};
use reqwest::{Request, Response};
use reqwest_middleware::{Middleware, Next, Result as MiddlewareResult};
use url::Url;

/// GCS middleware to authenticate requests
pub struct GCSMiddleware;

#[async_trait]
impl Middleware for GCSMiddleware {
/// Create a new authentication middleware for GCS
async fn handle(
&self,
mut req: Request,
extensions: &mut http::Extensions,
next: Next<'_>,
) -> MiddlewareResult<Response> {
if req.url().scheme() == "gcs" {
let mut url = req.url().clone();
let bucket_name = url.host_str().expect("Host should be present in GCS URL");
let new_url = format!(
"https://storage.googleapis.com/{}{}",
bucket_name,
url.path()
);
url = Url::parse(&new_url).expect("Failed to parse URL");
*req.url_mut() = url;
req = authenticate_with_google_cloud(req).await?;
}
next.run(req, extensions).await
}
}

/// Auth to GCS
async fn authenticate_with_google_cloud(mut req: Request) -> MiddlewareResult<Request> {
let audience = "https://storage.googleapis.com/";
let scopes = [
"https://www.googleapis.com/auth/cloud-platform",
"https://www.googleapis.com/auth/devstorage.read_only",
];
match create_token_source(Config {
audience: Some(audience),
scopes: Some(&scopes),
sub: None,
})
.await
{
Ok(ts) => match ts.token().await {
Ok(token) => {
let bearer_auth = format!("Bearer {}", token.access_token);
let header_value = reqwest::header::HeaderValue::from_str(&bearer_auth)
.map_err(reqwest_middleware::Error::middleware)?;
req.headers_mut()
.insert(reqwest::header::AUTHORIZATION, header_value);
Ok(req)
}
Err(e) => Err(reqwest_middleware::Error::Middleware(anyhow::Error::new(e))),
},
Err(e) => Err(reqwest_middleware::Error::Middleware(anyhow::Error::new(e))),
}
}
6 changes: 6 additions & 0 deletions crates/rattler_networking/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,14 @@ pub use authentication_storage::{authentication::Authentication, storage::Authen
pub use mirror_middleware::MirrorMiddleware;
pub use oci_middleware::OciMiddleware;

#[cfg(feature = "gcs")]
pub mod gcs_middleware;
#[cfg(feature = "gcs")]
pub use gcs_middleware::GCSMiddleware;

pub mod authentication_middleware;
pub mod authentication_storage;

pub mod mirror_middleware;
pub mod oci_middleware;
pub mod retry_policies;
Expand Down

0 comments on commit a4e3bd2

Please sign in to comment.