Shared Key Authentication Policy (#160)

* Remove Date in request options, assign x-ms-version a default value

* reformat files with clang-format

* Add shared key policy and basic request policy

* Add openssl dependency for linux and macOS

* add some comments, some slight fixes

* Rename CommonRequestPolicy -> CommonHeaderRequestPolicy
This commit is contained in:
JinmingHu 2020-06-09 18:38:35 +08:00 committed by GitHub
parent 6e01498038
commit 401298577e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 618 additions and 118 deletions

View File

@ -9,7 +9,7 @@ jobs:
matrix:
Linux_x64:
vm.image: 'ubuntu-18.04'
vcpkg.deps: 'curl[ssl] libxml2'
vcpkg.deps: 'curl[ssl] libxml2 openssl'
VCPKG_DEFAULT_TRIPLET: 'x64-linux'
Win_x86:
vm.image: 'windows-2019'
@ -25,13 +25,13 @@ jobs:
CMAKE_GENERATOR_PLATFORM: x64
MacOS_x64:
vm.image: 'macOS-10.14'
vcpkg.deps: 'curl[ssl] libxml2'
vcpkg.deps: 'curl[ssl] libxml2 openssl'
VCPKG_DEFAULT_TRIPLET: 'x64-osx'
# Unit testing ON
Linux_x64_with_unit_test:
vm.image: 'ubuntu-18.04'
vcpkg.deps: 'curl[ssl] libxml2'
vcpkg.deps: 'curl[ssl] libxml2 openssl'
VCPKG_DEFAULT_TRIPLET: 'x64-linux'
build.args: ' -DBUILD_TESTING=ON'
Win_x86_with_unit_test:
@ -50,7 +50,7 @@ jobs:
build.args: ' -DBUILD_TESTING=ON'
MacOS_x64_with_unit_test:
vm.image: 'macOS-10.14'
vcpkg.deps: 'curl[ssl] libxml2'
vcpkg.deps: 'curl[ssl] libxml2 openssl'
VCPKG_DEFAULT_TRIPLET: 'x64-osx'
build.args: ' -DBUILD_TESTING=ON'
pool:

View File

@ -13,6 +13,9 @@ set(AZURE_STORAGE_HEADER
inc/common/storage_common.hpp
inc/common/storage_credential.hpp
inc/common/storage_url_builder.hpp
inc/common/common_headers_request_policy.hpp
inc/common/shared_key_policy.hpp
inc/common/crypt.hpp
inc/blobs/blob.hpp
inc/blobs/blob_client.hpp
inc/blobs/block_blob_client.hpp
@ -28,6 +31,9 @@ set(AZURE_STORAGE_SOURCE
src/blobs/blob_container_client.cpp
src/common/storage_credential.cpp
src/common/storage_url_builder.cpp
src/common/common_headers_request_policy.cpp
src/common/shared_key_policy.cpp
src/common/crypt.cpp
)
add_library(azure-storage ${AZURE_STORAGE_HEADER} ${AZURE_STORAGE_SOURCE})
@ -39,7 +45,11 @@ target_include_directories(azure-storage PUBLIC ${LIBXML2_INCLUDE_DIR} $<BUILD_I
target_link_libraries(azure-storage azure-core ${LIBXML2_LIBRARIES})
if(MSVC)
target_link_libraries(azure-storage bcrypt)
target_compile_definitions(azure-storage PRIVATE NOMINMAX)
else()
find_package(OpenSSL REQUIRED)
target_link_libraries(azure-storage OpenSSL::SSL OpenSSL::Crypto)
endif()
# Set version numbers centralized

View File

@ -3,6 +3,6 @@
#pragma once
#include "blobs/blob_container_client.hpp"
#include "blobs/blob_client.hpp"
#include "blobs/blob_container_client.hpp"
#include "blobs/block_blob_client.hpp"

View File

@ -3,14 +3,14 @@
#pragma once
#include "blob_client_options.hpp"
#include "common/storage_credential.hpp"
#include "common/storage_url_builder.hpp"
#include "internal/protocol/blob_rest_client.hpp"
#include <map>
#include <memory>
#include <string>
#include <map>
#include "common/storage_url_builder.hpp"
#include "common/storage_credential.hpp"
#include "blob_client_options.hpp"
#include "internal/protocol/blob_rest_client.hpp"
namespace Azure { namespace Storage { namespace Blobs {

View File

@ -3,12 +3,12 @@
#pragma once
#include <string>
#include <limits>
#include <utility>
#include "internal/protocol/blob_rest_client.hpp"
#include <limits>
#include <string>
#include <utility>
namespace Azure { namespace Storage { namespace Blobs {
struct BlobClientOptions
@ -18,7 +18,6 @@ namespace Azure { namespace Storage { namespace Blobs {
struct BlockBlobClientOptions : public BlobClientOptions
{
};
struct GetBlobPropertiesOptions

View File

@ -3,15 +3,15 @@
#pragma once
#include <string>
#include "blob_container_client_options.hpp"
#include "blobs/blob_client.hpp"
#include "common/storage_credential.hpp"
#include "common/storage_url_builder.hpp"
#include "internal/protocol/blob_rest_client.hpp"
#include <map>
#include <memory>
#include "blobs/blob_client.hpp"
#include "common/storage_url_builder.hpp"
#include "common/storage_credential.hpp"
#include "blob_container_client_options.hpp"
#include "internal/protocol/blob_rest_client.hpp"
#include <string>
namespace Azure { namespace Storage { namespace Blobs {

View File

@ -3,11 +3,11 @@
#pragma once
#include "internal/protocol/blob_rest_client.hpp"
#include <map>
#include <string>
#include <vector>
#include <map>
#include "internal/protocol/blob_rest_client.hpp"
namespace Azure { namespace Storage { namespace Blobs {

View File

@ -3,14 +3,14 @@
#pragma once
#include <map>
#include <string>
#include "blob_client_options.hpp"
#include "blobs/blob_client.hpp"
#include "common/storage_credential.hpp"
#include "blob_client_options.hpp"
#include "internal/protocol/blob_rest_client.hpp"
#include <map>
#include <string>
namespace Azure { namespace Storage { namespace Blobs {
class BlockBlobClient : public BlobClient {

View File

@ -818,7 +818,6 @@ namespace Azure { namespace Storage { namespace Blobs {
struct ListBlobContainersOptions
{
std::string Version;
std::string Date;
std::string Prefix;
std::string Marker;
int MaxResults = 0;
@ -831,8 +830,14 @@ namespace Azure { namespace Storage { namespace Blobs {
{
auto request = Azure::Core::Http::Request(Azure::Core::Http::HttpMethod::Get, url);
request.AddHeader("Content-Length", "0");
request.AddHeader("x-ms-version", options.Version);
request.AddHeader("x-ms-date", options.Date);
if (!options.Version.empty())
{
request.AddHeader("x-ms-version", options.Version);
}
else
{
request.AddHeader("x-ms-version", "2019-07-07");
}
request.AddQueryParameter("comp", "list");
if (!options.Prefix.empty())
{
@ -1004,7 +1009,6 @@ namespace Azure { namespace Storage { namespace Blobs {
struct CreateOptions
{
std::string Version;
std::string Date;
PublicAccessType AccessType = PublicAccessType::Private;
std::map<std::string, std::string> Metadata;
}; // struct CreateOptions
@ -1016,8 +1020,14 @@ namespace Azure { namespace Storage { namespace Blobs {
auto request = Azure::Core::Http::Request(Azure::Core::Http::HttpMethod::Put, url);
request.AddHeader("Content-Length", "0");
request.AddQueryParameter("restype", "container");
request.AddHeader("x-ms-version", options.Version);
request.AddHeader("x-ms-date", options.Date);
if (!options.Version.empty())
{
request.AddHeader("x-ms-version", options.Version);
}
else
{
request.AddHeader("x-ms-version", "2019-07-07");
}
for (const auto& pair : options.Metadata)
{
request.AddHeader("x-ms-meta-" + pair.first, pair.second);
@ -1068,7 +1078,6 @@ namespace Azure { namespace Storage { namespace Blobs {
struct DeleteOptions
{
std::string Version;
std::string Date;
}; // struct DeleteOptions
static Azure::Core::Http::Request DeleteConstructRequest(
@ -1078,8 +1087,14 @@ namespace Azure { namespace Storage { namespace Blobs {
auto request = Azure::Core::Http::Request(Azure::Core::Http::HttpMethod::Delete, url);
request.AddHeader("Content-Length", "0");
request.AddQueryParameter("restype", "container");
request.AddHeader("x-ms-version", options.Version);
request.AddHeader("x-ms-date", options.Date);
if (!options.Version.empty())
{
request.AddHeader("x-ms-version", options.Version);
}
else
{
request.AddHeader("x-ms-version", "2019-07-07");
}
return request;
}
@ -1119,7 +1134,6 @@ namespace Azure { namespace Storage { namespace Blobs {
struct GetPropertiesOptions
{
std::string Version;
std::string Date;
std::string EncryptionKey;
std::string EncryptionKeySHA256;
std::string EncryptionAlgorithm;
@ -1132,8 +1146,14 @@ namespace Azure { namespace Storage { namespace Blobs {
auto request = Azure::Core::Http::Request(Azure::Core::Http::HttpMethod::Head, url);
request.AddHeader("Content-Length", "0");
request.AddQueryParameter("restype", "container");
request.AddHeader("x-ms-version", options.Version);
request.AddHeader("x-ms-date", options.Date);
if (!options.Version.empty())
{
request.AddHeader("x-ms-version", options.Version);
}
else
{
request.AddHeader("x-ms-version", "2019-07-07");
}
if (!options.EncryptionKey.empty())
{
request.AddHeader("x-ms-encryption-key", options.EncryptionKey);
@ -1213,7 +1233,6 @@ namespace Azure { namespace Storage { namespace Blobs {
struct SetMetadataOptions
{
std::string Version;
std::string Date;
std::map<std::string, std::string> Metadata;
}; // struct SetMetadataOptions
@ -1225,8 +1244,14 @@ namespace Azure { namespace Storage { namespace Blobs {
request.AddHeader("Content-Length", "0");
request.AddQueryParameter("restype", "container");
request.AddQueryParameter("comp", "metadata");
request.AddHeader("x-ms-version", options.Version);
request.AddHeader("x-ms-date", options.Date);
if (!options.Version.empty())
{
request.AddHeader("x-ms-version", options.Version);
}
else
{
request.AddHeader("x-ms-version", "2019-07-07");
}
for (const auto& pair : options.Metadata)
{
request.AddHeader("x-ms-meta-" + pair.first, pair.second);
@ -1272,7 +1297,6 @@ namespace Azure { namespace Storage { namespace Blobs {
struct ListBlobsOptions
{
std::string Version;
std::string Date;
std::string Prefix;
std::string Delimiter;
std::string Marker;
@ -1286,8 +1310,14 @@ namespace Azure { namespace Storage { namespace Blobs {
{
auto request = Azure::Core::Http::Request(Azure::Core::Http::HttpMethod::Get, url);
request.AddHeader("Content-Length", "0");
request.AddHeader("x-ms-version", options.Version);
request.AddHeader("x-ms-date", options.Date);
if (!options.Version.empty())
{
request.AddHeader("x-ms-version", options.Version);
}
else
{
request.AddHeader("x-ms-version", "2019-07-07");
}
request.AddQueryParameter("restype", "container");
request.AddQueryParameter("comp", "list");
if (!options.Prefix.empty())
@ -1498,7 +1528,6 @@ namespace Azure { namespace Storage { namespace Blobs {
struct DownloadOptions
{
std::string Version;
std::string Date;
std::pair<uint64_t, uint64_t> Range;
std::string EncryptionKey;
std::string EncryptionKeySHA256;
@ -1511,8 +1540,14 @@ namespace Azure { namespace Storage { namespace Blobs {
{
auto request = Azure::Core::Http::Request(Azure::Core::Http::HttpMethod::Get, url);
request.AddHeader("Content-Length", "0");
request.AddHeader("x-ms-version", options.Version);
request.AddHeader("x-ms-date", options.Date);
if (!options.Version.empty())
{
request.AddHeader("x-ms-version", options.Version);
}
else
{
request.AddHeader("x-ms-version", "2019-07-07");
}
if (options.Range.first <= options.Range.second)
{
request.AddHeader(
@ -1670,7 +1705,6 @@ namespace Azure { namespace Storage { namespace Blobs {
struct DeleteOptions
{
std::string Version;
std::string Date;
DeleteSnapshotsOption DeleteSnapshots = DeleteSnapshotsOption::None;
}; // struct DeleteOptions
@ -1680,8 +1714,14 @@ namespace Azure { namespace Storage { namespace Blobs {
{
auto request = Azure::Core::Http::Request(Azure::Core::Http::HttpMethod::Delete, url);
request.AddHeader("Content-Length", "0");
request.AddHeader("x-ms-version", options.Version);
request.AddHeader("x-ms-date", options.Date);
if (!options.Version.empty())
{
request.AddHeader("x-ms-version", options.Version);
}
else
{
request.AddHeader("x-ms-version", "2019-07-07");
}
return request;
}
@ -1721,7 +1761,6 @@ namespace Azure { namespace Storage { namespace Blobs {
struct GetPropertiesOptions
{
std::string Version;
std::string Date;
}; // struct GetPropertiesOptions
static Azure::Core::Http::Request GetPropertiesConstructRequest(
@ -1730,8 +1769,14 @@ namespace Azure { namespace Storage { namespace Blobs {
{
auto request = Azure::Core::Http::Request(Azure::Core::Http::HttpMethod::Head, url);
request.AddHeader("Content-Length", "0");
request.AddHeader("x-ms-version", options.Version);
request.AddHeader("x-ms-date", options.Date);
if (!options.Version.empty())
{
request.AddHeader("x-ms-version", options.Version);
}
else
{
request.AddHeader("x-ms-version", "2019-07-07");
}
return request;
}
@ -1873,7 +1918,6 @@ namespace Azure { namespace Storage { namespace Blobs {
struct SetHttpHeadersOptions
{
std::string Version;
std::string Date;
std::string ContentType;
std::string ContentEncoding;
std::string ContentLanguage;
@ -1892,8 +1936,14 @@ namespace Azure { namespace Storage { namespace Blobs {
auto request = Azure::Core::Http::Request(Azure::Core::Http::HttpMethod::Put, url);
request.AddHeader("Content-Length", "0");
request.AddQueryParameter("comp", "properties");
request.AddHeader("x-ms-version", options.Version);
request.AddHeader("x-ms-date", options.Date);
if (!options.Version.empty())
{
request.AddHeader("x-ms-version", options.Version);
}
else
{
request.AddHeader("x-ms-version", "2019-07-07");
}
if (!options.ContentType.empty())
{
request.AddHeader("x-ms-blob-content-type", options.ContentType);
@ -1977,7 +2027,6 @@ namespace Azure { namespace Storage { namespace Blobs {
struct SetMetadataOptions
{
std::string Version;
std::string Date;
std::map<std::string, std::string> Metadata;
std::string EncryptionKey;
std::string EncryptionKeySHA256;
@ -1991,8 +2040,14 @@ namespace Azure { namespace Storage { namespace Blobs {
auto request = Azure::Core::Http::Request(Azure::Core::Http::HttpMethod::Put, url);
request.AddHeader("Content-Length", "0");
request.AddQueryParameter("comp", "metadata");
request.AddHeader("x-ms-version", options.Version);
request.AddHeader("x-ms-date", options.Date);
if (!options.Version.empty())
{
request.AddHeader("x-ms-version", options.Version);
}
else
{
request.AddHeader("x-ms-version", "2019-07-07");
}
for (const auto& pair : options.Metadata)
{
request.AddHeader("x-ms-meta-" + pair.first, pair.second);
@ -2054,7 +2109,6 @@ namespace Azure { namespace Storage { namespace Blobs {
struct UploadOptions
{
std::string Version;
std::string Date;
std::vector<uint8_t>* BodyBuffer = nullptr;
Azure::Core::Http::BodyStream* BodyStream = nullptr;
std::string ContentMD5;
@ -2076,8 +2130,14 @@ namespace Azure { namespace Storage { namespace Blobs {
auto request = Azure::Core::Http::Request(
Azure::Core::Http::HttpMethod::Put, url, *options.BodyBuffer);
request.AddHeader("Content-Length", std::to_string(options.BodyBuffer->size()));
request.AddHeader("x-ms-version", options.Version);
request.AddHeader("x-ms-date", options.Date);
if (!options.Version.empty())
{
request.AddHeader("x-ms-version", options.Version);
}
else
{
request.AddHeader("x-ms-version", "2019-07-07");
}
if (!options.EncryptionKey.empty())
{
request.AddHeader("x-ms-encryption-key", options.EncryptionKey);
@ -2199,7 +2259,6 @@ namespace Azure { namespace Storage { namespace Blobs {
struct StageBlockOptions
{
std::string Version;
std::string Date;
std::vector<uint8_t>* BodyBuffer = nullptr;
Azure::Core::Http::BodyStream* BodyStream = nullptr;
std::string BlockId;
@ -2220,8 +2279,14 @@ namespace Azure { namespace Storage { namespace Blobs {
request.AddHeader("Content-Length", std::to_string(options.BodyBuffer->size()));
request.AddQueryParameter("comp", "block");
request.AddQueryParameter("blockid", options.BlockId);
request.AddHeader("x-ms-version", options.Version);
request.AddHeader("x-ms-date", options.Date);
if (!options.Version.empty())
{
request.AddHeader("x-ms-version", options.Version);
}
else
{
request.AddHeader("x-ms-version", "2019-07-07");
}
if (!options.ContentMD5.empty())
{
request.AddHeader("Content-MD5", options.ContentMD5);
@ -2307,7 +2372,6 @@ namespace Azure { namespace Storage { namespace Blobs {
struct CommitBlockListOptions
{
std::string Version;
std::string Date;
std::vector<std::pair<BlockType, std::string>> BlockList;
BlobHttpHeaders Properties;
std::map<std::string, std::string> Metadata;
@ -2369,8 +2433,14 @@ namespace Azure { namespace Storage { namespace Blobs {
= Azure::Core::Http::Request(Azure::Core::Http::HttpMethod::Put, url, body_buffer);
request.AddHeader("Content-Length", std::to_string(body_buffer.size()));
request.AddQueryParameter("comp", "blocklist");
request.AddHeader("x-ms-version", options.Version);
request.AddHeader("x-ms-date", options.Date);
if (!options.Version.empty())
{
request.AddHeader("x-ms-version", options.Version);
}
else
{
request.AddHeader("x-ms-version", "2019-07-07");
}
if (!options.Properties.ContentType.empty())
{
request.AddHeader("x-ms-blob-content-type", options.Properties.ContentType);
@ -2474,7 +2544,6 @@ namespace Azure { namespace Storage { namespace Blobs {
struct GetBlockListOptions
{
std::string Version;
std::string Date;
BlockListTypeOption ListType = BlockListTypeOption::All;
}; // struct GetBlockListOptions
@ -2490,8 +2559,14 @@ namespace Azure { namespace Storage { namespace Blobs {
{
request.AddQueryParameter("blocklisttype", block_list_type_option);
}
request.AddHeader("x-ms-version", options.Version);
request.AddHeader("x-ms-date", options.Date);
if (!options.Version.empty())
{
request.AddHeader("x-ms-version", options.Version);
}
else
{
request.AddHeader("x-ms-version", "2019-07-07");
}
return request;
}

View File

@ -0,0 +1,23 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// SPDX-License-Identifier: MIT
#pragma once
#include "http/policy.hpp"
namespace Azure { namespace Storage {
class CommonHeadersRequestPolicy : public Core::Http::HttpPolicy {
public:
explicit CommonHeadersRequestPolicy() {}
~CommonHeadersRequestPolicy() override {}
HttpPolicy* Clone() const override { return new CommonHeadersRequestPolicy(*this); }
std::unique_ptr<Core::Http::Response> Send(
Core::Context& ctx,
Core::Http::Request& request,
Core::Http::NextHttpPolicy nextHttpPolicy) const override;
};
}} // namespace Azure::Storage

View File

@ -0,0 +1,16 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// SPDX-License-Identifier: MIT
#pragma once
#include <string>
namespace Azure { namespace Storage {
std::string HMAC_SHA256(const std::string& text, const std::string& key);
std::string Base64Encode(const std::string& text);
std::string Base64Decode(const std::string& text);
std::string MD5(const std::string& text);
std::string CRC64(const std::string& text);
}} // namespace Azure::Storage

View File

@ -0,0 +1,38 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// SPDX-License-Identifier: MIT
#pragma once
#include "common/storage_credential.hpp"
#include "http/policy.hpp"
namespace Azure { namespace Storage {
class SharedKeyPolicy : public Core::Http::HttpPolicy {
public:
explicit SharedKeyPolicy(std::shared_ptr<SharedKeyCredential> credential)
: m_credential(std::move(credential))
{
}
~SharedKeyPolicy() override {}
HttpPolicy* Clone() const override { return new SharedKeyPolicy(m_credential); }
std::unique_ptr<Core::Http::Response> Send(
Core::Context& ctx,
Core::Http::Request& request,
Core::Http::NextHttpPolicy nextHttpPolicy) const override
{
request.AddHeader(
"Authorization", "SharedKey " + m_credential->AccountName + ":" + GetSignature(request));
return nextHttpPolicy.Send(ctx, request);
}
private:
std::string GetSignature(const Core::Http::Request& request) const;
std::shared_ptr<SharedKeyCredential> m_credential;
};
}} // namespace Azure::Storage

View File

@ -5,8 +5,6 @@
namespace Azure { namespace Storage {
template <class... T> void unused(T&&...)
{
}
template <class... T> void unused(T&&...) {}
}} // namespace Azure::Storage

View File

@ -11,9 +11,7 @@ namespace Azure { namespace Storage {
struct TokenCredential
{
explicit TokenCredential(std::string token) : Token(std::move(token))
{
}
explicit TokenCredential(std::string token) : Token(std::move(token)) {}
void SetToken(std::string token)
{
@ -47,6 +45,7 @@ namespace Azure { namespace Storage {
std::string AccountName;
private:
friend class SharedKeyPolicy;
std::string GetAccountKey()
{
std::lock_guard<std::mutex> guard(Mutex);

View File

@ -3,41 +3,35 @@
#pragma once
#include <functional>
#include <map>
#include <string>
#include <functional>
namespace Azure { namespace Storage {
class UrlBuilder {
public:
UrlBuilder()
{
}
UrlBuilder() {}
// url must be url-encoded
explicit UrlBuilder(const std::string& url);
void SetScheme(const std::string& scheme)
{
m_scheme = scheme;
}
void SetScheme(const std::string& scheme) { m_scheme = scheme; }
void SetHost(const std::string& host, bool do_encoding = false)
{
m_host = do_encoding ? EncodeHost(host) : host;
}
void SetPort(uint16_t port)
{
m_port = port;
}
void SetPort(uint16_t port) { m_port = port; }
void SetPath(const std::string& path, bool do_encoding = false)
{
m_path = do_encoding ? EncodePath(path) : path;
}
const std::string& GetPath() const { return m_path; }
void AppendPath(const std::string& path, bool do_encoding = false)
{
if (!m_path.empty() && m_path.back() != '/')
@ -62,10 +56,9 @@ namespace Azure { namespace Storage {
}
}
void RemoveQuery(const std::string& key)
{
m_query.erase(key);
}
void RemoveQuery(const std::string& key) { m_query.erase(key); }
const std::map<std::string, std::string>& GetQuery() const { return m_query; }
void SetFragment(const std::string& fragment, bool do_encoding = false)
{
@ -79,7 +72,9 @@ namespace Azure { namespace Storage {
static std::string EncodePath(const std::string& path);
static std::string EncodeQuery(const std::string& query);
static std::string EncodeFragment(const std::string& fragment);
static std::string EncodeImpl(const std::string& source, const std::function<bool(int)>& should_encode);
static std::string EncodeImpl(
const std::string& source,
const std::function<bool(int)>& should_encode);
std::string m_scheme;
std::string m_host;

View File

@ -3,6 +3,8 @@
#include "blobs/blob_client.hpp"
#include "common/common_headers_request_policy.hpp"
#include "common/shared_key_policy.hpp"
#include "common/storage_common.hpp"
#include "http/curl/curl.hpp"
@ -56,7 +58,7 @@ namespace Azure { namespace Storage { namespace Blobs {
}
else if (!accountName.empty())
{
builder.SetHost(accountName + ".blob" + defaultEndpointsProtocol);
builder.SetHost(accountName + ".blob." + EndpointSuffix);
}
else
{
@ -74,20 +76,37 @@ namespace Azure { namespace Storage { namespace Blobs {
const std::string& blobUri,
std::shared_ptr<SharedKeyCredential> credential,
const BlobClientOptions& options)
: BlobClient(blobUri, options)
: m_blobUrl(blobUri)
{
// not implemented yet
unused(credential, options);
std::vector<std::unique_ptr<Azure::Core::Http::HttpPolicy>> policies;
for (const auto& p : options.policies)
{
policies.emplace_back(std::unique_ptr<Azure::Core::Http::HttpPolicy>(p->Clone()));
}
policies.emplace_back(std::make_unique<CommonHeadersRequestPolicy>());
policies.emplace_back(std::make_unique<SharedKeyPolicy>(credential));
policies.emplace_back(std::make_unique<Azure::Core::Http::TransportPolicy>(
std::make_shared<Azure::Core::Http::CurlTransport>()));
m_pipeline = std::make_shared<Azure::Core::Http::HttpPipeline>(policies);
}
BlobClient::BlobClient(
const std::string& blobUri,
std::shared_ptr<TokenCredential> credential,
const BlobClientOptions& options)
: BlobClient(blobUri, options)
: m_blobUrl(blobUri)
{
std::vector<std::unique_ptr<Azure::Core::Http::HttpPolicy>> policies;
for (const auto& p : options.policies)
{
policies.emplace_back(std::unique_ptr<Azure::Core::Http::HttpPolicy>(p->Clone()));
}
policies.emplace_back(std::make_unique<CommonHeadersRequestPolicy>());
// not implemented yet
unused(credential);
policies.emplace_back(std::make_unique<Azure::Core::Http::TransportPolicy>(
std::make_shared<Azure::Core::Http::CurlTransport>()));
m_pipeline = std::make_shared<Azure::Core::Http::HttpPipeline>(policies);
}
BlobClient::BlobClient(const std::string& blobUri, const BlobClientOptions& options)
@ -98,6 +117,7 @@ namespace Azure { namespace Storage { namespace Blobs {
{
policies.emplace_back(std::unique_ptr<Azure::Core::Http::HttpPolicy>(p->Clone()));
}
policies.emplace_back(std::make_unique<CommonHeadersRequestPolicy>());
policies.emplace_back(std::make_unique<Azure::Core::Http::TransportPolicy>(
std::make_shared<Azure::Core::Http::CurlTransport>()));
m_pipeline = std::make_shared<Azure::Core::Http::HttpPipeline>(policies);

View File

@ -3,6 +3,8 @@
#include "blobs/blob_container_client.hpp"
#include "common/common_headers_request_policy.hpp"
#include "common/shared_key_policy.hpp"
#include "common/storage_common.hpp"
#include "http/curl/curl.hpp"
@ -55,7 +57,7 @@ namespace Azure { namespace Storage { namespace Blobs {
}
else if (!accountName.empty())
{
builder.SetHost(accountName + ".blob" + defaultEndpointsProtocol);
builder.SetHost(accountName + ".blob." + EndpointSuffix);
}
else
{
@ -74,8 +76,16 @@ namespace Azure { namespace Storage { namespace Blobs {
const BlobContainerClientOptions& options)
: BlobContainerClient(containerUri, options)
{
// not implemented yet
unused(credential);
std::vector<std::unique_ptr<Azure::Core::Http::HttpPolicy>> policies;
for (const auto& p : options.policies)
{
policies.emplace_back(std::unique_ptr<Azure::Core::Http::HttpPolicy>(p->Clone()));
}
policies.emplace_back(std::make_unique<CommonHeadersRequestPolicy>());
policies.emplace_back(std::make_unique<SharedKeyPolicy>(credential));
policies.emplace_back(std::make_unique<Azure::Core::Http::TransportPolicy>(
std::make_shared<Azure::Core::Http::CurlTransport>()));
m_pipeline = std::make_shared<Azure::Core::Http::HttpPipeline>(policies);
}
BlobContainerClient::BlobContainerClient(
@ -84,8 +94,17 @@ namespace Azure { namespace Storage { namespace Blobs {
const BlobContainerClientOptions& options)
: BlobContainerClient(containerUri, options)
{
std::vector<std::unique_ptr<Azure::Core::Http::HttpPolicy>> policies;
for (const auto& p : options.policies)
{
policies.emplace_back(std::unique_ptr<Azure::Core::Http::HttpPolicy>(p->Clone()));
}
policies.emplace_back(std::make_unique<CommonHeadersRequestPolicy>());
// not implemented yet
unused(credential);
policies.emplace_back(std::make_unique<Azure::Core::Http::TransportPolicy>(
std::make_shared<Azure::Core::Http::CurlTransport>()));
m_pipeline = std::make_shared<Azure::Core::Http::HttpPipeline>(policies);
}
BlobContainerClient::BlobContainerClient(
@ -98,6 +117,7 @@ namespace Azure { namespace Storage { namespace Blobs {
{
policies.emplace_back(std::unique_ptr<Azure::Core::Http::HttpPolicy>(p->Clone()));
}
policies.emplace_back(std::make_unique<CommonHeadersRequestPolicy>());
policies.emplace_back(std::make_unique<Azure::Core::Http::TransportPolicy>(
std::make_shared<Azure::Core::Http::CurlTransport>()));
m_pipeline = std::make_shared<Azure::Core::Http::HttpPipeline>(policies);

View File

@ -41,9 +41,7 @@ namespace Azure { namespace Storage { namespace Blobs {
{
}
BlockBlobClient::BlockBlobClient(BlobClient blobClient) : BlobClient(std::move(blobClient))
{
}
BlockBlobClient::BlockBlobClient(BlobClient blobClient) : BlobClient(std::move(blobClient)) {}
BlockBlobClient BlockBlobClient::WithSnapshot(const std::string& snapshot)
{

View File

@ -0,0 +1,36 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// SPDX-License-Identifier: MIT
#include "common/common_headers_request_policy.hpp"
#include <ctime>
namespace Azure { namespace Storage {
std::unique_ptr<Core::Http::Response> CommonHeadersRequestPolicy::Send(
Core::Context& ctx,
Core::Http::Request& request,
Core::Http::NextHttpPolicy nextHttpPolicy) const
{
const auto& headers = request.GetHeaders();
if (headers.find("Date") == headers.end() && headers.find("x-ms-date") == headers.end())
{
// add x-ms-date header in RFC1123 format
// TODO: call helper function provided by Azure Core when they provide one.
time_t t = std::time(nullptr);
struct tm ct;
#ifdef _WIN32
gmtime_s(&ct, &t);
#else
gmtime_r(&t, &ct);
#endif
char dateString[128];
strftime(dateString, sizeof(dateString), "%a, %d %b %Y %H:%M:%S GMT", &ct);
request.AddHeader("x-ms-date", dateString);
}
return nextHttpPolicy.Send(ctx, request);
}
}} // namespace Azure::Storage

View File

@ -0,0 +1,193 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// SPDX-License-Identifier: MIT
#include "common/crypt.hpp"
#include "common/storage_common.hpp"
#ifdef _WIN32
#include <Windows.h>
#include <bcrypt.h>
#else
#include <openssl/bio.h>
#include <openssl/buffer.h>
#include <openssl/evp.h>
#include <openssl/hmac.h>
#endif
#include <stdexcept>
namespace Azure { namespace Storage {
#ifdef _WIN32
std::string HMAC_SHA256(const std::string& text, const std::string& key)
{
struct AlgorithmProviderInstance
{
BCRYPT_ALG_HANDLE Handle;
std::size_t ContextSize;
std::size_t HashLength;
AlgorithmProviderInstance()
{
NTSTATUS status = BCryptOpenAlgorithmProvider(
&Handle, BCRYPT_SHA256_ALGORITHM, NULL, BCRYPT_ALG_HANDLE_HMAC_FLAG);
if (!BCRYPT_SUCCESS(status))
{
throw std::runtime_error("BCryptOpenAlgorithmProvider failed");
}
DWORD objectLength = 0;
DWORD dataLength = 0;
status = BCryptGetProperty(
Handle,
BCRYPT_OBJECT_LENGTH,
(PBYTE)&objectLength,
sizeof(objectLength),
&dataLength,
0);
if (!BCRYPT_SUCCESS(status))
{
throw std::runtime_error("BCryptGetProperty failed");
}
ContextSize = objectLength;
DWORD hashLength = 0;
status = BCryptGetProperty(
Handle, BCRYPT_HASH_LENGTH, (PBYTE)&hashLength, sizeof(hashLength), &dataLength, 0);
if (!BCRYPT_SUCCESS(status))
{
throw std::runtime_error("BCryptGetProperty failed");
}
HashLength = hashLength;
}
~AlgorithmProviderInstance() { BCryptCloseAlgorithmProvider(Handle, 0); }
};
static AlgorithmProviderInstance AlgorithmProvider;
std::string context;
context.resize(AlgorithmProvider.ContextSize);
BCRYPT_HASH_HANDLE hashHandle;
NTSTATUS status = BCryptCreateHash(
AlgorithmProvider.Handle,
&hashHandle,
(PUCHAR)context.data(),
(ULONG)context.size(),
(PUCHAR)key.data(),
(ULONG)key.length(),
0);
if (!BCRYPT_SUCCESS(status))
{
throw std::runtime_error("BCryptCreateHash failed");
}
status = BCryptHashData(hashHandle, (PBYTE)text.data(), (ULONG)text.length(), 0);
if (!BCRYPT_SUCCESS(status))
{
throw std::runtime_error("BCryptHashData failed");
}
std::string hash;
hash.resize(AlgorithmProvider.HashLength);
status = BCryptFinishHash(hashHandle, (PUCHAR)hash.data(), (ULONG)hash.length(), 0);
if (!BCRYPT_SUCCESS(status))
{
throw std::runtime_error("BCryptFinishHash failed");
}
BCryptDestroyHash(hashHandle);
return hash;
}
std::string Base64Encode(const std::string& text)
{
std::string encoded;
// According to RFC 4648, the encoded length should be ceiling(n / 3) * 4
DWORD encodedLength = DWORD((text.length() + 2) / 3 * 4);
encoded.resize(encodedLength);
CryptBinaryToStringA(
(BYTE*)text.data(),
(DWORD)text.length(),
CRYPT_STRING_BASE64 | CRYPT_STRING_NOCRLF,
(LPSTR)encoded.data(),
(DWORD*)&encodedLength);
return encoded;
}
std::string Base64Decode(const std::string& text)
{
std::string decoded;
// According to RFC 4648, the encoded length should be ceiling(n / 3) * 4, so we can infer an
// upper bound here
DWORD decodedLength = DWORD(text.length() / 4 * 3);
decoded.resize(decodedLength);
CryptStringToBinaryA(
text.data(),
(DWORD)text.length(),
CRYPT_STRING_BASE64 | CRYPT_STRING_STRICT,
(BYTE*)decoded.data(),
&decodedLength,
NULL,
NULL);
decoded.resize(decodedLength);
return decoded;
}
#else
std::string HMAC_SHA256(const std::string& text, const std::string& key)
{
char hash[EVP_MAX_MD_SIZE];
unsigned int hashLength = 0;
HMAC(
EVP_sha256(),
key.data(),
(int)key.length(),
(const unsigned char*)text.data(),
text.length(),
(unsigned char*)&hash[0],
&hashLength);
return std::string(hash, hashLength);
}
std::string Base64Encode(const std::string& text)
{
BIO* bio = BIO_new(BIO_s_mem());
bio = BIO_push(BIO_new(BIO_f_base64()), bio);
BIO_set_flags(bio, BIO_FLAGS_BASE64_NO_NL);
BIO_write(bio, text.data(), (int)text.length());
BIO_flush(bio);
BUF_MEM* bufferPtr;
BIO_get_mem_ptr(bio, &bufferPtr);
BIO_set_close(bio, BIO_NOCLOSE);
BIO_free_all(bio);
return std::string(bufferPtr->data, bufferPtr->length);
}
std::string Base64Decode(const std::string& text)
{
std::string decoded;
// According to RFC 4648, the encoded length should be ceiling(n / 3) * 4, so we can infer an
// upper bound here
std::size_t maxDecodedLength = text.length() / 4 * 3;
decoded.resize(maxDecodedLength);
BIO* bio = BIO_new_mem_buf(text.data(), -1);
bio = BIO_push(BIO_new(BIO_f_base64()), bio);
BIO_set_flags(bio, BIO_FLAGS_BASE64_NO_NL);
int decodedLength = BIO_read(bio, &decoded[0], (int)text.length());
BIO_free_all(bio);
decoded.resize(decodedLength);
return decoded;
}
#endif
}} // namespace Azure::Storage

View File

@ -0,0 +1,78 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// SPDX-License-Identifier: MIT
#include "common/shared_key_policy.hpp"
#include "common/crypt.hpp"
#include "common/storage_url_builder.hpp"
#include <algorithm>
#include <cctype>
namespace Azure { namespace Storage {
std::string SharedKeyPolicy::GetSignature(const Core::Http::Request& request) const
{
std::string string_to_sign;
string_to_sign += Azure::Core::Http::HttpMethodToString(request.GetMethod()) + "\n";
const auto& headers = request.GetHeaders();
for (std::string headerName :
{"Content-Encoding",
"Content-Language",
"Content-Length",
"Content-MD5",
"Content-Type",
"Date",
"If-Modified-Since",
"If-Match",
"If-None-Match",
"If-Unmodified-Since",
"Range"})
{
auto ite = headers.find(headerName);
if (ite != headers.end())
{
if (headerName == "Content-Length" && ite->second == "0")
{
// do nothing
}
else
{
string_to_sign += ite->second;
}
}
string_to_sign += "\n";
}
// canonicalized headers
const std::string prefix = "x-ms-";
for (auto ite = headers.lower_bound(prefix);
ite != headers.end() && ite->first.substr(0, prefix.length()) == prefix;
++ite)
{
std::string key = ite->first;
std::string value = ite->second;
std::transform(key.begin(), key.end(), key.begin(), [](char c) {
return static_cast<char>(std::tolower(static_cast<unsigned char>(c)));
});
string_to_sign += key + ":" + value + "\n";
}
// canonicalized resource
UrlBuilder resourceUrl(request.GetEncodedUrl());
string_to_sign += "/" + m_credential->AccountName + "/" + resourceUrl.GetPath() + "\n";
for (const auto& query : resourceUrl.GetQuery())
{
std::string key = query.first;
std::transform(key.begin(), key.end(), key.begin(), [](char c) {
return static_cast<char>(std::tolower(static_cast<unsigned char>(c)));
});
string_to_sign += key + ":" + query.second + "\n";
}
// remove last linebreak
string_to_sign.pop_back();
return Base64Encode(HMAC_SHA256(string_to_sign, Base64Decode(m_credential->GetAccountKey())));
}
}} // namespace Azure::Storage

View File

@ -1,10 +1,10 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// SPDX-License-Identifier: MIT
#include <algorithm>
#include "common/storage_credential.hpp"
#include <algorithm>
namespace Azure { namespace Storage {
std::map<std::string, std::string> ParseConnectionString(const std::string& connectionString)

View File

@ -3,10 +3,9 @@
#include "common/storage_url_builder.hpp"
#include <locale>
#include <vector>
#include <algorithm>
#include <cctype>
#include <vector>
namespace Azure { namespace Storage {
@ -18,19 +17,22 @@ namespace Azure { namespace Storage {
auto schemeIter = url.find(schemeEnd);
if (schemeIter != std::string::npos)
{
std::transform(url.begin(), url.begin() + schemeIter, std::back_inserter(m_scheme), [](char c) {
return static_cast<char>(std::tolower(static_cast<unsigned char>(c)));
});
std::transform(
url.begin(), url.begin() + schemeIter, std::back_inserter(m_scheme), [](char c) {
return static_cast<char>(std::tolower(static_cast<unsigned char>(c)));
});
pos = url.begin() + schemeIter + schemeEnd.length();
}
auto hostIter = std::find_if(pos, url.end(), [](char c) { return c == '/' || c == '?' || c == ':'; });
auto hostIter
= std::find_if(pos, url.end(), [](char c) { return c == '/' || c == '?' || c == ':'; });
m_host = std::string(pos, hostIter);
pos = hostIter;
if (pos != url.end() && *pos == ':')
{
auto port_ite = std::find_if_not(pos + 1, url.end(), [](char c) { return std::isdigit(static_cast<unsigned char>(c)); });
auto port_ite = std::find_if_not(
pos + 1, url.end(), [](char c) { return std::isdigit(static_cast<unsigned char>(c)); });
m_port = std::stoi(std::string(pos + 1, port_ite));
pos = port_ite;
}