Storage Client Options Support Audiences.
This commit is contained in:
parent
f04b7164eb
commit
04a7477d2e
@ -2,5 +2,5 @@
|
||||
"AssetsRepo": "Azure/azure-sdk-assets",
|
||||
"AssetsRepoPrefixPath": "cpp",
|
||||
"TagPrefix": "cpp/storage",
|
||||
"Tag": "cpp/storage_a5249cec25"
|
||||
"Tag": "cpp/storage_e44851d82e"
|
||||
}
|
||||
|
||||
@ -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;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@ -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));
|
||||
|
||||
@ -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());
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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());
|
||||
|
||||
@ -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 = {"*"};
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@ -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));
|
||||
|
||||
@ -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
|
||||
@ -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));
|
||||
|
||||
@ -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));
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@ -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));
|
||||
|
||||
@ -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));
|
||||
|
||||
@ -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));
|
||||
|
||||
10
sdk/storage/azure-storage-files-shares/src/share_options.cpp
Normal file
10
sdk/storage/azure-storage-files-shares/src/share_options.cpp
Normal 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
|
||||
@ -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));
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@ -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));
|
||||
|
||||
@ -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};
|
||||
|
||||
@ -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));
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
Loading…
Reference in New Issue
Block a user