This is an automated email from the ASF dual-hosted git repository.

tustvold pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/arrow-rs.git


The following commit(s) were added to refs/heads/master by this push:
     new 57cd0945db Allow opting out of request signing (#4927) (#4929)
57cd0945db is described below

commit 57cd0945db863059d30d31a890b692a6844038fd
Author: Raphael Taylor-Davies <[email protected]>
AuthorDate: Mon Oct 16 10:56:10 2023 +0100

    Allow opting out of request signing (#4927) (#4929)
---
 object_store/src/aws/client.rs     | 24 ++++++++++++---------
 object_store/src/aws/credential.rs | 21 +++++++++++-------
 object_store/src/aws/mod.rs        | 44 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 71 insertions(+), 18 deletions(-)

diff --git a/object_store/src/aws/client.rs b/object_store/src/aws/client.rs
index ac07f9ab9a..8a45a9f3ac 100644
--- a/object_store/src/aws/client.rs
+++ b/object_store/src/aws/client.rs
@@ -207,6 +207,7 @@ pub struct S3Config {
     pub retry_config: RetryConfig,
     pub client_options: ClientOptions,
     pub sign_payload: bool,
+    pub skip_signature: bool,
     pub checksum: Option<Checksum>,
     pub copy_if_not_exists: Option<S3CopyIfNotExists>,
 }
@@ -234,8 +235,11 @@ impl S3Client {
         &self.config
     }
 
-    async fn get_credential(&self) -> Result<Arc<AwsCredential>> {
-        self.config.credentials.get_credential().await
+    async fn get_credential(&self) -> Result<Option<Arc<AwsCredential>>> {
+        Ok(match self.config.skip_signature {
+            false => Some(self.config.credentials.get_credential().await?),
+            true => None,
+        })
     }
 
     /// Make an S3 PUT request 
<https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutObject.html>
@@ -271,7 +275,7 @@ impl S3Client {
         let response = builder
             .query(query)
             .with_aws_sigv4(
-                credential.as_ref(),
+                credential.as_deref(),
                 &self.config.region,
                 "s3",
                 self.config.sign_payload,
@@ -299,7 +303,7 @@ impl S3Client {
             .request(Method::DELETE, url)
             .query(query)
             .with_aws_sigv4(
-                credential.as_ref(),
+                credential.as_deref(),
                 &self.config.region,
                 "s3",
                 self.config.sign_payload,
@@ -390,7 +394,7 @@ impl S3Client {
             .header(CONTENT_TYPE, "application/xml")
             .body(body)
             .with_aws_sigv4(
-                credential.as_ref(),
+                credential.as_deref(),
                 &self.config.region,
                 "s3",
                 self.config.sign_payload,
@@ -459,7 +463,7 @@ impl S3Client {
 
         builder
             .with_aws_sigv4(
-                credential.as_ref(),
+                credential.as_deref(),
                 &self.config.region,
                 "s3",
                 self.config.sign_payload,
@@ -490,7 +494,7 @@ impl S3Client {
             .client
             .request(Method::POST, url)
             .with_aws_sigv4(
-                credential.as_ref(),
+                credential.as_deref(),
                 &self.config.region,
                 "s3",
                 self.config.sign_payload,
@@ -535,7 +539,7 @@ impl S3Client {
             .query(&[("uploadId", upload_id)])
             .body(body)
             .with_aws_sigv4(
-                credential.as_ref(),
+                credential.as_deref(),
                 &self.config.region,
                 "s3",
                 self.config.sign_payload,
@@ -567,7 +571,7 @@ impl GetClient for S3Client {
         let response = builder
             .with_get_options(options)
             .with_aws_sigv4(
-                credential.as_ref(),
+                credential.as_deref(),
                 &self.config.region,
                 "s3",
                 self.config.sign_payload,
@@ -621,7 +625,7 @@ impl ListClient for S3Client {
             .request(Method::GET, &url)
             .query(&query)
             .with_aws_sigv4(
-                credential.as_ref(),
+                credential.as_deref(),
                 &self.config.region,
                 "s3",
                 self.config.sign_payload,
diff --git a/object_store/src/aws/credential.rs 
b/object_store/src/aws/credential.rs
index e27b71f7c4..e0c5de5fe7 100644
--- a/object_store/src/aws/credential.rs
+++ b/object_store/src/aws/credential.rs
@@ -291,7 +291,7 @@ pub trait CredentialExt {
     /// Sign a request 
<https://docs.aws.amazon.com/general/latest/gr/sigv4_signing.html>
     fn with_aws_sigv4(
         self,
-        credential: &AwsCredential,
+        credential: Option<&AwsCredential>,
         region: &str,
         service: &str,
         sign_payload: bool,
@@ -302,20 +302,25 @@ pub trait CredentialExt {
 impl CredentialExt for RequestBuilder {
     fn with_aws_sigv4(
         self,
-        credential: &AwsCredential,
+        credential: Option<&AwsCredential>,
         region: &str,
         service: &str,
         sign_payload: bool,
         payload_sha256: Option<&[u8]>,
     ) -> Self {
-        let (client, request) = self.build_split();
-        let mut request = request.expect("request valid");
+        match credential {
+            Some(credential) => {
+                let (client, request) = self.build_split();
+                let mut request = request.expect("request valid");
 
-        AwsAuthorizer::new(credential, service, region)
-            .with_sign_payload(sign_payload)
-            .authorize(&mut request, payload_sha256);
+                AwsAuthorizer::new(credential, service, region)
+                    .with_sign_payload(sign_payload)
+                    .authorize(&mut request, payload_sha256);
 
-        Self::from_parts(client, request)
+                Self::from_parts(client, request)
+            }
+            None => self,
+        }
     }
 }
 
diff --git a/object_store/src/aws/mod.rs b/object_store/src/aws/mod.rs
index 285ee2f59d..70170a3cf4 100644
--- a/object_store/src/aws/mod.rs
+++ b/object_store/src/aws/mod.rs
@@ -448,6 +448,8 @@ pub struct AmazonS3Builder {
     client_options: ClientOptions,
     /// Credentials
     credentials: Option<AwsCredentialProvider>,
+    /// Skip signing requests
+    skip_signature: ConfigValue<bool>,
     /// Copy if not exists
     copy_if_not_exists: Option<ConfigValue<S3CopyIfNotExists>>,
 }
@@ -586,6 +588,9 @@ pub enum AmazonS3ConfigKey {
     /// See [`S3CopyIfNotExists`]
     CopyIfNotExists,
 
+    /// Skip signing request
+    SkipSignature,
+
     /// Client options
     Client(ClientConfigKey),
 }
@@ -608,6 +613,7 @@ impl AsRef<str> for AmazonS3ConfigKey {
             Self::ContainerCredentialsRelativeUri => {
                 "aws_container_credentials_relative_uri"
             }
+            Self::SkipSignature => "aws_skip_signature",
             Self::CopyIfNotExists => "copy_if_not_exists",
             Self::Client(opt) => opt.as_ref(),
         }
@@ -642,6 +648,7 @@ impl FromStr for AmazonS3ConfigKey {
             "aws_container_credentials_relative_uri" => {
                 Ok(Self::ContainerCredentialsRelativeUri)
             }
+            "aws_skip_signature" | "skip_signature" => Ok(Self::SkipSignature),
             "copy_if_not_exists" => Ok(Self::CopyIfNotExists),
             // Backwards compatibility
             "aws_allow_http" => Ok(Self::Client(ClientConfigKey::AllowHttp)),
@@ -753,6 +760,7 @@ impl AmazonS3Builder {
             AmazonS3ConfigKey::Client(key) => {
                 self.client_options = self.client_options.with_config(key, 
value)
             }
+            AmazonS3ConfigKey::SkipSignature => 
self.skip_signature.parse(value),
             AmazonS3ConfigKey::CopyIfNotExists => {
                 self.copy_if_not_exists = 
Some(ConfigValue::Deferred(value.into()))
             }
@@ -823,6 +831,7 @@ impl AmazonS3Builder {
             AmazonS3ConfigKey::ContainerCredentialsRelativeUri => {
                 self.container_credentials_relative_uri.clone()
             }
+            AmazonS3ConfigKey::SkipSignature => 
Some(self.skip_signature.to_string()),
             AmazonS3ConfigKey::CopyIfNotExists => {
                 self.copy_if_not_exists.as_ref().map(ToString::to_string)
             }
@@ -977,6 +986,14 @@ impl AmazonS3Builder {
         self
     }
 
+    /// If enabled, [`AmazonS3`] will not fetch credentials and will not sign 
requests
+    ///
+    /// This can be useful when interacting with public S3 buckets that deny 
authorized requests
+    pub fn with_skip_signature(mut self, skip_signature: bool) -> Self {
+        self.skip_signature = skip_signature.into();
+        self
+    }
+
     /// Sets the [checksum algorithm] which has to be used for object 
integrity check during upload.
     ///
     /// [checksum algorithm]: 
https://docs.aws.amazon.com/AmazonS3/latest/userguide/checking-object-integrity.html
@@ -1146,6 +1163,7 @@ impl AmazonS3Builder {
             retry_config: self.retry_config,
             client_options: self.client_options,
             sign_payload: !self.unsigned_payload.get()?,
+            skip_signature: self.skip_signature.get()?,
             checksum,
             copy_if_not_exists,
         };
@@ -1505,4 +1523,30 @@ mod s3_resolve_bucket_region_tests {
 
         assert!(result.is_err());
     }
+
+    #[tokio::test]
+    #[ignore = "Tests shouldn't call use remote services by default"]
+    async fn test_disable_creds() {
+        // https://registry.opendata.aws/daylight-osm/
+        let v1 = AmazonS3Builder::new()
+            .with_bucket_name("daylight-map-distribution")
+            .with_region("us-west-1")
+            .with_access_key_id("local")
+            .with_secret_access_key("development")
+            .build()
+            .unwrap();
+
+        let prefix = Path::from("release");
+
+        v1.list_with_delimiter(Some(&prefix)).await.unwrap_err();
+
+        let v2 = AmazonS3Builder::new()
+            .with_bucket_name("daylight-map-distribution")
+            .with_region("us-west-1")
+            .with_skip_signature(true)
+            .build()
+            .unwrap();
+
+        v2.list_with_delimiter(Some(&prefix)).await.unwrap();
+    }
 }

Reply via email to