Skip to content

Commit

Permalink
Storage/feature parity (#4869)
Browse files Browse the repository at this point in the history
* add content type support on rename file. (#4712)

* Storage/STG74 Bearer Challenge (#4743)

* bearer challenge

* Storage/STG78 OAuth Copy (#4831)

* OAuth Copy

* add test for oauth copy

* add test

* fix conversation

* fix conversation

* update clang format

* update test record

* update test case

* fix unit test cases

* update recordings

* recordings

* fff

* fix doc

* CL

* recording

* fix typo

* CL

* recording

* revert debug code

---------

Co-authored-by: Jinming Hu <[email protected]>
  • Loading branch information
microzchang and Jinming-Hu authored Aug 11, 2023
1 parent b8d2301 commit 69e5f1a
Show file tree
Hide file tree
Showing 39 changed files with 622 additions and 62 deletions.
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_366c2de93d"
}
4 changes: 4 additions & 0 deletions sdk/storage/azure-storage-blobs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@

### Features Added

- TenantId can now be discovered through the service challenge response, when using a TokenCredential for authorization.
- A new property is now available on `BlobClientOptions` called `EnableTenantDiscovery`. If set to `true`, the client will attempt an initial unauthorized request to the service to prompt a challenge containing the tenantId hint.
- Added a new field `SourceAuthorization` in options for copy operations, which can be used to specify authorization for copy source.

### Breaking Changes

### Bugs Fixed
Expand Down
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: \<scheme\> \<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: \<scheme\> \<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: \<scheme\> \<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: \<scheme\> \<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: \<scheme\> \<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
6 changes: 3 additions & 3 deletions sdk/storage/azure-storage-blobs/src/blob_container_client.cpp
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)
{
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(),
InitStorageClientOptions<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>();
auto options = InitStorageClientOptions<Azure::Identity::ClientSecretCredentialOptions>();

// 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

0 comments on commit 69e5f1a

Please sign in to comment.