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

alamb 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 25d39c13fc feat: further TLS options on ClientOptions: #5034 (#6148)
25d39c13fc is described below

commit 25d39c13fc8937e86373821093d063202484ac00
Author: ByteBaker <[email protected]>
AuthorDate: Tue Aug 20 00:43:09 2024 +0530

    feat: further TLS options on ClientOptions: #5034 (#6148)
    
    * feat: further TLS options on ClientOptions: #5034
    
    * Rename to Certificate and with_root_certificate, add docs
    
    ---------
    
    Co-authored-by: Andrew Lamb <[email protected]>
---
 object_store/src/client/mod.rs | 64 ++++++++++++++++++++++++++++++++++++++++++
 object_store/src/lib.rs        |  4 +--
 2 files changed, 66 insertions(+), 2 deletions(-)

diff --git a/object_store/src/client/mod.rs b/object_store/src/client/mod.rs
index 43fd65892c..c45833b89d 100644
--- a/object_store/src/client/mod.rs
+++ b/object_store/src/client/mod.rs
@@ -167,10 +167,60 @@ impl FromStr for ClientConfigKey {
     }
 }
 
+/// Represents a CA certificate provided by the user.
+///
+/// This is used to configure the client to trust a specific certificate. See
+/// [Self::from_pem] for an example
+#[derive(Debug, Clone)]
+pub struct Certificate(reqwest::tls::Certificate);
+
+impl Certificate {
+    /// Create a `Certificate` from a PEM encoded certificate.
+    ///
+    /// # Example from a PEM file
+    ///
+    /// ```no_run
+    /// # use object_store::Certificate;
+    /// # use std::fs::File;
+    /// # use std::io::Read;
+    /// let mut buf = Vec::new();
+    /// File::open("my_cert.pem").unwrap()
+    ///   .read_to_end(&mut buf).unwrap();
+    /// let cert = Certificate::from_pem(&buf).unwrap();
+    ///
+    /// ```
+    pub fn from_pem(pem: &[u8]) -> Result<Self> {
+        Ok(Self(
+            
reqwest::tls::Certificate::from_pem(pem).map_err(map_client_error)?,
+        ))
+    }
+
+    /// Create a collection of `Certificate` from a PEM encoded certificate
+    /// bundle.
+    ///
+    /// Files that contain such collections have extensions such as `.crt`,
+    /// `.cer` and `.pem` files.
+    pub fn from_pem_bundle(pem_bundle: &[u8]) -> Result<Vec<Self>> {
+        Ok(reqwest::tls::Certificate::from_pem_bundle(pem_bundle)
+            .map_err(map_client_error)?
+            .into_iter()
+            .map(Self)
+            .collect())
+    }
+
+    /// Create a `Certificate` from a binary DER encoded certificate.
+    pub fn from_der(der: &[u8]) -> Result<Self> {
+        Ok(Self(
+            
reqwest::tls::Certificate::from_der(der).map_err(map_client_error)?,
+        ))
+    }
+}
+
 /// HTTP client configuration for remote object stores
 #[derive(Debug, Clone)]
 pub struct ClientOptions {
     user_agent: Option<ConfigValue<HeaderValue>>,
+    root_certificates: Vec<Certificate>,
     content_type_map: HashMap<String, String>,
     default_content_type: Option<String>,
     default_headers: Option<HeaderMap>,
@@ -201,6 +251,7 @@ impl Default for ClientOptions {
         // we opt for a slightly higher default timeout of 30 seconds
         Self {
             user_agent: None,
+            root_certificates: Default::default(),
             content_type_map: Default::default(),
             default_content_type: None,
             default_headers: None,
@@ -310,6 +361,15 @@ impl ClientOptions {
         self
     }
 
+    /// Add a custom root certificate.
+    ///
+    /// This can be used to connect to a server that has a self-signed
+    /// certificate for example.
+    pub fn with_root_certificate(mut self, certificate: Certificate) -> Self {
+        self.root_certificates.push(certificate);
+        self
+    }
+
     /// Set the default CONTENT_TYPE for uploads
     pub fn with_default_content_type(mut self, mime: impl Into<String>) -> 
Self {
         self.default_content_type = Some(mime.into());
@@ -541,6 +601,10 @@ impl ClientOptions {
             builder = builder.proxy(proxy);
         }
 
+        for certificate in &self.root_certificates {
+            builder = builder.add_root_certificate(certificate.0.clone());
+        }
+
         if let Some(timeout) = &self.timeout {
             builder = builder.timeout(timeout.get()?)
         }
diff --git a/object_store/src/lib.rs b/object_store/src/lib.rs
index 4184d58a0a..4b43f0cdeb 100644
--- a/object_store/src/lib.rs
+++ b/object_store/src/lib.rs
@@ -526,8 +526,8 @@ mod client;
 
 #[cfg(feature = "cloud")]
 pub use client::{
-    backoff::BackoffConfig, retry::RetryConfig, ClientConfigKey, 
ClientOptions, CredentialProvider,
-    StaticCredentialProvider,
+    backoff::BackoffConfig, retry::RetryConfig, Certificate, ClientConfigKey, 
ClientOptions,
+    CredentialProvider, StaticCredentialProvider,
 };
 
 #[cfg(feature = "cloud")]

Reply via email to