diff --git a/cpp/src/arrow/filesystem/azurefs.cc b/cpp/src/arrow/filesystem/azurefs.cc index f367bbdd3ed90..809aef32b3623 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(); + return Status::OK(); +} + Status AzureOptions::ConfigureWorkloadIdentityCredential() { credential_kind_ = CredentialKind::kWorkloadIdentity; token_credential_ = std::make_shared(); @@ -364,6 +376,7 @@ Result> AzureOptions::MakeBlobServiceC [[fallthrough]]; case CredentialKind::kClientSecret: case CredentialKind::kManagedIdentity: + case CredentialKind::kCLI: case CredentialKind::kWorkloadIdentity: case CredentialKind::kEnvironment: return std::make_unique(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( diff --git a/cpp/src/arrow/filesystem/azurefs.h b/cpp/src/arrow/filesystem/azurefs.h index 5d100bbcb4a8a..93d6ec2f945b4 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 6075cbf0c0c91..05ff3e551cd81 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(); }