Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Storage/feature parity #4869

Merged
merged 17 commits into from
Aug 11, 2023
2 changes: 1 addition & 1 deletion sdk/storage/assets.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
"AssetsRepo": "Azure/azure-sdk-assets",
"AssetsRepoPrefixPath": "cpp",
"TagPrefix": "cpp/storage",
"Tag": "cpp/storage_62e8551aa8"
"Tag": "cpp/storage_dcd2923183"
}
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,13 @@ namespace Azure { namespace Storage { namespace Blobs {
* API version used by this client.
*/
std::string ApiVersion;

/**
* Enables tenant discovery through the authorization challenge when the client is configured to
* use a TokenCredential. When enabled, the client will attempt an initial un-authorized request
* to prompt a challenge in order to discover the correct tenant for the resource.
*/
bool EnableTenantDiscovery = false;
};

/**
Expand Down Expand Up @@ -580,6 +587,14 @@ namespace Azure { namespace Storage { namespace Blobs {
* in this option. Default is to replace.
*/
Models::BlobCopySourceTagsMode CopySourceTagsMode;

/**
* @brief Optional. Source authorization used to access the source file.
* The format is: <schema> <signature>
* Only Bearer type is supported. Credentials should be a valid OAuth access token to copy
* source.
*/
std::string SourceAuthorization;
};

/**
Expand Down Expand Up @@ -949,6 +964,14 @@ namespace Azure { namespace Storage { namespace Blobs {
* in this option. Default is to replace.
*/
Models::BlobCopySourceTagsMode CopySourceTagsMode;

/**
* @brief Optional. Source authorization used to access the source file.
* The format is: <schema> <signature>
* Only Bearer type is supported. Credentials should be a valid OAuth access token to copy
* source.
*/
std::string SourceAuthorization;
};

/**
Expand Down Expand Up @@ -997,6 +1020,14 @@ namespace Azure { namespace Storage { namespace Blobs {
* @brief Optional conditions that the source must meet to perform this operation.
*/
SourceAccessConditions;

/**
* @brief Optional. Source authorization used to access the source file.
* The format is: <schema> <signature>
* Only Bearer type is supported. Credentials should be a valid OAuth access token to copy
* source.
*/
std::string SourceAuthorization;
};

/**
Expand Down Expand Up @@ -1282,6 +1313,14 @@ namespace Azure { namespace Storage { namespace Blobs {
* @brief Optional conditions that must be met to perform this operation.
*/
AppendBlobAccessConditions AccessConditions;

/**
* @brief Optional. Source authorization used to access the source file.
* The format is: <schema> <signature>
* Only Bearer type is supported. Credentials should be a valid OAuth access token to copy
* source.
*/
std::string SourceAuthorization;
};

/**
Expand Down Expand Up @@ -1385,6 +1424,14 @@ namespace Azure { namespace Storage { namespace Blobs {
* @brief Optional conditions that the source must meet to perform this operation.
*/
SourceAccessConditions;

/**
* @brief Optional. Source authorization used to access the source file.
* The format is: <schema> <signature>
* Only Bearer type is supported. Credentials should be a valid OAuth access token to copy
* source.
*/
std::string SourceAuthorization;
};

/**
Expand Down
4 changes: 4 additions & 0 deletions sdk/storage/azure-storage-blobs/src/append_blob_client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,10 @@ namespace Azure { namespace Storage { namespace Blobs {
protocolLayerOptions.EncryptionAlgorithm = m_customerProvidedKey.Value().Algorithm.ToString();
}
protocolLayerOptions.EncryptionScope = m_encryptionScope;
if (!options.SourceAuthorization.empty())
{
protocolLayerOptions.CopySourceAuthorization = options.SourceAuthorization;
}
return _detail::AppendBlobClient::AppendBlockFromUri(
*m_pipeline, m_blobUrl, protocolLayerOptions, context);
}
Expand Down
9 changes: 7 additions & 2 deletions sdk/storage/azure-storage-blobs/src/blob_client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include <azure/storage/common/internal/file_io.hpp>
#include <azure/storage/common/internal/reliable_stream.hpp>
#include <azure/storage/common/internal/shared_key_policy.hpp>
#include <azure/storage/common/internal/storage_bearer_token_authentication_policy.hpp>
#include <azure/storage/common/internal/storage_per_retry_policy.hpp>
#include <azure/storage/common/internal/storage_service_version_policy.hpp>
#include <azure/storage/common/internal/storage_switch_to_secondary_policy.hpp>
Expand Down Expand Up @@ -87,8 +88,8 @@ namespace Azure { namespace Storage { namespace Blobs {
Azure::Core::Credentials::TokenRequestContext tokenContext;
tokenContext.Scopes.emplace_back(_internal::StorageScope);
perRetryPolicies.emplace_back(
std::make_unique<Azure::Core::Http::Policies::_internal::BearerTokenAuthenticationPolicy>(
credential, tokenContext));
std::make_unique<_internal::StorageBearerTokenAuthenticationPolicy>(
credential, tokenContext, options.EnableTenantDiscovery));
}
perOperationPolicies.emplace_back(
std::make_unique<_internal::StorageServiceVersionPolicy>(options.ApiVersion));
Expand Down Expand Up @@ -677,6 +678,10 @@ namespace Azure { namespace Storage { namespace Blobs {
protocolLayerOptions.LegalHold = options.HasLegalHold;
protocolLayerOptions.EncryptionScope = m_encryptionScope;
protocolLayerOptions.CopySourceTags = options.CopySourceTagsMode;
if (!options.SourceAuthorization.empty())
{
protocolLayerOptions.CopySourceAuthorization = options.SourceAuthorization;
}

return _detail::BlobClient::CopyFromUri(*m_pipeline, m_blobUrl, protocolLayerOptions, context);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include <azure/storage/common/crypt.hpp>
#include <azure/storage/common/internal/constants.hpp>
#include <azure/storage/common/internal/shared_key_policy.hpp>
#include <azure/storage/common/internal/storage_bearer_token_authentication_policy.hpp>
#include <azure/storage/common/internal/storage_per_retry_policy.hpp>
#include <azure/storage/common/internal/storage_service_version_policy.hpp>
#include <azure/storage/common/internal/storage_switch_to_secondary_policy.hpp>
Expand Down Expand Up @@ -169,9 +170,8 @@ namespace Azure { namespace Storage { namespace Blobs {
{
Azure::Core::Credentials::TokenRequestContext tokenContext;
tokenContext.Scopes.emplace_back(_internal::StorageScope);
tokenAuthPolicy = std::make_unique<
Azure::Core::Http::Policies::_internal::BearerTokenAuthenticationPolicy>(
credential, tokenContext);
tokenAuthPolicy = std::make_unique<_internal::StorageBearerTokenAuthenticationPolicy>(
credential, tokenContext, options.EnableTenantDiscovery);
perRetryPolicies.emplace_back(tokenAuthPolicy->Clone());
}
perOperationPolicies.emplace_back(
Expand Down
6 changes: 3 additions & 3 deletions sdk/storage/azure-storage-blobs/src/blob_service_client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include <azure/storage/common/crypt.hpp>
#include <azure/storage/common/internal/constants.hpp>
#include <azure/storage/common/internal/shared_key_policy.hpp>
#include <azure/storage/common/internal/storage_bearer_token_authentication_policy.hpp>
#include <azure/storage/common/internal/storage_per_retry_policy.hpp>
#include <azure/storage/common/internal/storage_service_version_policy.hpp>
#include <azure/storage/common/internal/storage_switch_to_secondary_policy.hpp>
Expand Down Expand Up @@ -82,9 +83,8 @@ namespace Azure { namespace Storage { namespace Blobs {
{
Azure::Core::Credentials::TokenRequestContext tokenContext;
tokenContext.Scopes.emplace_back(_internal::StorageScope);
tokenAuthPolicy = std::make_unique<
Azure::Core::Http::Policies::_internal::BearerTokenAuthenticationPolicy>(
credential, tokenContext);
tokenAuthPolicy = std::make_unique<_internal::StorageBearerTokenAuthenticationPolicy>(
credential, tokenContext, options.EnableTenantDiscovery);
perRetryPolicies.emplace_back(tokenAuthPolicy->Clone());
}
perOperationPolicies.emplace_back(
Expand Down
10 changes: 10 additions & 0 deletions sdk/storage/azure-storage-blobs/src/block_blob_client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,11 @@ namespace Azure { namespace Storage { namespace Blobs {
}
protocolLayerOptions.EncryptionScope = m_encryptionScope;
protocolLayerOptions.CopySourceTags = options.CopySourceTagsMode;
if (!options.SourceAuthorization.empty())
{
protocolLayerOptions.CopySourceAuthorization = options.SourceAuthorization;
}

return _detail::BlockBlobClient::UploadFromUri(
*m_pipeline, m_blobUrl, protocolLayerOptions, context);
}
Expand Down Expand Up @@ -458,6 +463,11 @@ namespace Azure { namespace Storage { namespace Blobs {
protocolLayerOptions.EncryptionAlgorithm = m_customerProvidedKey.Value().Algorithm.ToString();
}
protocolLayerOptions.EncryptionScope = m_encryptionScope;
if (!options.SourceAuthorization.empty())
{
protocolLayerOptions.CopySourceAuthorization = options.SourceAuthorization;
}

return _detail::BlockBlobClient::StageBlockFromUri(
*m_pipeline, m_blobUrl, protocolLayerOptions, context);
}
Expand Down
4 changes: 4 additions & 0 deletions sdk/storage/azure-storage-blobs/src/page_blob_client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,10 @@ namespace Azure { namespace Storage { namespace Blobs {
protocolLayerOptions.EncryptionAlgorithm = m_customerProvidedKey.Value().Algorithm.ToString();
}
protocolLayerOptions.EncryptionScope = m_encryptionScope;
if (!options.SourceAuthorization.empty())
{
protocolLayerOptions.CopySourceAuthorization = options.SourceAuthorization;
}
return _detail::PageBlobClient::UploadPagesFromUri(
*m_pipeline, m_blobUrl, protocolLayerOptions, context);
}
Expand Down
1 change: 1 addition & 0 deletions sdk/storage/azure-storage-blobs/test/ut/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ add_executable (
azure-storage-blobs-test
append_blob_client_test.cpp
append_blob_client_test.hpp
bearer_token_test.cpp
blob_batch_client_test.cpp
blob_container_client_test.cpp
blob_container_client_test.hpp
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -367,4 +367,30 @@ namespace Azure { namespace Storage { namespace Test {
EXPECT_EQ(ReadBodyStream(appendBlobClient.Download().Value.BodyStream), blockContent);
}

TEST_F(AppendBlobClientTest, OAuthAppendBlockFromUri_LIVEONLY_)
{
const std::vector<uint8_t> blobContent = RandomBuffer(10);
auto contentStream = Azure::Core::IO::MemoryBodyStream(blobContent.data(), blobContent.size());

auto sourceBlobClient = m_blobContainerClient->GetBlockBlobClient(RandomString());
sourceBlobClient.Upload(contentStream);

Azure::Identity::ClientSecretCredential oauthCredential(
AadTenantId(),
AadClientId(),
AadClientSecret(),
Azure::Identity::ClientSecretCredentialOptions());
Azure::Core::Credentials::TokenRequestContext requestContext;
requestContext.Scopes = {Storage::_internal::StorageScope};
auto oauthToken = oauthCredential.GetToken(requestContext, Azure::Core::Context());

auto destBlobClient = GetAppendBlobClientForTest(RandomString());
EXPECT_NO_THROW(destBlobClient.Create());
Storage::Blobs::AppendBlockFromUriOptions options;
options.SourceAuthorization = "Bearer " + oauthToken.Token;
EXPECT_NO_THROW(destBlobClient.AppendBlockFromUri(sourceBlobClient.GetUrl(), options));
auto properties = destBlobClient.GetProperties().Value;
EXPECT_EQ(blobContent.size(), properties.BlobSize);
}

}}} // namespace Azure::Storage::Test
82 changes: 82 additions & 0 deletions sdk/storage/azure-storage-blobs/test/ut/bearer_token_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// SPDX-License-Identifier: MIT

#include "block_blob_client_test.hpp"

namespace Azure { namespace Storage { namespace Test {

TEST_F(BlockBlobClientTest, ClientSecretCredentialWorks)
{
const std::string containerName = LowercaseRandomString();
auto containerClient = Azure::Storage::Blobs::BlobContainerClient::CreateFromConnectionString(
StandardStorageConnectionString(), containerName);
auto credential = std::make_shared<Azure::Identity::ClientSecretCredential>(
AadTenantId(),
AadClientId(),
AadClientSecret(),
InitStorageClientOptions<Core::Credentials::TokenCredentialOptions>());
containerClient = Blobs::BlobContainerClient(
containerClient.GetUrl(), credential, InitStorageClientOptions<Blobs::BlobClientOptions>());

EXPECT_NO_THROW(containerClient.Create());
EXPECT_NO_THROW(containerClient.Delete());
}

TEST_F(BlockBlobClientTest, BearerChallengeWorks)
{
Blobs::BlobClientOptions clientOptions
= InitStorageClientOptions<Azure::Storage::Blobs::BlobClientOptions>();
Azure::Identity::ClientSecretCredentialOptions options;

// With tenantId
clientOptions.EnableTenantDiscovery = true;
options.AdditionallyAllowedTenants = {"*"};
auto blobClient = Blobs::BlobClient(
m_blockBlobClient->GetUrl(),
std::make_shared<Azure::Identity::ClientSecretCredential>(
AadTenantId(), AadClientId(), AadClientSecret(), options),
clientOptions);
EXPECT_NO_THROW(blobClient.GetProperties());
EXPECT_NO_THROW(ReadBodyStream(blobClient.Download().Value.BodyStream));

// Without tenantId
clientOptions.EnableTenantDiscovery = true;
options.AdditionallyAllowedTenants = {"*"};
blobClient = Blobs::BlobClient(
m_blockBlobClient->GetUrl(),
std::make_shared<Azure::Identity::ClientSecretCredential>(
"", AadClientId(), AadClientSecret(), options),
clientOptions);
EXPECT_NO_THROW(blobClient.GetProperties());

// With error tenantId
clientOptions.EnableTenantDiscovery = true;
options.AdditionallyAllowedTenants = {"*"};
blobClient = Blobs::BlobClient(
m_blockBlobClient->GetUrl(),
std::make_shared<Azure::Identity::ClientSecretCredential>(
"test", AadClientId(), AadClientSecret(), options),
clientOptions);
EXPECT_NO_THROW(blobClient.GetProperties());

// Disable Tenant Discovery and without tenantId
clientOptions.EnableTenantDiscovery = false;
blobClient = Blobs::BlobClient(
m_blockBlobClient->GetUrl(),
std::make_shared<Azure::Identity::ClientSecretCredential>(
"", AadClientId(), AadClientSecret(), options),
clientOptions);
EXPECT_THROW(blobClient.GetProperties(), Azure::Core::Credentials::AuthenticationException);

// Don't allow additional tenants
clientOptions.EnableTenantDiscovery = true;
options.AdditionallyAllowedTenants = {};
blobClient = Blobs::BlobClient(
m_blockBlobClient->GetUrl(),
std::make_shared<Azure::Identity::ClientSecretCredential>(
"", AadClientId(), AadClientSecret(), options),
clientOptions);
EXPECT_THROW(blobClient.GetProperties(), Azure::Core::Credentials::AuthenticationException);
}

}}} // namespace Azure::Storage::Test
Loading
Loading