Skip to content

Commit 04a7477

Browse files
authored
Storage/Storage Client Options Support Audiences (#4957) (#4966)
Storage Client Options Support Audiences.
1 parent f04b716 commit 04a7477

36 files changed

+579
-13
lines changed

sdk/storage/assets.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,5 @@
22
"AssetsRepo": "Azure/azure-sdk-assets",
33
"AssetsRepoPrefixPath": "cpp",
44
"TagPrefix": "cpp/storage",
5-
"Tag": "cpp/storage_a5249cec25"
5+
"Tag": "cpp/storage_e44851d82e"
66
}

sdk/storage/azure-storage-blobs/inc/azure/storage/blobs/blob_options.hpp

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include "azure/storage/blobs/rest_client.hpp"
77

88
#include <azure/core/internal/client_options.hpp>
9+
#include <azure/core/internal/extendable_enumeration.hpp>
910
#include <azure/core/match_conditions.hpp>
1011
#include <azure/core/modified_conditions.hpp>
1112
#include <azure/storage/common/access_conditions.hpp>
@@ -20,6 +21,35 @@
2021

2122
namespace Azure { namespace Storage { namespace Blobs {
2223

24+
namespace Models {
25+
26+
/**
27+
* @brief Audiences available for Blobs
28+
*
29+
*/
30+
class BlobAudience final : public Azure::Core::_internal::ExtendableEnumeration<BlobAudience> {
31+
public:
32+
/**
33+
* @brief Construct a new BlobAudience object
34+
*
35+
* @param blobAudience The Azure Active Directory audience to use when forming authorization
36+
* scopes. For the Language service, this value corresponds to a URL that identifies the Azure
37+
* cloud where the resource is located. For more information: See
38+
* https://learn.microsoft.com/en-us/azure/storage/blobs/authorize-access-azure-active-directory
39+
*/
40+
explicit BlobAudience(std::string blobAudience)
41+
: ExtendableEnumeration(std::move(blobAudience))
42+
{
43+
}
44+
45+
/**
46+
* @brief Default Audience. Use to acquire a token for authorizing requests to any Azure
47+
* Storage account.
48+
*/
49+
AZ_STORAGE_BLOBS_DLLEXPORT const static BlobAudience PublicAudience;
50+
};
51+
} // namespace Models
52+
2353
/**
2454
* @brief Specifies access conditions for a container.
2555
*/
@@ -165,6 +195,13 @@ namespace Azure { namespace Storage { namespace Blobs {
165195
* to prompt a challenge in order to discover the correct tenant for the resource.
166196
*/
167197
bool EnableTenantDiscovery = false;
198+
199+
/**
200+
* The Audience to use for authentication with Azure Active Directory (AAD).
201+
* #Azure::Storage::Blobs::Models::BlobAudience::PublicAudience will be assumed if Audience is
202+
* not set.
203+
*/
204+
Azure::Nullable<Models::BlobAudience> Audience;
168205
};
169206

170207
/**

sdk/storage/azure-storage-blobs/src/blob_client.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,9 @@ namespace Azure { namespace Storage { namespace Blobs {
8686
perRetryPolicies.emplace_back(std::make_unique<_internal::StoragePerRetryPolicy>());
8787
{
8888
Azure::Core::Credentials::TokenRequestContext tokenContext;
89-
tokenContext.Scopes.emplace_back(_internal::StorageScope);
89+
tokenContext.Scopes.emplace_back(
90+
options.Audience.HasValue() ? options.Audience.Value().ToString()
91+
: Models::BlobAudience::PublicAudience.ToString());
9092
perRetryPolicies.emplace_back(
9193
std::make_unique<_internal::StorageBearerTokenAuthenticationPolicy>(
9294
credential, tokenContext, options.EnableTenantDiscovery));

sdk/storage/azure-storage-blobs/src/blob_container_client.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,9 @@ namespace Azure { namespace Storage { namespace Blobs {
169169
std::unique_ptr<Azure::Core::Http::Policies::HttpPolicy> tokenAuthPolicy;
170170
{
171171
Azure::Core::Credentials::TokenRequestContext tokenContext;
172-
tokenContext.Scopes.emplace_back(_internal::StorageScope);
172+
tokenContext.Scopes.emplace_back(
173+
options.Audience.HasValue() ? options.Audience.Value().ToString()
174+
: Models::BlobAudience::PublicAudience.ToString());
173175
tokenAuthPolicy = std::make_unique<_internal::StorageBearerTokenAuthenticationPolicy>(
174176
credential, tokenContext, options.EnableTenantDiscovery);
175177
perRetryPolicies.emplace_back(tokenAuthPolicy->Clone());

sdk/storage/azure-storage-blobs/src/blob_options.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@
55

66
namespace Azure { namespace Storage { namespace Blobs {
77

8+
namespace Models {
9+
const BlobAudience BlobAudience::PublicAudience(Azure::Storage::_internal::StorageScope);
10+
} // namespace Models
11+
812
BlobQueryInputTextOptions BlobQueryInputTextOptions::CreateCsvTextOptions(
913
const std::string& recordSeparator,
1014
const std::string& columnSeparator,

sdk/storage/azure-storage-blobs/src/blob_service_client.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,9 @@ namespace Azure { namespace Storage { namespace Blobs {
8282
std::unique_ptr<Azure::Core::Http::Policies::HttpPolicy> tokenAuthPolicy;
8383
{
8484
Azure::Core::Credentials::TokenRequestContext tokenContext;
85-
tokenContext.Scopes.emplace_back(_internal::StorageScope);
85+
tokenContext.Scopes.emplace_back(
86+
options.Audience.HasValue() ? options.Audience.Value().ToString()
87+
: Models::BlobAudience::PublicAudience.ToString());
8688
tokenAuthPolicy = std::make_unique<_internal::StorageBearerTokenAuthenticationPolicy>(
8789
credential, tokenContext, options.EnableTenantDiscovery);
8890
perRetryPolicies.emplace_back(tokenAuthPolicy->Clone());

sdk/storage/azure-storage-blobs/test/ut/bearer_token_test.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,18 @@ namespace Azure { namespace Storage { namespace Test {
4949
clientOptions);
5050
EXPECT_NO_THROW(blobClient.GetProperties());
5151

52+
// With custom audience
53+
auto blobUrl = Azure::Core::Url(m_blockBlobClient->GetUrl());
54+
clientOptions.Audience = Blobs::Models::BlobAudience(
55+
blobUrl.GetScheme() + "://" + blobUrl.GetHost() + "/.default");
56+
blobClient = Blobs::BlobClient(
57+
m_blockBlobClient->GetUrl(),
58+
std::make_shared<Azure::Identity::ClientSecretCredential>(
59+
"", AadClientId(), AadClientSecret(), options),
60+
clientOptions);
61+
EXPECT_NO_THROW(blobClient.GetProperties());
62+
clientOptions.Audience.Reset();
63+
5264
// With error tenantId
5365
clientOptions.EnableTenantDiscovery = true;
5466
options.AdditionallyAllowedTenants = {"*"};

sdk/storage/azure-storage-blobs/test/ut/blob_container_client_test.cpp

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1439,4 +1439,33 @@ namespace Azure { namespace Storage { namespace Test {
14391439
EXPECT_FALSE(downloadResponse.Details.ObjectReplicationDestinationPolicyId.Value().empty());
14401440
}
14411441
}
1442+
1443+
TEST_F(BlobContainerClientTest, Audience)
1444+
{
1445+
auto credential = std::make_shared<Azure::Identity::ClientSecretCredential>(
1446+
AadTenantId(),
1447+
AadClientId(),
1448+
AadClientSecret(),
1449+
InitStorageClientOptions<Azure::Identity::ClientSecretCredentialOptions>());
1450+
auto clientOptions = InitStorageClientOptions<Blobs::BlobClientOptions>();
1451+
1452+
// default audience
1453+
auto containerClient
1454+
= Blobs::BlobContainerClient(m_blobContainerClient->GetUrl(), credential, clientOptions);
1455+
EXPECT_NO_THROW(containerClient.GetProperties());
1456+
1457+
// custom audience
1458+
auto containerUrl = Azure::Core::Url(containerClient.GetUrl());
1459+
clientOptions.Audience = Blobs::Models::BlobAudience(
1460+
containerUrl.GetScheme() + "://" + containerUrl.GetHost() + "/.default");
1461+
containerClient
1462+
= Blobs::BlobContainerClient(m_blobContainerClient->GetUrl(), credential, clientOptions);
1463+
EXPECT_NO_THROW(containerClient.GetProperties());
1464+
1465+
// error audience
1466+
clientOptions.Audience = Blobs::Models::BlobAudience("https://disk.compute.azure.com/.default");
1467+
containerClient
1468+
= Blobs::BlobContainerClient(m_blobContainerClient->GetUrl(), credential, clientOptions);
1469+
EXPECT_THROW(containerClient.GetProperties(), StorageException);
1470+
}
14421471
}}} // namespace Azure::Storage::Test

sdk/storage/azure-storage-blobs/test/ut/blob_service_client_test.cpp

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -497,4 +497,33 @@ namespace Azure { namespace Storage { namespace Test {
497497
auto destContainerClient2 = serviceClient.GetBlobContainerClient(destContainerName2);
498498
destContainerClient2.Delete();
499499
}
500+
501+
TEST_F(BlobServiceClientTest, Audience)
502+
{
503+
auto credential = std::make_shared<Azure::Identity::ClientSecretCredential>(
504+
AadTenantId(),
505+
AadClientId(),
506+
AadClientSecret(),
507+
InitStorageClientOptions<Azure::Identity::ClientSecretCredentialOptions>());
508+
auto clientOptions = InitStorageClientOptions<Blobs::BlobClientOptions>();
509+
510+
// default audience
511+
auto serviceClient
512+
= Blobs::BlobServiceClient(m_blobServiceClient->GetUrl(), credential, clientOptions);
513+
EXPECT_NO_THROW(serviceClient.GetProperties());
514+
515+
// custom audience
516+
auto serviceUrl = Azure::Core::Url(serviceClient.GetUrl());
517+
clientOptions.Audience = Blobs::Models::BlobAudience(
518+
serviceUrl.GetScheme() + "://" + serviceUrl.GetHost() + "/.default");
519+
serviceClient
520+
= Blobs::BlobServiceClient(m_blobServiceClient->GetUrl(), credential, clientOptions);
521+
EXPECT_NO_THROW(serviceClient.GetProperties());
522+
523+
// error audience
524+
clientOptions.Audience = Blobs::Models::BlobAudience("https://disk.compute.azure.com/.default");
525+
serviceClient
526+
= Blobs::BlobServiceClient(m_blobServiceClient->GetUrl(), credential, clientOptions);
527+
EXPECT_THROW(serviceClient.GetProperties(), StorageException);
528+
}
500529
}}} // namespace Azure::Storage::Test

sdk/storage/azure-storage-blobs/test/ut/block_blob_client_test.cpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2026,4 +2026,32 @@ namespace Azure { namespace Storage { namespace Test {
20262026
EXPECT_EQ(properties.CopyStatus.Value(), Blobs::Models::CopyStatus::Aborted);
20272027
}
20282028

2029+
TEST_F(BlockBlobClientTest, Audience)
2030+
{
2031+
auto credential = std::make_shared<Azure::Identity::ClientSecretCredential>(
2032+
AadTenantId(),
2033+
AadClientId(),
2034+
AadClientSecret(),
2035+
InitStorageClientOptions<Azure::Identity::ClientSecretCredentialOptions>());
2036+
auto clientOptions = InitStorageClientOptions<Blobs::BlobClientOptions>();
2037+
2038+
// default audience
2039+
auto blockBlobClient
2040+
= Blobs::BlockBlobClient(m_blockBlobClient->GetUrl(), credential, clientOptions);
2041+
EXPECT_NO_THROW(blockBlobClient.GetProperties());
2042+
2043+
// custom audience
2044+
auto blobUrl = Azure::Core::Url(blockBlobClient.GetUrl());
2045+
clientOptions.Audience = Blobs::Models::BlobAudience(
2046+
blobUrl.GetScheme() + "://" + blobUrl.GetHost() + "/.default");
2047+
blockBlobClient
2048+
= Blobs::BlockBlobClient(m_blockBlobClient->GetUrl(), credential, clientOptions);
2049+
EXPECT_NO_THROW(blockBlobClient.GetProperties());
2050+
2051+
// error audience
2052+
clientOptions.Audience = Blobs::Models::BlobAudience("https://disk.compute.azure.com/.default");
2053+
blockBlobClient
2054+
= Blobs::BlockBlobClient(m_blockBlobClient->GetUrl(), credential, clientOptions);
2055+
EXPECT_THROW(blockBlobClient.GetProperties(), StorageException);
2056+
}
20292057
}}} // namespace Azure::Storage::Test

sdk/storage/azure-storage-files-datalake/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ set(
6565
src/datalake_file_client.cpp
6666
src/datalake_file_system_client.cpp
6767
src/datalake_lease_client.cpp
68+
src/datalake_options.cpp
6869
src/datalake_path_client.cpp
6970
src/datalake_responses.cpp
7071
src/datalake_sas_builder.cpp

sdk/storage/azure-storage-files-datalake/inc/azure/storage/files/datalake/datalake_options.hpp

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,33 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake {
7878
*/
7979
static std::string SerializeAcls(const std::vector<Acl>& aclsArray);
8080
};
81+
82+
/**
83+
* @brief Audiences available for Blobs
84+
*
85+
*/
86+
class DataLakeAudience final
87+
: public Azure::Core::_internal::ExtendableEnumeration<DataLakeAudience> {
88+
public:
89+
/**
90+
* @brief Construct a new DataLakeAudience object
91+
*
92+
* @param dataLakeAudience The Azure Active Directory audience to use when forming
93+
* authorization scopes. For the Language service, this value corresponds to a URL that
94+
* identifies the Azure cloud where the resource is located. For more information: See
95+
* https://learn.microsoft.com/en-us/azure/storage/blobs/authorize-access-azure-active-directory
96+
*/
97+
explicit DataLakeAudience(std::string dataLakeAudience)
98+
: ExtendableEnumeration(std::move(dataLakeAudience))
99+
{
100+
}
101+
102+
/**
103+
* @brief Default Audience. Use to acquire a token for authorizing requests to any Azure
104+
* Storage account.
105+
*/
106+
AZ_STORAGE_FILES_DATALAKE_DLLEXPORT const static DataLakeAudience PublicAudience;
107+
};
81108
} // namespace Models
82109

83110
using DownloadFileToOptions = Blobs::DownloadBlobToOptions;
@@ -143,6 +170,13 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake {
143170
* to prompt a challenge in order to discover the correct tenant for the resource.
144171
*/
145172
bool EnableTenantDiscovery = false;
173+
174+
/**
175+
* The Audience to use for authentication with Azure Active Directory (AAD).
176+
* #Azure::Storage::Files::DataLake::Models::DataLakeAudience::PublicAudience will be assumed if
177+
* Audience is not set.
178+
*/
179+
Azure::Nullable<Models::DataLakeAudience> Audience;
146180
};
147181

148182
/**

sdk/storage/azure-storage-files-datalake/src/datalake_file_system_client.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,9 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake {
9797
perRetryPolicies.emplace_back(std::make_unique<_internal::StoragePerRetryPolicy>());
9898
{
9999
Azure::Core::Credentials::TokenRequestContext tokenContext;
100-
tokenContext.Scopes.emplace_back(_internal::StorageScope);
100+
tokenContext.Scopes.emplace_back(
101+
options.Audience.HasValue() ? options.Audience.Value().ToString()
102+
: Models::DataLakeAudience::PublicAudience.ToString());
101103
perRetryPolicies.emplace_back(
102104
std::make_unique<_internal::StorageBearerTokenAuthenticationPolicy>(
103105
credential, tokenContext, options.EnableTenantDiscovery));
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
#include "azure/storage/files/datalake/datalake_options.hpp"
5+
6+
namespace Azure { namespace Storage { namespace Files { namespace DataLake { namespace Models {
7+
8+
const DataLakeAudience DataLakeAudience::PublicAudience(Azure::Storage::_internal::StorageScope);
9+
10+
}}}}} // namespace Azure::Storage::Files::DataLake::Models

sdk/storage/azure-storage-files-datalake/src/datalake_path_client.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,9 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake {
9595
perRetryPolicies.emplace_back(std::make_unique<_internal::StoragePerRetryPolicy>());
9696
{
9797
Azure::Core::Credentials::TokenRequestContext tokenContext;
98-
tokenContext.Scopes.emplace_back(_internal::StorageScope);
98+
tokenContext.Scopes.emplace_back(
99+
options.Audience.HasValue() ? options.Audience.Value().ToString()
100+
: Models::DataLakeAudience::PublicAudience.ToString());
99101
perRetryPolicies.emplace_back(
100102
std::make_unique<_internal::StorageBearerTokenAuthenticationPolicy>(
101103
credential, tokenContext, options.EnableTenantDiscovery));

sdk/storage/azure-storage-files-datalake/src/datalake_service_client.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,9 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake {
9191
perRetryPolicies.emplace_back(std::make_unique<_internal::StoragePerRetryPolicy>());
9292
{
9393
Azure::Core::Credentials::TokenRequestContext tokenContext;
94-
tokenContext.Scopes.emplace_back(_internal::StorageScope);
94+
tokenContext.Scopes.emplace_back(
95+
options.Audience.HasValue() ? options.Audience.Value().ToString()
96+
: Models::DataLakeAudience::PublicAudience.ToString());
9597
perRetryPolicies.emplace_back(
9698
std::make_unique<_internal::StorageBearerTokenAuthenticationPolicy>(
9799
credential, tokenContext, options.EnableTenantDiscovery));

sdk/storage/azure-storage-files-datalake/src/datalake_utilities.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,10 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake { nam
9898
blobOptions.ApiVersion = options.ApiVersion;
9999
blobOptions.CustomerProvidedKey = options.CustomerProvidedKey;
100100
blobOptions.EnableTenantDiscovery = options.EnableTenantDiscovery;
101+
if (options.Audience.HasValue())
102+
{
103+
blobOptions.Audience = Blobs::Models::BlobAudience(options.Audience.Value().ToString());
104+
}
101105
return blobOptions;
102106
}
103107

sdk/storage/azure-storage-files-datalake/test/ut/datalake_file_system_client_test.cpp

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -907,4 +907,33 @@ namespace Azure { namespace Storage { namespace Test {
907907
}
908908
}
909909

910+
TEST_F(DataLakeFileSystemClientTest, Audience)
911+
{
912+
auto credential = std::make_shared<Azure::Identity::ClientSecretCredential>(
913+
AadTenantId(),
914+
AadClientId(),
915+
AadClientSecret(),
916+
InitStorageClientOptions<Azure::Identity::ClientSecretCredentialOptions>());
917+
auto clientOptions = InitStorageClientOptions<Files::DataLake::DataLakeClientOptions>();
918+
919+
// default audience
920+
auto fileSystemClient = Files::DataLake::DataLakeFileSystemClient(
921+
m_fileSystemClient->GetUrl(), credential, clientOptions);
922+
EXPECT_NO_THROW(fileSystemClient.GetProperties());
923+
924+
// custom audience
925+
auto fileSystemUrl = Azure::Core::Url(fileSystemClient.GetUrl());
926+
clientOptions.Audience = Files::DataLake::Models::DataLakeAudience(
927+
fileSystemUrl.GetScheme() + "://" + fileSystemUrl.GetHost() + "/.default");
928+
fileSystemClient = Files::DataLake::DataLakeFileSystemClient(
929+
m_fileSystemClient->GetUrl(), credential, clientOptions);
930+
EXPECT_NO_THROW(fileSystemClient.GetProperties());
931+
932+
// error audience
933+
clientOptions.Audience
934+
= Files::DataLake::Models::DataLakeAudience("https://disk.compute.azure.com/.default");
935+
fileSystemClient = Files::DataLake::DataLakeFileSystemClient(
936+
m_fileSystemClient->GetUrl(), credential, clientOptions);
937+
EXPECT_THROW(fileSystemClient.GetProperties(), StorageException);
938+
}
910939
}}} // namespace Azure::Storage::Test

0 commit comments

Comments
 (0)