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 7eb588d7a Cleanup ApplicationDefaultCredentials (#3799)
7eb588d7a is described below

commit 7eb588d7a9cee6516d53be6228130eda24810d37
Author: Raphael Taylor-Davies <[email protected]>
AuthorDate: Sun Mar 5 13:10:12 2023 +0000

    Cleanup ApplicationDefaultCredentials (#3799)
    
    * Cleanup ApplicationDefaultCredentials
    
    * Fix doc
---
 object_store/src/gcp/credential.rs | 84 ++++++++++++++++++++++++++------------
 1 file changed, 59 insertions(+), 25 deletions(-)

diff --git a/object_store/src/gcp/credential.rs 
b/object_store/src/gcp/credential.rs
index 853e4ce83..a8dce7132 100644
--- a/object_store/src/gcp/credential.rs
+++ b/object_store/src/gcp/credential.rs
@@ -62,8 +62,8 @@ pub enum Error {
     #[snafu(display("Error getting token response body: {}", source))]
     TokenResponseBody { source: reqwest::Error },
 
-    #[snafu(display("A configuration file was passed in but was not used."))]
-    UnusedConfigurationFile,
+    #[snafu(display("Unsupported ApplicationCredentials type: {}", type_))]
+    UnsupportedCredentialsType { type_: String },
 
     #[snafu(display("Error creating client: {}", source))]
     Client { source: crate::Error },
@@ -399,36 +399,60 @@ impl TokenProvider for InstanceCredentialProvider {
     }
 }
 
+/// ApplicationDefaultCredentials
+/// <https://google.aip.dev/auth/4110>
+#[derive(Debug)]
+pub enum ApplicationDefaultCredentials {
+    /// <https://google.aip.dev/auth/4113>
+    AuthorizedUser {
+        client_id: String,
+        client_secret: String,
+        refresh_token: String,
+    },
+}
+
+impl ApplicationDefaultCredentials {
+    pub fn new(path: Option<&str>) -> Result<Option<Self>, Error> {
+        let file = match ApplicationDefaultCredentialsFile::read(path)? {
+            Some(f) => f,
+            None => return Ok(None),
+        };
+
+        Ok(Some(match file.type_.as_str() {
+            "authorized_user" => Self::AuthorizedUser {
+                client_id: file.client_id,
+                client_secret: file.client_secret,
+                refresh_token: file.refresh_token,
+            },
+            type_ => return UnsupportedCredentialsTypeSnafu { type_ }.fail(),
+        }))
+    }
+}
+
 /// A deserialized `application_default_credentials.json`-file.
 /// 
<https://cloud.google.com/docs/authentication/application-default-credentials#personal>
-#[derive(serde::Deserialize, Debug)]
-pub struct ApplicationDefaultCredentials {
+#[derive(serde::Deserialize)]
+struct ApplicationDefaultCredentialsFile {
+    #[serde(default)]
     client_id: String,
+    #[serde(default)]
     client_secret: String,
+    #[serde(default)]
     refresh_token: String,
     #[serde(rename = "type")]
     type_: String,
 }
 
-impl ApplicationDefaultCredentials {
-    const DEFAULT_TOKEN_GCP_URI: &'static str =
-        "https://accounts.google.com/o/oauth2/token";;
+impl ApplicationDefaultCredentialsFile {
     const CREDENTIALS_PATH: &'static str =
         ".config/gcloud/application_default_credentials.json";
-    const EXPECTED_TYPE: &str = "authorized_user";
 
     // Create a new application default credential in the following situations:
     //  1. a file is passed in and the type matches.
     //  2. without argument if the well-known configuration file is present.
-    pub fn new(path: Option<&str>) -> Result<Option<Self>, Error> {
+    fn read(path: Option<&str>) -> Result<Option<Self>, Error> {
         if let Some(path) = path {
-            if let Ok(credentials) = read_credentials_file::<Self>(path) {
-                if credentials.type_ == Self::EXPECTED_TYPE {
-                    return Ok(Some(credentials));
-                }
-            }
-            // Return an error if the path has not been used.
-            return Err(Error::UnusedConfigurationFile);
+            return read_credentials_file::<Self>(path).map(Some);
         }
         if let Some(home) = env::var_os("HOME") {
             let path = Path::new(&home).join(Self::CREDENTIALS_PATH);
@@ -442,6 +466,8 @@ impl ApplicationDefaultCredentials {
     }
 }
 
+const DEFAULT_TOKEN_GCP_URI: &str = 
"https://accounts.google.com/o/oauth2/token";;
+
 #[async_trait]
 impl TokenProvider for ApplicationDefaultCredentials {
     async fn fetch_token(
@@ -449,16 +475,24 @@ impl TokenProvider for ApplicationDefaultCredentials {
         client: &Client,
         retry: &RetryConfig,
     ) -> Result<TemporaryToken<String>, Error> {
-        let body = [
-            ("grant_type", "refresh_token"),
-            ("client_id", &self.client_id),
-            ("client_secret", &self.client_secret),
-            ("refresh_token", &self.refresh_token),
-        ];
+        let builder = client.request(Method::POST, DEFAULT_TOKEN_GCP_URI);
+        let builder = match self {
+            Self::AuthorizedUser {
+                client_id,
+                client_secret,
+                refresh_token,
+            } => {
+                let body = [
+                    ("grant_type", "refresh_token"),
+                    ("client_id", client_id),
+                    ("client_secret", client_secret),
+                    ("refresh_token", refresh_token),
+                ];
+                builder.form(&body)
+            }
+        };
 
-        let response = client
-            .request(Method::POST, Self::DEFAULT_TOKEN_GCP_URI)
-            .form(&body)
+        let response = builder
             .send_retry(retry)
             .await
             .context(TokenRequestSnafu)?

Reply via email to