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

kou pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/arrow.git


The following commit(s) were added to refs/heads/main by this push:
     new f0a6f49016 GH-39344: [C++][FS][Azure] Support azure cli auth (#41976)
f0a6f49016 is described below

commit f0a6f490161e50411242cd026d7adf9303f5f109
Author: Thomas Newton <[email protected]>
AuthorDate: Mon Jun 10 07:40:58 2024 +0100

    GH-39344: [C++][FS][Azure] Support azure cli auth (#41976)
    
    
    
    ### Rationale for this change
    Maybe be useful to support explicit environment credential (currently 
environment credential can be used as part of the Azure default credential 
flow).
    
    ### What changes are included in this PR?
    Create `ConfigureCLICredential`. Add it to FromUri
    
    ### Are these changes tested?
    There are new unittests but no integration tests that we can actually 
authenticate successfully. We are relying on the Azure C++ SDK to abstracting 
that away.
    
    ### Are there any user-facing changes?
    Explicit CLI auth is now supported
    
    * GitHub Issue: #39344
    
    Authored-by: Thomas Newton <[email protected]>
    Signed-off-by: Sutou Kouhei <[email protected]>
---
 cpp/src/arrow/filesystem/azurefs.cc      | 14 ++++++++++++++
 cpp/src/arrow/filesystem/azurefs.h       | 11 +++++++----
 cpp/src/arrow/filesystem/azurefs_test.cc | 17 +++++++++++++++++
 3 files changed, 38 insertions(+), 4 deletions(-)

diff --git a/cpp/src/arrow/filesystem/azurefs.cc 
b/cpp/src/arrow/filesystem/azurefs.cc
index f367bbdd3e..809aef32b3 100644
--- a/cpp/src/arrow/filesystem/azurefs.cc
+++ b/cpp/src/arrow/filesystem/azurefs.cc
@@ -117,6 +117,8 @@ Status AzureOptions::ExtractFromUriQuery(const Uri& uri) {
         credential_kind = CredentialKind::kDefault;
       } else if (kv.second == "anonymous") {
         credential_kind = CredentialKind::kAnonymous;
+      } else if (kv.second == "cli") {
+        credential_kind = CredentialKind::kCLI;
       } else if (kv.second == "workload_identity") {
         credential_kind = CredentialKind::kWorkloadIdentity;
       } else if (kv.second == "environment") {
@@ -170,6 +172,9 @@ Status AzureOptions::ExtractFromUriQuery(const Uri& uri) {
       case CredentialKind::kAnonymous:
         RETURN_NOT_OK(ConfigureAnonymousCredential());
         break;
+      case CredentialKind::kCLI:
+        RETURN_NOT_OK(ConfigureCLICredential());
+        break;
       case CredentialKind::kWorkloadIdentity:
         RETURN_NOT_OK(ConfigureWorkloadIdentityCredential());
         break;
@@ -255,6 +260,7 @@ bool AzureOptions::Equals(const AzureOptions& other) const {
       return storage_shared_key_credential_->AccountName ==
              other.storage_shared_key_credential_->AccountName;
     case CredentialKind::kClientSecret:
+    case CredentialKind::kCLI:
     case CredentialKind::kManagedIdentity:
     case CredentialKind::kWorkloadIdentity:
     case CredentialKind::kEnvironment:
@@ -337,6 +343,12 @@ Status 
AzureOptions::ConfigureManagedIdentityCredential(const std::string& clien
   return Status::OK();
 }
 
+Status AzureOptions::ConfigureCLICredential() {
+  credential_kind_ = CredentialKind::kCLI;
+  token_credential_ = std::make_shared<Azure::Identity::AzureCliCredential>();
+  return Status::OK();
+}
+
 Status AzureOptions::ConfigureWorkloadIdentityCredential() {
   credential_kind_ = CredentialKind::kWorkloadIdentity;
   token_credential_ = 
std::make_shared<Azure::Identity::WorkloadIdentityCredential>();
@@ -364,6 +376,7 @@ Result<std::unique_ptr<Blobs::BlobServiceClient>> 
AzureOptions::MakeBlobServiceC
       [[fallthrough]];
     case CredentialKind::kClientSecret:
     case CredentialKind::kManagedIdentity:
+    case CredentialKind::kCLI:
     case CredentialKind::kWorkloadIdentity:
     case CredentialKind::kEnvironment:
       return 
std::make_unique<Blobs::BlobServiceClient>(AccountBlobUrl(account_name),
@@ -391,6 +404,7 @@ AzureOptions::MakeDataLakeServiceClient() const {
       [[fallthrough]];
     case CredentialKind::kClientSecret:
     case CredentialKind::kManagedIdentity:
+    case CredentialKind::kCLI:
     case CredentialKind::kWorkloadIdentity:
     case CredentialKind::kEnvironment:
       return std::make_unique<DataLake::DataLakeServiceClient>(
diff --git a/cpp/src/arrow/filesystem/azurefs.h 
b/cpp/src/arrow/filesystem/azurefs.h
index 5d100bbcb4..93d6ec2f94 100644
--- a/cpp/src/arrow/filesystem/azurefs.h
+++ b/cpp/src/arrow/filesystem/azurefs.h
@@ -119,6 +119,7 @@ struct ARROW_EXPORT AzureOptions {
     kStorageSharedKey,
     kClientSecret,
     kManagedIdentity,
+    kCLI,
     kWorkloadIdentity,
     kEnvironment,
   } credential_kind_ = CredentialKind::kDefault;
@@ -160,14 +161,15 @@ struct ARROW_EXPORT AzureOptions {
   /// * blob_storage_authority: Set AzureOptions::blob_storage_authority
   /// * dfs_storage_authority: Set AzureOptions::dfs_storage_authority
   /// * enable_tls: If it's "false" or "0", HTTP not HTTPS is used.
-  /// * credential_kind: One of "default", "anonymous",
-  ///   "workload_identity" or "environment". If "default" is specified, it's
+  /// * credential_kind: One of "default", "anonymous", "workload_identity",
+  ///   "environment" or "cli". If "default" is specified, it's
   ///   just ignored.  If "anonymous" is specified,
   ///   AzureOptions::ConfigureAnonymousCredential() is called. If
   ///   "workload_identity" is specified,
-  ///   AzureOptions::ConfigureWorkloadIdentityCredential() is called, If
+  ///   AzureOptions::ConfigureWorkloadIdentityCredential() is called. If
   ///   "environment" is specified,
-  ///   AzureOptions::ConfigureEnvironmentCredential() is called.
+  ///   AzureOptions::ConfigureEnvironmentCredential() is called. If "cli" is
+  ///   specified, AzureOptions::ConfigureCLICredential() is called.
   /// * tenant_id: You must specify "client_id" and "client_secret"
   ///   too. AzureOptions::ConfigureClientSecretCredential() is called.
   /// * client_id: If you don't specify "tenant_id" and
@@ -190,6 +192,7 @@ struct ARROW_EXPORT AzureOptions {
                                          const std::string& client_id,
                                          const std::string& client_secret);
   Status ConfigureManagedIdentityCredential(const std::string& client_id = 
std::string());
+  Status ConfigureCLICredential();
   Status ConfigureWorkloadIdentityCredential();
   Status ConfigureEnvironmentCredential();
 
diff --git a/cpp/src/arrow/filesystem/azurefs_test.cc 
b/cpp/src/arrow/filesystem/azurefs_test.cc
index 6075cbf0c0..05ff3e551c 100644
--- a/cpp/src/arrow/filesystem/azurefs_test.cc
+++ b/cpp/src/arrow/filesystem/azurefs_test.cc
@@ -521,6 +521,13 @@ TEST(AzureFileSystem, 
InitializeWithManagedIdentityCredential) {
   EXPECT_OK_AND_ASSIGN(fs, AzureFileSystem::Make(options));
 }
 
+TEST(AzureFileSystem, InitializeWithCLICredential) {
+  AzureOptions options;
+  options.account_name = "dummy-account-name";
+  ARROW_EXPECT_OK(options.ConfigureCLICredential());
+  EXPECT_OK_AND_ASSIGN(auto fs, AzureFileSystem::Make(options));
+}
+
 TEST(AzureFileSystem, InitializeWithWorkloadIdentityCredential) {
   AzureOptions options;
   options.account_name = "dummy-account-name";
@@ -667,6 +674,15 @@ class TestAzureOptions : public ::testing::Test {
     ASSERT_EQ(options.credential_kind_, 
AzureOptions::CredentialKind::kManagedIdentity);
   }
 
+  void TestFromUriCredentialCLI() {
+    ASSERT_OK_AND_ASSIGN(
+        auto options,
+        
AzureOptions::FromUri("abfs://account.blob.core.windows.net/container/dir/blob?"
+                              "credential_kind=cli",
+                              nullptr));
+    ASSERT_EQ(options.credential_kind_, AzureOptions::CredentialKind::kCLI);
+  }
+
   void TestFromUriCredentialWorkloadIdentity() {
     ASSERT_OK_AND_ASSIGN(
         auto options,
@@ -733,6 +749,7 @@ TEST_F(TestAzureOptions, FromUriCredentialClientSecret) {
 TEST_F(TestAzureOptions, FromUriCredentialManagedIdentity) {
   TestFromUriCredentialManagedIdentity();
 }
+TEST_F(TestAzureOptions, FromUriCredentialCLI) { TestFromUriCredentialCLI(); }
 TEST_F(TestAzureOptions, FromUriCredentialWorkloadIdentity) {
   TestFromUriCredentialWorkloadIdentity();
 }

Reply via email to