Storage/Storage Client Options Support Audiences (#4957) (#4966)

Storage Client Options Support Audiences.
This commit is contained in:
microzchang 2023-09-19 03:28:42 +00:00 committed by GitHub
parent f04b7164eb
commit 04a7477d2e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
36 changed files with 579 additions and 13 deletions

View File

@ -2,5 +2,5 @@
"AssetsRepo": "Azure/azure-sdk-assets",
"AssetsRepoPrefixPath": "cpp",
"TagPrefix": "cpp/storage",
"Tag": "cpp/storage_a5249cec25"
"Tag": "cpp/storage_e44851d82e"
}

View File

@ -6,6 +6,7 @@
#include "azure/storage/blobs/rest_client.hpp"
#include <azure/core/internal/client_options.hpp>
#include <azure/core/internal/extendable_enumeration.hpp>
#include <azure/core/match_conditions.hpp>
#include <azure/core/modified_conditions.hpp>
#include <azure/storage/common/access_conditions.hpp>
@ -20,6 +21,35 @@
namespace Azure { namespace Storage { namespace Blobs {
namespace Models {
/**
* @brief Audiences available for Blobs
*
*/
class BlobAudience final : public Azure::Core::_internal::ExtendableEnumeration<BlobAudience> {
public:
/**
* @brief Construct a new BlobAudience object
*
* @param blobAudience The Azure Active Directory audience to use when forming authorization
* scopes. For the Language service, this value corresponds to a URL that identifies the Azure
* cloud where the resource is located. For more information: See
* https://learn.microsoft.com/en-us/azure/storage/blobs/authorize-access-azure-active-directory
*/
explicit BlobAudience(std::string blobAudience)
: ExtendableEnumeration(std::move(blobAudience))
{
}
/**
* @brief Default Audience. Use to acquire a token for authorizing requests to any Azure
* Storage account.
*/
AZ_STORAGE_BLOBS_DLLEXPORT const static BlobAudience PublicAudience;
};
} // namespace Models
/**
* @brief Specifies access conditions for a container.
*/
@ -165,6 +195,13 @@ namespace Azure { namespace Storage { namespace Blobs {
* to prompt a challenge in order to discover the correct tenant for the resource.
*/
bool EnableTenantDiscovery = false;
/**
* The Audience to use for authentication with Azure Active Directory (AAD).
* #Azure::Storage::Blobs::Models::BlobAudience::PublicAudience will be assumed if Audience is
* not set.
*/
Azure::Nullable<Models::BlobAudience> Audience;
};
/**

View File

@ -86,7 +86,9 @@ namespace Azure { namespace Storage { namespace Blobs {
perRetryPolicies.emplace_back(std::make_unique<_internal::StoragePerRetryPolicy>());
{
Azure::Core::Credentials::TokenRequestContext tokenContext;
tokenContext.Scopes.emplace_back(_internal::StorageScope);
tokenContext.Scopes.emplace_back(
options.Audience.HasValue() ? options.Audience.Value().ToString()
: Models::BlobAudience::PublicAudience.ToString());
perRetryPolicies.emplace_back(
std::make_unique<_internal::StorageBearerTokenAuthenticationPolicy>(
credential, tokenContext, options.EnableTenantDiscovery));

View File

@ -169,7 +169,9 @@ namespace Azure { namespace Storage { namespace Blobs {
std::unique_ptr<Azure::Core::Http::Policies::HttpPolicy> tokenAuthPolicy;
{
Azure::Core::Credentials::TokenRequestContext tokenContext;
tokenContext.Scopes.emplace_back(_internal::StorageScope);
tokenContext.Scopes.emplace_back(
options.Audience.HasValue() ? options.Audience.Value().ToString()
: Models::BlobAudience::PublicAudience.ToString());
tokenAuthPolicy = std::make_unique<_internal::StorageBearerTokenAuthenticationPolicy>(
credential, tokenContext, options.EnableTenantDiscovery);
perRetryPolicies.emplace_back(tokenAuthPolicy->Clone());

View File

@ -5,6 +5,10 @@
namespace Azure { namespace Storage { namespace Blobs {
namespace Models {
const BlobAudience BlobAudience::PublicAudience(Azure::Storage::_internal::StorageScope);
} // namespace Models
BlobQueryInputTextOptions BlobQueryInputTextOptions::CreateCsvTextOptions(
const std::string& recordSeparator,
const std::string& columnSeparator,

View File

@ -82,7 +82,9 @@ namespace Azure { namespace Storage { namespace Blobs {
std::unique_ptr<Azure::Core::Http::Policies::HttpPolicy> tokenAuthPolicy;
{
Azure::Core::Credentials::TokenRequestContext tokenContext;
tokenContext.Scopes.emplace_back(_internal::StorageScope);
tokenContext.Scopes.emplace_back(
options.Audience.HasValue() ? options.Audience.Value().ToString()
: Models::BlobAudience::PublicAudience.ToString());
tokenAuthPolicy = std::make_unique<_internal::StorageBearerTokenAuthenticationPolicy>(
credential, tokenContext, options.EnableTenantDiscovery);
perRetryPolicies.emplace_back(tokenAuthPolicy->Clone());

View File

@ -49,6 +49,18 @@ namespace Azure { namespace Storage { namespace Test {
clientOptions);
EXPECT_NO_THROW(blobClient.GetProperties());
// With custom audience
auto blobUrl = Azure::Core::Url(m_blockBlobClient->GetUrl());
clientOptions.Audience = Blobs::Models::BlobAudience(
blobUrl.GetScheme() + "://" + blobUrl.GetHost() + "/.default");
blobClient = Blobs::BlobClient(
m_blockBlobClient->GetUrl(),
std::make_shared<Azure::Identity::ClientSecretCredential>(
"", AadClientId(), AadClientSecret(), options),
clientOptions);
EXPECT_NO_THROW(blobClient.GetProperties());
clientOptions.Audience.Reset();
// With error tenantId
clientOptions.EnableTenantDiscovery = true;
options.AdditionallyAllowedTenants = {"*"};

View File

@ -1439,4 +1439,33 @@ namespace Azure { namespace Storage { namespace Test {
EXPECT_FALSE(downloadResponse.Details.ObjectReplicationDestinationPolicyId.Value().empty());
}
}
TEST_F(BlobContainerClientTest, Audience)
{
auto credential = std::make_shared<Azure::Identity::ClientSecretCredential>(
AadTenantId(),
AadClientId(),
AadClientSecret(),
InitStorageClientOptions<Azure::Identity::ClientSecretCredentialOptions>());
auto clientOptions = InitStorageClientOptions<Blobs::BlobClientOptions>();
// default audience
auto containerClient
= Blobs::BlobContainerClient(m_blobContainerClient->GetUrl(), credential, clientOptions);
EXPECT_NO_THROW(containerClient.GetProperties());
// custom audience
auto containerUrl = Azure::Core::Url(containerClient.GetUrl());
clientOptions.Audience = Blobs::Models::BlobAudience(
containerUrl.GetScheme() + "://" + containerUrl.GetHost() + "/.default");
containerClient
= Blobs::BlobContainerClient(m_blobContainerClient->GetUrl(), credential, clientOptions);
EXPECT_NO_THROW(containerClient.GetProperties());
// error audience
clientOptions.Audience = Blobs::Models::BlobAudience("https://disk.compute.azure.com/.default");
containerClient
= Blobs::BlobContainerClient(m_blobContainerClient->GetUrl(), credential, clientOptions);
EXPECT_THROW(containerClient.GetProperties(), StorageException);
}
}}} // namespace Azure::Storage::Test

View File

@ -497,4 +497,33 @@ namespace Azure { namespace Storage { namespace Test {
auto destContainerClient2 = serviceClient.GetBlobContainerClient(destContainerName2);
destContainerClient2.Delete();
}
TEST_F(BlobServiceClientTest, Audience)
{
auto credential = std::make_shared<Azure::Identity::ClientSecretCredential>(
AadTenantId(),
AadClientId(),
AadClientSecret(),
InitStorageClientOptions<Azure::Identity::ClientSecretCredentialOptions>());
auto clientOptions = InitStorageClientOptions<Blobs::BlobClientOptions>();
// default audience
auto serviceClient
= Blobs::BlobServiceClient(m_blobServiceClient->GetUrl(), credential, clientOptions);
EXPECT_NO_THROW(serviceClient.GetProperties());
// custom audience
auto serviceUrl = Azure::Core::Url(serviceClient.GetUrl());
clientOptions.Audience = Blobs::Models::BlobAudience(
serviceUrl.GetScheme() + "://" + serviceUrl.GetHost() + "/.default");
serviceClient
= Blobs::BlobServiceClient(m_blobServiceClient->GetUrl(), credential, clientOptions);
EXPECT_NO_THROW(serviceClient.GetProperties());
// error audience
clientOptions.Audience = Blobs::Models::BlobAudience("https://disk.compute.azure.com/.default");
serviceClient
= Blobs::BlobServiceClient(m_blobServiceClient->GetUrl(), credential, clientOptions);
EXPECT_THROW(serviceClient.GetProperties(), StorageException);
}
}}} // namespace Azure::Storage::Test

View File

@ -2026,4 +2026,32 @@ namespace Azure { namespace Storage { namespace Test {
EXPECT_EQ(properties.CopyStatus.Value(), Blobs::Models::CopyStatus::Aborted);
}
TEST_F(BlockBlobClientTest, Audience)
{
auto credential = std::make_shared<Azure::Identity::ClientSecretCredential>(
AadTenantId(),
AadClientId(),
AadClientSecret(),
InitStorageClientOptions<Azure::Identity::ClientSecretCredentialOptions>());
auto clientOptions = InitStorageClientOptions<Blobs::BlobClientOptions>();
// default audience
auto blockBlobClient
= Blobs::BlockBlobClient(m_blockBlobClient->GetUrl(), credential, clientOptions);
EXPECT_NO_THROW(blockBlobClient.GetProperties());
// custom audience
auto blobUrl = Azure::Core::Url(blockBlobClient.GetUrl());
clientOptions.Audience = Blobs::Models::BlobAudience(
blobUrl.GetScheme() + "://" + blobUrl.GetHost() + "/.default");
blockBlobClient
= Blobs::BlockBlobClient(m_blockBlobClient->GetUrl(), credential, clientOptions);
EXPECT_NO_THROW(blockBlobClient.GetProperties());
// error audience
clientOptions.Audience = Blobs::Models::BlobAudience("https://disk.compute.azure.com/.default");
blockBlobClient
= Blobs::BlockBlobClient(m_blockBlobClient->GetUrl(), credential, clientOptions);
EXPECT_THROW(blockBlobClient.GetProperties(), StorageException);
}
}}} // namespace Azure::Storage::Test

View File

@ -65,6 +65,7 @@ set(
src/datalake_file_client.cpp
src/datalake_file_system_client.cpp
src/datalake_lease_client.cpp
src/datalake_options.cpp
src/datalake_path_client.cpp
src/datalake_responses.cpp
src/datalake_sas_builder.cpp

View File

@ -78,6 +78,33 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake {
*/
static std::string SerializeAcls(const std::vector<Acl>& aclsArray);
};
/**
* @brief Audiences available for Blobs
*
*/
class DataLakeAudience final
: public Azure::Core::_internal::ExtendableEnumeration<DataLakeAudience> {
public:
/**
* @brief Construct a new DataLakeAudience object
*
* @param dataLakeAudience The Azure Active Directory audience to use when forming
* authorization scopes. For the Language service, this value corresponds to a URL that
* identifies the Azure cloud where the resource is located. For more information: See
* https://learn.microsoft.com/en-us/azure/storage/blobs/authorize-access-azure-active-directory
*/
explicit DataLakeAudience(std::string dataLakeAudience)
: ExtendableEnumeration(std::move(dataLakeAudience))
{
}
/**
* @brief Default Audience. Use to acquire a token for authorizing requests to any Azure
* Storage account.
*/
AZ_STORAGE_FILES_DATALAKE_DLLEXPORT const static DataLakeAudience PublicAudience;
};
} // namespace Models
using DownloadFileToOptions = Blobs::DownloadBlobToOptions;
@ -143,6 +170,13 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake {
* to prompt a challenge in order to discover the correct tenant for the resource.
*/
bool EnableTenantDiscovery = false;
/**
* The Audience to use for authentication with Azure Active Directory (AAD).
* #Azure::Storage::Files::DataLake::Models::DataLakeAudience::PublicAudience will be assumed if
* Audience is not set.
*/
Azure::Nullable<Models::DataLakeAudience> Audience;
};
/**

View File

@ -97,7 +97,9 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake {
perRetryPolicies.emplace_back(std::make_unique<_internal::StoragePerRetryPolicy>());
{
Azure::Core::Credentials::TokenRequestContext tokenContext;
tokenContext.Scopes.emplace_back(_internal::StorageScope);
tokenContext.Scopes.emplace_back(
options.Audience.HasValue() ? options.Audience.Value().ToString()
: Models::DataLakeAudience::PublicAudience.ToString());
perRetryPolicies.emplace_back(
std::make_unique<_internal::StorageBearerTokenAuthenticationPolicy>(
credential, tokenContext, options.EnableTenantDiscovery));

View File

@ -0,0 +1,10 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
#include "azure/storage/files/datalake/datalake_options.hpp"
namespace Azure { namespace Storage { namespace Files { namespace DataLake { namespace Models {
const DataLakeAudience DataLakeAudience::PublicAudience(Azure::Storage::_internal::StorageScope);
}}}}} // namespace Azure::Storage::Files::DataLake::Models

View File

@ -95,7 +95,9 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake {
perRetryPolicies.emplace_back(std::make_unique<_internal::StoragePerRetryPolicy>());
{
Azure::Core::Credentials::TokenRequestContext tokenContext;
tokenContext.Scopes.emplace_back(_internal::StorageScope);
tokenContext.Scopes.emplace_back(
options.Audience.HasValue() ? options.Audience.Value().ToString()
: Models::DataLakeAudience::PublicAudience.ToString());
perRetryPolicies.emplace_back(
std::make_unique<_internal::StorageBearerTokenAuthenticationPolicy>(
credential, tokenContext, options.EnableTenantDiscovery));

View File

@ -91,7 +91,9 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake {
perRetryPolicies.emplace_back(std::make_unique<_internal::StoragePerRetryPolicy>());
{
Azure::Core::Credentials::TokenRequestContext tokenContext;
tokenContext.Scopes.emplace_back(_internal::StorageScope);
tokenContext.Scopes.emplace_back(
options.Audience.HasValue() ? options.Audience.Value().ToString()
: Models::DataLakeAudience::PublicAudience.ToString());
perRetryPolicies.emplace_back(
std::make_unique<_internal::StorageBearerTokenAuthenticationPolicy>(
credential, tokenContext, options.EnableTenantDiscovery));

View File

@ -98,6 +98,10 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake { nam
blobOptions.ApiVersion = options.ApiVersion;
blobOptions.CustomerProvidedKey = options.CustomerProvidedKey;
blobOptions.EnableTenantDiscovery = options.EnableTenantDiscovery;
if (options.Audience.HasValue())
{
blobOptions.Audience = Blobs::Models::BlobAudience(options.Audience.Value().ToString());
}
return blobOptions;
}

View File

@ -907,4 +907,33 @@ namespace Azure { namespace Storage { namespace Test {
}
}
TEST_F(DataLakeFileSystemClientTest, Audience)
{
auto credential = std::make_shared<Azure::Identity::ClientSecretCredential>(
AadTenantId(),
AadClientId(),
AadClientSecret(),
InitStorageClientOptions<Azure::Identity::ClientSecretCredentialOptions>());
auto clientOptions = InitStorageClientOptions<Files::DataLake::DataLakeClientOptions>();
// default audience
auto fileSystemClient = Files::DataLake::DataLakeFileSystemClient(
m_fileSystemClient->GetUrl(), credential, clientOptions);
EXPECT_NO_THROW(fileSystemClient.GetProperties());
// custom audience
auto fileSystemUrl = Azure::Core::Url(fileSystemClient.GetUrl());
clientOptions.Audience = Files::DataLake::Models::DataLakeAudience(
fileSystemUrl.GetScheme() + "://" + fileSystemUrl.GetHost() + "/.default");
fileSystemClient = Files::DataLake::DataLakeFileSystemClient(
m_fileSystemClient->GetUrl(), credential, clientOptions);
EXPECT_NO_THROW(fileSystemClient.GetProperties());
// error audience
clientOptions.Audience
= Files::DataLake::Models::DataLakeAudience("https://disk.compute.azure.com/.default");
fileSystemClient = Files::DataLake::DataLakeFileSystemClient(
m_fileSystemClient->GetUrl(), credential, clientOptions);
EXPECT_THROW(fileSystemClient.GetProperties(), StorageException);
}
}}} // namespace Azure::Storage::Test

View File

@ -479,4 +479,34 @@ namespace Azure { namespace Storage { namespace Test {
ASSERT_TRUE(properties.Group.HasValue());
ASSERT_TRUE(properties.Permissions.HasValue());
}
TEST_F(DataLakePathClientTest, Audience)
{
auto credential = std::make_shared<Azure::Identity::ClientSecretCredential>(
AadTenantId(),
AadClientId(),
AadClientSecret(),
InitStorageClientOptions<Azure::Identity::ClientSecretCredentialOptions>());
auto clientOptions = InitStorageClientOptions<Files::DataLake::DataLakeClientOptions>();
// default audience
auto pathClient
= Files::DataLake::DataLakePathClient(m_pathClient->GetUrl(), credential, clientOptions);
EXPECT_NO_THROW(pathClient.GetProperties());
// custom audience
auto pathUrl = Azure::Core::Url(pathClient.GetUrl());
clientOptions.Audience = Files::DataLake::Models::DataLakeAudience(
pathUrl.GetScheme() + "://" + pathUrl.GetHost() + "/.default");
pathClient
= Files::DataLake::DataLakePathClient(m_pathClient->GetUrl(), credential, clientOptions);
EXPECT_NO_THROW(pathClient.GetProperties());
// error audience
clientOptions.Audience
= Files::DataLake::Models::DataLakeAudience("https://disk.compute.azure.com/.default");
pathClient
= Files::DataLake::DataLakePathClient(m_pathClient->GetUrl(), credential, clientOptions);
EXPECT_THROW(pathClient.GetProperties(), StorageException);
}
}}} // namespace Azure::Storage::Test

View File

@ -343,4 +343,33 @@ namespace Azure { namespace Storage { namespace Test {
auto res = m_dataLakeServiceClient->SetProperties(originalProperties);
}
TEST_F(DataLakeServiceClientTest, Audience)
{
auto credential = std::make_shared<Azure::Identity::ClientSecretCredential>(
AadTenantId(),
AadClientId(),
AadClientSecret(),
InitStorageClientOptions<Azure::Identity::ClientSecretCredentialOptions>());
auto clientOptions = InitStorageClientOptions<Files::DataLake::DataLakeClientOptions>();
// default audience
auto serviceClient = Files::DataLake::DataLakeServiceClient(
m_dataLakeServiceClient->GetUrl(), credential, clientOptions);
EXPECT_NO_THROW(serviceClient.GetProperties());
// custom audience
auto fileSystemUrl = Azure::Core::Url(serviceClient.GetUrl());
clientOptions.Audience = Files::DataLake::Models::DataLakeAudience(
fileSystemUrl.GetScheme() + "://" + fileSystemUrl.GetHost() + "/.default");
serviceClient = Files::DataLake::DataLakeServiceClient(
m_dataLakeServiceClient->GetUrl(), credential, clientOptions);
EXPECT_NO_THROW(serviceClient.GetProperties());
// error audience
clientOptions.Audience
= Files::DataLake::Models::DataLakeAudience("https://disk.compute.azure.com/.default");
serviceClient = Files::DataLake::DataLakeServiceClient(
m_dataLakeServiceClient->GetUrl(), credential, clientOptions);
EXPECT_THROW(serviceClient.GetProperties(), StorageException);
}
}}} // namespace Azure::Storage::Test

View File

@ -64,6 +64,7 @@ set(
src/share_directory_client.cpp
src/share_file_client.cpp
src/share_lease_client.cpp
src/share_options.cpp
src/share_responses.cpp
src/share_sas_builder.cpp
src/share_service_client.cpp

View File

@ -6,6 +6,7 @@
#include "azure/storage/files/shares/rest_client.hpp"
#include <azure/core/internal/client_options.hpp>
#include <azure/core/internal/extendable_enumeration.hpp>
#include <azure/core/nullable.hpp>
#include <azure/storage/common/access_conditions.hpp>
@ -17,6 +18,36 @@
namespace Azure { namespace Storage { namespace Files { namespace Shares {
namespace Models {
/**
* @brief Audiences available for Blobs
*
*/
class ShareAudience final
: public Azure::Core::_internal::ExtendableEnumeration<ShareAudience> {
public:
/**
* @brief Construct a new ShareAudience object
*
* @param shareAudience The Azure Active Directory audience to use when forming authorization
* scopes. For the Language service, this value corresponds to a URL that identifies the Azure
* cloud where the resource is located. For more information: See
* https://learn.microsoft.com/en-us/azure/storage/blobs/authorize-access-azure-active-directory
*/
explicit ShareAudience(std::string shareAudience)
: ExtendableEnumeration(std::move(shareAudience))
{
}
/**
* @brief Default Audience. Use to acquire a token for authorizing requests to any Azure
* Storage account.
*/
AZ_STORAGE_FILES_SHARES_DLLEXPORT const static ShareAudience PublicAudience;
};
} // namespace Models
/**
* @brief Client options used to initialize share clients.
*/
@ -46,6 +77,13 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares {
* request. This is currently required when using token authentication.
*/
Nullable<Models::ShareTokenIntent> ShareTokenIntent;
/**
* The Audience to use for authentication with Azure Active Directory (AAD).
* #Azure::Storage::Files::Shares::Models::ShareAudience::PublicAudience will be assumed if
* Audience is not set.
*/
Azure::Nullable<Models::ShareAudience> Audience;
};
/**

View File

@ -78,7 +78,9 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares {
perRetryPolicies.emplace_back(std::make_unique<_internal::StoragePerRetryPolicy>());
{
Azure::Core::Credentials::TokenRequestContext tokenContext;
tokenContext.Scopes.emplace_back(_internal::StorageScope);
tokenContext.Scopes.emplace_back(
options.Audience.HasValue() ? options.Audience.Value().ToString()
: Models::ShareAudience::PublicAudience.ToString());
perRetryPolicies.emplace_back(
std::make_unique<Azure::Core::Http::Policies::_internal::BearerTokenAuthenticationPolicy>(
credential, tokenContext));

View File

@ -80,7 +80,9 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares {
perRetryPolicies.emplace_back(std::make_unique<_internal::StoragePerRetryPolicy>());
{
Azure::Core::Credentials::TokenRequestContext tokenContext;
tokenContext.Scopes.emplace_back(_internal::StorageScope);
tokenContext.Scopes.emplace_back(
options.Audience.HasValue() ? options.Audience.Value().ToString()
: Models::ShareAudience::PublicAudience.ToString());
perRetryPolicies.emplace_back(
std::make_unique<Azure::Core::Http::Policies::_internal::BearerTokenAuthenticationPolicy>(
credential, tokenContext));

View File

@ -85,7 +85,9 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares {
perRetryPolicies.emplace_back(std::make_unique<_internal::StoragePerRetryPolicy>());
{
Azure::Core::Credentials::TokenRequestContext tokenContext;
tokenContext.Scopes.emplace_back(_internal::StorageScope);
tokenContext.Scopes.emplace_back(
options.Audience.HasValue() ? options.Audience.Value().ToString()
: Models::ShareAudience::PublicAudience.ToString());
perRetryPolicies.emplace_back(
std::make_unique<Azure::Core::Http::Policies::_internal::BearerTokenAuthenticationPolicy>(
credential, tokenContext));

View File

@ -0,0 +1,10 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
#include "azure/storage/files/shares/share_options.hpp"
namespace Azure { namespace Storage { namespace Files { namespace Shares { namespace Models {
const ShareAudience ShareAudience::PublicAudience(Azure::Storage::_internal::StorageScope);
}}}}} // namespace Azure::Storage::Files::Shares::Models

View File

@ -75,7 +75,9 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares {
perRetryPolicies.emplace_back(std::make_unique<_internal::StoragePerRetryPolicy>());
{
Azure::Core::Credentials::TokenRequestContext tokenContext;
tokenContext.Scopes.emplace_back(_internal::StorageScope);
tokenContext.Scopes.emplace_back(
options.Audience.HasValue() ? options.Audience.Value().ToString()
: Models::ShareAudience::PublicAudience.ToString());
perRetryPolicies.emplace_back(
std::make_unique<Azure::Core::Http::Policies::_internal::BearerTokenAuthenticationPolicy>(
credential, tokenContext));

View File

@ -696,4 +696,38 @@ namespace Azure { namespace Storage { namespace Test {
EXPECT_TRUE(client1.GetUrl().find("snapshot=" + timestamp1) == std::string::npos);
EXPECT_TRUE(client1.GetUrl().find("snapshot=" + timestamp2) == std::string::npos);
}
TEST_F(FileShareClientTest, Audience_PLAYBACKONLY_)
{
auto credential = std::make_shared<Azure::Identity::ClientSecretCredential>(
AadTenantId(),
AadClientId(),
AadClientSecret(),
InitStorageClientOptions<Azure::Identity::ClientSecretCredentialOptions>());
auto clientOptions = InitStorageClientOptions<Files::Shares::ShareClientOptions>();
clientOptions.ShareTokenIntent = Files::Shares::Models::ShareTokenIntent::Backup;
std::string permission = "O:S-1-5-21-2127521184-1604012920-1887927527-21560751G:S-1-5-21-"
"2127521184-1604012920-1887927527-513D:AI(A;;FA;;;SY)(A;;FA;;;BA)(A;;"
"0x1200a9;;;S-1-5-21-397955417-626881126-188441444-3053964)";
// default audience
auto shareClient
= Files::Shares::ShareClient(m_shareClient->GetUrl(), credential, clientOptions);
Files::Shares::Models::CreateSharePermissionResult created;
EXPECT_NO_THROW(created = shareClient.CreatePermission(permission).Value);
EXPECT_NO_THROW(shareClient.GetPermission(created.FilePermissionKey));
// custom audience
auto shareUrl = Azure::Core::Url(shareClient.GetUrl());
clientOptions.Audience = Files::Shares::Models::ShareAudience(
shareUrl.GetScheme() + "://" + shareUrl.GetHost() + "/.default");
shareClient = Files::Shares::ShareClient(m_shareClient->GetUrl(), credential, clientOptions);
EXPECT_NO_THROW(shareClient.GetPermission(created.FilePermissionKey));
// error audience
clientOptions.Audience
= Files::Shares::Models::ShareAudience("https://disk.compute.azure.com/.default");
shareClient = Files::Shares::ShareClient(m_shareClient->GetUrl(), credential, clientOptions);
EXPECT_THROW(shareClient.GetPermission(created.FilePermissionKey), StorageException);
}
}}} // namespace Azure::Storage::Test

View File

@ -1205,4 +1205,35 @@ namespace Azure { namespace Storage { namespace Test {
EXPECT_TRUE(client1.GetUrl().find("snapshot=" + timestamp1) == std::string::npos);
EXPECT_TRUE(client1.GetUrl().find("snapshot=" + timestamp2) == std::string::npos);
}
TEST_F(FileShareDirectoryClientTest, Audience_PLAYBACKONLY_)
{
auto credential = std::make_shared<Azure::Identity::ClientSecretCredential>(
AadTenantId(),
AadClientId(),
AadClientSecret(),
InitStorageClientOptions<Azure::Identity::ClientSecretCredentialOptions>());
auto clientOptions = InitStorageClientOptions<Files::Shares::ShareClientOptions>();
clientOptions.ShareTokenIntent = Files::Shares::Models::ShareTokenIntent::Backup;
// default audience
auto directoryClient = Files::Shares::ShareDirectoryClient(
m_fileShareDirectoryClient->GetUrl(), credential, clientOptions);
EXPECT_NO_THROW(directoryClient.GetProperties());
// custom audience
auto directoryUrl = Azure::Core::Url(directoryClient.GetUrl());
clientOptions.Audience = Files::Shares::Models::ShareAudience(
directoryUrl.GetScheme() + "://" + directoryUrl.GetHost() + "/.default");
directoryClient = Files::Shares::ShareDirectoryClient(
m_fileShareDirectoryClient->GetUrl(), credential, clientOptions);
EXPECT_NO_THROW(directoryClient.GetProperties());
// error audience
clientOptions.Audience
= Files::Shares::Models::ShareAudience("https://disk.compute.azure.com/.default");
directoryClient = Files::Shares::ShareDirectoryClient(
m_fileShareDirectoryClient->GetUrl(), credential, clientOptions);
EXPECT_THROW(directoryClient.GetProperties(), StorageException);
}
}}} // namespace Azure::Storage::Test

View File

@ -1692,6 +1692,51 @@ namespace Azure { namespace Storage { namespace Test {
EXPECT_TRUE(client1.GetUrl().find("snapshot=" + timestamp2) == std::string::npos);
}
TEST_F(FileShareFileClientTest, Audience_PLAYBACKONLY_)
{
auto credential = std::make_shared<Azure::Identity::ClientSecretCredential>(
AadTenantId(),
AadClientId(),
AadClientSecret(),
InitStorageClientOptions<Azure::Identity::ClientSecretCredentialOptions>());
auto clientOptions = InitStorageClientOptions<Files::Shares::ShareClientOptions>();
clientOptions.ShareTokenIntent = Files::Shares::Models::ShareTokenIntent::Backup;
// default audience
auto fileClient
= Files::Shares::ShareFileClient(m_fileClient->GetUrl(), credential, clientOptions);
EXPECT_NO_THROW(fileClient.GetProperties());
// custom audience
auto fileUrl = Azure::Core::Url(fileClient.GetUrl());
clientOptions.Audience = Files::Shares::Models::ShareAudience(
fileUrl.GetScheme() + "://" + fileUrl.GetHost() + "/.default");
fileClient = Files::Shares::ShareFileClient(m_fileClient->GetUrl(), credential, clientOptions);
EXPECT_NO_THROW(fileClient.GetProperties());
fileClient = Files::Shares::ShareServiceClient(
m_shareServiceClient->GetUrl(), credential, clientOptions)
.GetShareClient(m_shareName)
.GetRootDirectoryClient()
.GetSubdirectoryClient(m_directoryName)
.GetFileClient(m_fileName);
EXPECT_NO_THROW(fileClient.GetProperties());
// error audience
clientOptions.Audience
= Files::Shares::Models::ShareAudience("https://disk.compute.azure.com/.default");
fileClient = Files::Shares::ShareFileClient(m_fileClient->GetUrl(), credential, clientOptions);
EXPECT_THROW(fileClient.GetProperties(), StorageException);
fileClient = Files::Shares::ShareServiceClient(
m_shareServiceClient->GetUrl(), credential, clientOptions)
.GetShareClient(m_shareName)
.GetRootDirectoryClient()
.GetSubdirectoryClient(m_directoryName)
.GetFileClient(m_fileName);
EXPECT_THROW(fileClient.GetProperties(), StorageException);
}
TEST(ShareFileHandleAccessRightsTest, ShareFileHandleAccessRights)
{
Files::Shares::Models::ShareFileHandleAccessRights accessRightsA

View File

@ -11,12 +11,42 @@
#include "azure/storage/queues/rest_client.hpp"
#include <azure/core/internal/client_options.hpp>
#include <azure/core/internal/extendable_enumeration.hpp>
#include <azure/storage/common/storage_common.hpp>
#include <chrono>
#include <string>
namespace Azure { namespace Storage { namespace Queues {
namespace Models {
/**
* @brief Audiences available for Blobs
*
*/
class QueueAudience final
: public Azure::Core::_internal::ExtendableEnumeration<QueueAudience> {
public:
/**
* @brief Construct a new QueueAudience object
*
* @param queueAudience The Azure Active Directory audience to use when forming authorization
* scopes. For the Language service, this value corresponds to a URL that identifies the Azure
* cloud where the resource is located. For more information: See
* https://learn.microsoft.com/en-us/azure/storage/blobs/authorize-access-azure-active-directory
*/
explicit QueueAudience(std::string queueAudience)
: ExtendableEnumeration(std::move(queueAudience))
{
}
/**
* @brief Default Audience. Use to acquire a token for authorizing requests to any Azure
* Storage account.
*/
AZ_STORAGE_QUEUES_DLLEXPORT const static QueueAudience PublicAudience;
};
} // namespace Models
/**
* @brief API version for Storage Queue service.
@ -91,6 +121,13 @@ namespace Azure { namespace Storage { namespace Queues {
* to prompt a challenge in order to discover the correct tenant for the resource.
*/
bool EnableTenantDiscovery = false;
/**
* The Audience to use for authentication with Azure Active Directory (AAD).
* #Azure::Storage::Queues::Models::QueueAudience::PublicAudience will be assumed if
* Audience is not set.
*/
Azure::Nullable<Models::QueueAudience> Audience;
};
/**

View File

@ -74,7 +74,9 @@ namespace Azure { namespace Storage { namespace Queues {
perRetryPolicies.emplace_back(std::make_unique<_internal::StoragePerRetryPolicy>());
{
Azure::Core::Credentials::TokenRequestContext tokenContext;
tokenContext.Scopes.emplace_back(_internal::StorageScope);
tokenContext.Scopes.emplace_back(
options.Audience.HasValue() ? options.Audience.Value().ToString()
: Models::QueueAudience::PublicAudience.ToString());
perRetryPolicies.emplace_back(
std::make_unique<_internal::StorageBearerTokenAuthenticationPolicy>(
credential, tokenContext, options.EnableTenantDiscovery));

View File

@ -5,6 +5,11 @@
namespace Azure { namespace Storage { namespace Queues {
namespace Models {
const QueueAudience QueueAudience::PublicAudience(Azure::Storage::_internal::StorageScope);
} // namespace Models
const ServiceVersion ServiceVersion::V2018_03_28(std::string("2018-03-28"));
const ServiceVersion ServiceVersion::V2019_12_12(std::string("2019-12-12"));
const std::chrono::seconds EnqueueMessageOptions::MessageNeverExpires{-1};

View File

@ -72,7 +72,9 @@ namespace Azure { namespace Storage { namespace Queues {
perRetryPolicies.emplace_back(std::make_unique<_internal::StoragePerRetryPolicy>());
{
Azure::Core::Credentials::TokenRequestContext tokenContext;
tokenContext.Scopes.emplace_back(_internal::StorageScope);
tokenContext.Scopes.emplace_back(
options.Audience.HasValue() ? options.Audience.Value().ToString()
: Models::QueueAudience::PublicAudience.ToString());
perRetryPolicies.emplace_back(
std::make_unique<_internal::StorageBearerTokenAuthenticationPolicy>(
credential, tokenContext, options.EnableTenantDiscovery));

View File

@ -233,4 +233,40 @@ namespace Azure { namespace Storage { namespace Test {
queueClient.Delete();
}
TEST_F(QueueClientTest, Audience)
{
auto credential = std::make_shared<Azure::Identity::ClientSecretCredential>(
AadTenantId(),
AadClientId(),
AadClientSecret(),
InitStorageClientOptions<Azure::Identity::ClientSecretCredentialOptions>());
auto clientOptions = InitStorageClientOptions<Queues::QueueClientOptions>();
// default audience
auto queueClient = Queues::QueueClient(m_queueClient->GetUrl(), credential, clientOptions);
EXPECT_NO_THROW(queueClient.GetProperties());
// custom audience
auto queueUrl = Azure::Core::Url(queueClient.GetUrl());
clientOptions.Audience = Queues::Models::QueueAudience(
queueUrl.GetScheme() + "://" + queueUrl.GetHost() + "/.default");
queueClient = Queues::QueueClient(m_queueClient->GetUrl(), credential, clientOptions);
EXPECT_NO_THROW(queueClient.GetProperties());
queueClient
= Queues::QueueServiceClient(m_queueServiceClient->GetUrl(), credential, clientOptions)
.GetQueueClient(m_queueName);
EXPECT_NO_THROW(queueClient.GetProperties());
// error audience
clientOptions.Audience
= Queues::Models::QueueAudience("https://disk.compute.azure.com/.default");
queueClient = Queues::QueueClient(m_queueClient->GetUrl(), credential, clientOptions);
EXPECT_THROW(queueClient.GetProperties(), StorageException);
queueClient
= Queues::QueueServiceClient(m_queueServiceClient->GetUrl(), credential, clientOptions)
.GetQueueClient(m_queueName);
EXPECT_THROW(queueClient.GetProperties(), StorageException);
}
}}} // namespace Azure::Storage::Test

View File

@ -314,4 +314,33 @@ namespace Azure { namespace Storage { namespace Test {
EXPECT_THROW(queueClient.Value.GetProperties(), StorageException);
}
TEST_F(QueueServiceClientTest, Audience)
{
auto credential = std::make_shared<Azure::Identity::ClientSecretCredential>(
AadTenantId(),
AadClientId(),
AadClientSecret(),
InitStorageClientOptions<Azure::Identity::ClientSecretCredentialOptions>());
auto clientOptions = InitStorageClientOptions<Queues::QueueClientOptions>();
// default audience
auto queueServiceClient
= Queues::QueueServiceClient(m_queueServiceClient->GetUrl(), credential, clientOptions);
EXPECT_NO_THROW(queueServiceClient.GetProperties());
// custom audience
auto queueUrl = Azure::Core::Url(queueServiceClient.GetUrl());
clientOptions.Audience = Queues::Models::QueueAudience(
queueUrl.GetScheme() + "://" + queueUrl.GetHost() + "/.default");
queueServiceClient
= Queues::QueueServiceClient(m_queueServiceClient->GetUrl(), credential, clientOptions);
EXPECT_NO_THROW(queueServiceClient.GetProperties());
// error audience
clientOptions.Audience
= Queues::Models::QueueAudience("https://disk.compute.azure.com/.default");
queueServiceClient
= Queues::QueueServiceClient(m_queueServiceClient->GetUrl(), credential, clientOptions);
EXPECT_THROW(queueServiceClient.GetProperties(), StorageException);
}
}}} // namespace Azure::Storage::Test