Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 61 additions & 1 deletion src/gcp/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,8 @@ pub struct GoogleCloudStorageBuilder {
client_options: ClientOptions,
/// Credentials
credentials: Option<GcpCredentialProvider>,
/// Explicit bearer token, if configured
bearer_token: Option<String>,
/// Skip signing requests
skip_signature: ConfigValue<bool>,
/// Credentials for sign url
Expand Down Expand Up @@ -179,6 +181,15 @@ pub enum GoogleConfigKey {
/// - `application_credentials`
ApplicationCredentials,

/// Explicit OAuth bearer token
///
/// This is treated as a static token and will not be refreshed automatically.
///
/// Supported keys:
/// - `google_bearer_token`
/// - `bearer_token`
BearerToken,

/// Skip signing request
///
/// Supported keys:
Expand All @@ -198,6 +209,7 @@ impl AsRef<str> for GoogleConfigKey {
Self::Bucket => "google_bucket",
Self::BaseUrl => "google_base_url",
Self::ApplicationCredentials => "google_application_credentials",
Self::BearerToken => "google_bearer_token",
Self::SkipSignature => "google_skip_signature",
Self::Client(key) => key.as_ref(),
}
Expand All @@ -219,6 +231,7 @@ impl FromStr for GoogleConfigKey {
"google_application_credentials" | "application_credentials" => {
Ok(Self::ApplicationCredentials)
}
"google_bearer_token" | "bearer_token" => Ok(Self::BearerToken),
"google_skip_signature" | "skip_signature" => Ok(Self::SkipSignature),
_ => match s.strip_prefix("google_").unwrap_or(s).parse() {
Ok(key) => Ok(Self::Client(key)),
Expand All @@ -240,6 +253,7 @@ impl Default for GoogleCloudStorageBuilder {
url: None,
base_url: None,
credentials: None,
bearer_token: None,
skip_signature: Default::default(),
signing_credentials: None,
http_connector: None,
Expand Down Expand Up @@ -322,6 +336,7 @@ impl GoogleCloudStorageBuilder {
GoogleConfigKey::ApplicationCredentials => {
self.application_credentials_path = Some(value.into())
}
GoogleConfigKey::BearerToken => self = self.with_bearer_token(value),
GoogleConfigKey::SkipSignature => self.skip_signature.parse(value),
GoogleConfigKey::Client(key) => {
self.client_options = self.client_options.with_config(key, value)
Expand All @@ -348,6 +363,7 @@ impl GoogleCloudStorageBuilder {
GoogleConfigKey::Bucket => self.bucket_name.clone(),
GoogleConfigKey::BaseUrl => self.base_url.clone(),
GoogleConfigKey::ApplicationCredentials => self.application_credentials_path.clone(),
GoogleConfigKey::BearerToken => self.bearer_token.clone(),
GoogleConfigKey::SkipSignature => Some(self.skip_signature.to_string()),
GoogleConfigKey::Client(key) => self.client_options.get_config_value(key),
}
Expand Down Expand Up @@ -445,6 +461,18 @@ impl GoogleCloudStorageBuilder {
self
}

/// Set an explicit OAuth bearer token.
///
/// This is treated as a static token and will not be refreshed automatically.
pub fn with_bearer_token(mut self, bearer_token: impl Into<String>) -> Self {
let bearer_token = bearer_token.into();
self.credentials = Some(Arc::new(StaticCredentialProvider::new(GcpCredential {
bearer: bearer_token.clone(),
})));
self.bearer_token = Some(bearer_token);
self
}

/// If enabled, [`GoogleCloudStorage`] will not fetch credentials and will not sign requests.
///
/// This can be useful when interacting with public GCS buckets that deny authorized requests.
Expand All @@ -456,6 +484,7 @@ impl GoogleCloudStorageBuilder {
/// Set the credential provider overriding any other options
pub fn with_credentials(mut self, credentials: GcpCredentialProvider) -> Self {
self.credentials = Some(credentials);
self.bearer_token = None;
self
}

Expand Down Expand Up @@ -641,6 +670,7 @@ impl GoogleCloudStorageBuilder {
#[cfg(test)]
mod tests {
use super::*;
use futures_executor::block_on;
use std::collections::HashMap;
use std::io::Write;
use tempfile::NamedTempFile;
Expand Down Expand Up @@ -714,6 +744,17 @@ mod tests {
GoogleCloudStorageBuilder::new().with_config(alias.parse().unwrap(), "fake_bucket");
assert_eq!("fake_bucket", builder.bucket_name.unwrap());
}

for alias in ["google_bearer_token", "bearer_token"] {
let gcs = GoogleCloudStorageBuilder::new()
.with_config(alias.parse().unwrap(), "test-token")
.with_bucket_name("fake_bucket")
.build()
.unwrap();

let credential = block_on(gcs.credentials().get_credential()).unwrap();
assert_eq!(credential.bearer, "test-token");
}
}

#[tokio::test]
Expand Down Expand Up @@ -805,9 +846,11 @@ mod tests {
fn gcs_test_config_get_value() {
let google_service_account = "object_store:fake_service_account".to_string();
let google_bucket_name = "object_store:fake_bucket".to_string();
let google_bearer_token = "test-token".to_string();
let builder = GoogleCloudStorageBuilder::new()
.with_config(GoogleConfigKey::ServiceAccount, &google_service_account)
.with_config(GoogleConfigKey::Bucket, &google_bucket_name);
.with_config(GoogleConfigKey::Bucket, &google_bucket_name)
.with_config(GoogleConfigKey::BearerToken, &google_bearer_token);

assert_eq!(
builder
Expand All @@ -819,6 +862,23 @@ mod tests {
builder.get_config_value(&GoogleConfigKey::Bucket).unwrap(),
google_bucket_name
);
assert_eq!(
builder.get_config_value(&GoogleConfigKey::BearerToken).unwrap(),
google_bearer_token
);
}

#[test]
fn gcs_test_with_credentials_clears_bearer_config_value() {
let custom_creds = Arc::new(StaticCredentialProvider::new(GcpCredential {
bearer: "custom-token".to_string(),
}));

let builder = GoogleCloudStorageBuilder::new()
.with_bearer_token("test-token")
.with_credentials(custom_creds);

assert_eq!(builder.get_config_value(&GoogleConfigKey::BearerToken), None);
}

#[test]
Expand Down
18 changes: 18 additions & 0 deletions src/parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,7 @@ where
#[cfg(test)]
mod tests {
use super::*;
use url::Url;

#[test]
fn test_parse() {
Expand Down Expand Up @@ -422,6 +423,23 @@ mod tests {
assert_eq!(path.as_ref(), "my file with spaces");
}

#[test]
#[cfg(feature = "gcp")]
fn test_url_gcs_bearer_token_opts() {
let url = Url::parse("gs://bucket/path").unwrap();

for alias in ["google_bearer_token", "bearer_token"] {
let opts = [
(alias, "test-token"),
("google_proxy_url", "https://example.com"),
];

let (store, path) = parse_url_opts(&url, opts).unwrap();
assert_eq!(path.as_ref(), "path");
assert_eq!(store.to_string(), "GoogleCloudStorage(bucket)");
}
}

#[tokio::test]
#[cfg(all(feature = "http", not(target_arch = "wasm32")))]
async fn test_url_http() {
Expand Down