Add Account SAS and Blob SAS (#353)

* Add Account SAS and Blob SAS

* add doc for sas

* remove Nones

* remove Nones
This commit is contained in:
JinmingHu 2020-07-28 13:43:04 +08:00 committed by GitHub
parent b380dfb76a
commit 9d1566c5ea
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 1376 additions and 37 deletions

View File

@ -28,6 +28,7 @@ set(AZURE_STORAGE_COMMON_HEADER
inc/common/storage_uri_builder.hpp
inc/common/storage_version.hpp
inc/common/xml_wrapper.hpp
inc/common/account_sas_builder.hpp
)
set(AZURE_STORAGE_COMMON_SOURCE
@ -39,6 +40,7 @@ set(AZURE_STORAGE_COMMON_SOURCE
src/common/storage_error.cpp
src/common/storage_uri_builder.cpp
src/common/xml_wrapper.cpp
src/common/account_sas_builder.cpp
)
add_library(azure-storage-common ${AZURE_STORAGE_COMMON_HEADER} ${AZURE_STORAGE_COMMON_SOURCE})
@ -93,6 +95,7 @@ set (AZURE_STORAGE_BLOB_HEADER
inc/blobs/append_blob_client.hpp
inc/blobs/blob_options.hpp
inc/blobs/blob_responses.hpp
inc/blobs/blob_sas_builder.hpp
inc/blobs/protocol/blob_rest_client.hpp
)
@ -103,6 +106,7 @@ set (AZURE_STORAGE_BLOB_SOURCE
src/blobs/block_blob_client.cpp
src/blobs/page_blob_client.cpp
src/blobs/append_blob_client.cpp
src/blobs/blob_sas_builder.cpp
)
add_library(azure-storage-blob ${AZURE_STORAGE_BLOB_HEADER} ${AZURE_STORAGE_BLOB_SOURCE})

View File

@ -0,0 +1,313 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// SPDX-License-Identifier: MIT
#pragma once
#include <string>
#include "blobs/protocol/blob_rest_client.hpp"
#include "common/account_sas_builder.hpp"
#include "nullable.hpp"
namespace Azure { namespace Storage { namespace Blobs {
/**
* @brief Specifies which resources are accessible via the shared access signature.
*/
enum class BlobSasResource
{
/**
* @brief Grants access to the content and metadata of any blob in the container, and to
* the list of blobs in the container.
*/
Container,
/**
* @brief Grants access to the content and metadata of the blob.
*/
Blob,
/**
* @brief Grants access to the content and metadata of the specific snapshot, but not
* the corresponding root blob.
*/
BlobSnapshot,
/**
* @brief Grants access to the content and metadata of the specific version, but not the
* corresponding root blob.
*/
BlobVersion,
};
/**
* @brief The list of permissions that can be set for a blob container's access policy.
*/
enum class BlobContainerSasPermissions
{
/**
* @brief Indicates that Read is permitted.
*/
Read = 1,
/**
* @brief Indicates that Write is permitted.
*/
Write = 2,
/**
* @brief Indicates that Delete is permitted.
*/
Delete = 4,
/**
* @brief Indicates that List is permitted.
*/
List = 8,
/**
* @brief Indicates that Add is permitted.
*/
Add = 16,
/**
* @brief Indicates that Create is permitted.
*/
Create = 32,
/**
* @brief Indicates that reading and writing tags is permitted.
*/
Tags = 64,
/**
* @brief Indicates that deleting previous blob version is permitted.
*/
DeleteVersion = 128,
/**
* @beirf Indicates that all permissions are set.
*/
All = ~0,
};
inline BlobContainerSasPermissions operator|(
BlobContainerSasPermissions lhs,
BlobContainerSasPermissions rhs)
{
using type = std::underlying_type_t<BlobContainerSasPermissions>;
return static_cast<BlobContainerSasPermissions>(
static_cast<type>(lhs) | static_cast<type>(rhs));
}
inline BlobContainerSasPermissions operator&(
BlobContainerSasPermissions lhs,
BlobContainerSasPermissions rhs)
{
using type = std::underlying_type_t<BlobContainerSasPermissions>;
return static_cast<BlobContainerSasPermissions>(
static_cast<type>(lhs) & static_cast<type>(rhs));
}
/**
* @brief The list of permissions that can be set for a blob's access policy.
*/
enum class BlobSasPermissions
{
/**
* @brief Indicates that Read is permitted.
*/
Read = 1,
/**
* @brief Indicates that Write is permitted.
*/
Write = 2,
/**
* @brief Indicates that Delete is permitted.
*/
Delete = 4,
/**
* @brief Indicates that Add is permitted.
*/
Add = 8,
/**
* @brief Indicates that Create is permitted.
*/
Create = 16,
/**
* @brief Indicates that reading and writing tags is permitted.
*/
Tags = 32,
/**
* @brief Indicates that deleting previous blob version is permitted.
*/
DeleteVersion = 64,
/**
* @beirf Indicates that all permissions are set.
*/
All = ~0,
};
inline BlobSasPermissions operator|(BlobSasPermissions lhs, BlobSasPermissions rhs)
{
using type = std::underlying_type_t<BlobSasPermissions>;
return static_cast<BlobSasPermissions>(static_cast<type>(lhs) | static_cast<type>(rhs));
}
inline BlobSasPermissions operator&(BlobSasPermissions lhs, BlobSasPermissions rhs)
{
using type = std::underlying_type_t<BlobSasPermissions>;
return static_cast<BlobSasPermissions>(static_cast<type>(lhs) & static_cast<type>(rhs));
}
/**
* @brief BlobSasBuilder is used to generate a Shared Access Signature (SAS) for an Azure
* Storage container or blob.
*/
struct BlobSasBuilder
{
/**
* @brief The storage service version to use to authenticate requests made with this
* shared access signature, and the service version to use when handling requests made with this
* shared access signature.
*/
std::string Version = c_APIVersion;
/**
* @brief The optional signed protocol field specifies the protocol permitted for a
* request made with the SAS.
*/
SasProtocol Protocol;
/**
* @brief Optionally specify the time at which the shared access signature becomes
* valid.
*/
std::string StartsOn;
/**
* @brief The time at which the shared access signature becomes invalid. This field must
* be omitted if it has been specified in an associated stored access policy.
*/
std::string ExpiresOn;
/**
* @brief Specifies an IP address or a range of IP addresses from which to accept
* requests. If the IP address from which the request originates does not match the IP address
* or address range specified on the SAS token, the request is not authenticated. When
* specifying a range of IP addresses, note that the range is inclusive.
*/
Azure::Core::Nullable<std::string> IPRange;
/**
* @brief An optional unique value up to 64 characters in length that correlates to an
* access policy specified for the container.
*/
std::string Identifier;
/**
* @brief The name of the blob container being made accessible.
*/
std::string ContainerName;
/**
* @brief The name of the blob being made accessible, or empty for a container SAS..
*/
std::string BlobName;
/**
* @brief The name of the blob snapshot being made accessible, or empty for a container
* SAS and blob SAS.
*/
std::string Snapshot;
/**
* @brief The id of the blob version being made accessible, or empty for a container
* SAS, blob SAS and blob snapshot SAS.
*/
std::string BlobVersionId;
/**
* @brief Specifies which resources are accessible via the shared access signature.
*/
BlobSasResource Resource;
/**
* @brief Override the value returned for Cache-Control response header..
*/
std::string CacheControl;
/**
* @brief Override the value returned for Content-Disposition response header..
*/
std::string ContentDisposition;
/**
* @brief Override the value returned for Content-Encoding response header..
*/
std::string ContentEncoding;
/**
* @brief Override the value returned for Content-Language response header..
*/
std::string ContentLanguage;
/**
* @brief Override the value returned for Content-Type response header..
*/
std::string ContentType;
/**
* @brief Sets the permissions for the blob container SAS.
*
* @param
* permissions The allowed permissions.
*/
void SetPermissions(BlobContainerSasPermissions permissions);
/**
* @brief Sets the permissions for the blob SAS.
*
* @param permissions The
* allowed permissions.
*/
void SetPermissions(BlobSasPermissions permissions);
/**
* @brief Uses the SharedKeyCredential to sign this shared access signature, to produce
* the proper SAS query parameters for authentication requests.
*
* @param credential
* The storage account's shared key credential.
* @return The SAS query parameters used for
* authenticating requests.
*/
std::string ToSasQueryParameters(const SharedKeyCredential& credential);
/**
* @brief Uses an account's user delegation key to sign this shared access signature, to
* produce the proper SAS query parameters for authentication requests.
*
* @param
* credential UserDelegationKey retruned from BlobServiceClient.GetUserDelegationKey.
* @param accountName The name of the storage account.
* @return The SAS query parameters
* used for authenticating requests.
*/
std::string ToSasQueryParameters(
const UserDelegationKey& userDelegationKey,
const std::string& accountName);
private:
std::string Permissions;
};
}}} // namespace Azure::Storage::Blobs

View File

@ -28,6 +28,8 @@ namespace Azure { namespace Storage { namespace Blobs {
Azure::Core::Http::BodyStream,
std::function<void(Azure::Core::Http::BodyStream*)>>;
constexpr static const char* c_APIVersion = "2019-12-12";
struct AbortCopyBlobInfo
{
}; // struct AbortCopyBlobInfo
@ -1006,7 +1008,7 @@ namespace Azure { namespace Storage { namespace Blobs {
{
unused(body);
auto request = Azure::Core::Http::Request(Azure::Core::Http::HttpMethod::Get, url);
request.AddHeader("x-ms-version", "2019-12-12");
request.AddHeader("x-ms-version", c_APIVersion);
if (options.Timeout.HasValue())
{
request.AddQueryParameter("timeout", std::to_string(options.Timeout.GetValue()));
@ -1109,7 +1111,7 @@ namespace Azure { namespace Storage { namespace Blobs {
request.AddHeader("Content-Length", std::to_string(body->Length()));
request.AddQueryParameter("restype", "service");
request.AddQueryParameter("comp", "userdelegationkey");
request.AddHeader("x-ms-version", "2019-12-12");
request.AddHeader("x-ms-version", c_APIVersion);
if (options.Timeout.HasValue())
{
request.AddQueryParameter("timeout", std::to_string(options.Timeout.GetValue()));
@ -1608,7 +1610,7 @@ 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", "2019-12-12");
request.AddHeader("x-ms-version", c_APIVersion);
if (options.Timeout.HasValue())
{
request.AddQueryParameter("timeout", std::to_string(options.Timeout.GetValue()));
@ -1684,7 +1686,7 @@ namespace Azure { namespace Storage { namespace Blobs {
unused(body);
auto request = Azure::Core::Http::Request(Azure::Core::Http::HttpMethod::Delete, url);
request.AddQueryParameter("restype", "container");
request.AddHeader("x-ms-version", "2019-12-12");
request.AddHeader("x-ms-version", c_APIVersion);
if (options.Timeout.HasValue())
{
request.AddQueryParameter("timeout", std::to_string(options.Timeout.GetValue()));
@ -1752,7 +1754,7 @@ namespace Azure { namespace Storage { namespace Blobs {
unused(body);
auto request = Azure::Core::Http::Request(Azure::Core::Http::HttpMethod::Head, url);
request.AddQueryParameter("restype", "container");
request.AddHeader("x-ms-version", "2019-12-12");
request.AddHeader("x-ms-version", c_APIVersion);
if (options.Timeout.HasValue())
{
request.AddQueryParameter("timeout", std::to_string(options.Timeout.GetValue()));
@ -1852,7 +1854,7 @@ namespace Azure { namespace Storage { namespace Blobs {
request.AddHeader("Content-Length", "0");
request.AddQueryParameter("restype", "container");
request.AddQueryParameter("comp", "metadata");
request.AddHeader("x-ms-version", "2019-12-12");
request.AddHeader("x-ms-version", c_APIVersion);
if (options.Timeout.HasValue())
{
request.AddQueryParameter("timeout", std::to_string(options.Timeout.GetValue()));
@ -1931,7 +1933,7 @@ namespace Azure { namespace Storage { namespace Blobs {
{
unused(body);
auto request = Azure::Core::Http::Request(Azure::Core::Http::HttpMethod::Get, url);
request.AddHeader("x-ms-version", "2019-12-12");
request.AddHeader("x-ms-version", c_APIVersion);
if (options.Timeout.HasValue())
{
request.AddQueryParameter("timeout", std::to_string(options.Timeout.GetValue()));
@ -2022,7 +2024,7 @@ namespace Azure { namespace Storage { namespace Blobs {
{
unused(body);
auto request = Azure::Core::Http::Request(Azure::Core::Http::HttpMethod::Get, url);
request.AddHeader("x-ms-version", "2019-12-12");
request.AddHeader("x-ms-version", c_APIVersion);
if (options.Timeout.HasValue())
{
request.AddQueryParameter("timeout", std::to_string(options.Timeout.GetValue()));
@ -2726,7 +2728,7 @@ namespace Azure { namespace Storage { namespace Blobs {
{
unused(body);
auto request = Azure::Core::Http::Request(Azure::Core::Http::HttpMethod::Get, url);
request.AddHeader("x-ms-version", "2019-12-12");
request.AddHeader("x-ms-version", c_APIVersion);
if (options.Timeout.HasValue())
{
request.AddQueryParameter("timeout", std::to_string(options.Timeout.GetValue()));
@ -2935,7 +2937,7 @@ namespace Azure { namespace Storage { namespace Blobs {
{
unused(body);
auto request = Azure::Core::Http::Request(Azure::Core::Http::HttpMethod::Delete, url);
request.AddHeader("x-ms-version", "2019-12-12");
request.AddHeader("x-ms-version", c_APIVersion);
if (options.Timeout.HasValue())
{
request.AddQueryParameter("timeout", std::to_string(options.Timeout.GetValue()));
@ -3012,7 +3014,7 @@ namespace Azure { namespace Storage { namespace Blobs {
unused(body);
auto request = Azure::Core::Http::Request(Azure::Core::Http::HttpMethod::Put, url);
request.AddHeader("Content-Length", "0");
request.AddHeader("x-ms-version", "2019-12-12");
request.AddHeader("x-ms-version", c_APIVersion);
if (options.Timeout.HasValue())
{
request.AddQueryParameter("timeout", std::to_string(options.Timeout.GetValue()));
@ -3069,7 +3071,7 @@ namespace Azure { namespace Storage { namespace Blobs {
{
unused(body);
auto request = Azure::Core::Http::Request(Azure::Core::Http::HttpMethod::Head, url);
request.AddHeader("x-ms-version", "2019-12-12");
request.AddHeader("x-ms-version", c_APIVersion);
if (options.Timeout.HasValue())
{
request.AddQueryParameter("timeout", std::to_string(options.Timeout.GetValue()));
@ -3290,7 +3292,7 @@ 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", "2019-12-12");
request.AddHeader("x-ms-version", c_APIVersion);
if (options.Timeout.HasValue())
{
request.AddQueryParameter("timeout", std::to_string(options.Timeout.GetValue()));
@ -3416,7 +3418,7 @@ 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", "2019-12-12");
request.AddHeader("x-ms-version", c_APIVersion);
if (options.Timeout.HasValue())
{
request.AddQueryParameter("timeout", std::to_string(options.Timeout.GetValue()));
@ -3518,7 +3520,7 @@ 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", "tier");
request.AddHeader("x-ms-version", "2019-12-12");
request.AddHeader("x-ms-version", c_APIVersion);
if (options.Timeout.HasValue())
{
request.AddQueryParameter("timeout", std::to_string(options.Timeout.GetValue()));
@ -3591,7 +3593,7 @@ namespace Azure { namespace Storage { namespace Blobs {
unused(body);
auto request = Azure::Core::Http::Request(Azure::Core::Http::HttpMethod::Put, url);
request.AddHeader("Content-Length", "0");
request.AddHeader("x-ms-version", "2019-12-12");
request.AddHeader("x-ms-version", c_APIVersion);
if (options.Timeout.HasValue())
{
request.AddQueryParameter("timeout", std::to_string(options.Timeout.GetValue()));
@ -3716,7 +3718,7 @@ namespace Azure { namespace Storage { namespace Blobs {
unused(body);
auto request = Azure::Core::Http::Request(Azure::Core::Http::HttpMethod::Put, url);
request.AddHeader("Content-Length", "0");
request.AddHeader("x-ms-version", "2019-12-12");
request.AddHeader("x-ms-version", c_APIVersion);
if (options.Timeout.HasValue())
{
request.AddQueryParameter("timeout", std::to_string(options.Timeout.GetValue()));
@ -3785,7 +3787,7 @@ 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", "snapshot");
request.AddHeader("x-ms-version", "2019-12-12");
request.AddHeader("x-ms-version", c_APIVersion);
if (options.Timeout.HasValue())
{
request.AddQueryParameter("timeout", std::to_string(options.Timeout.GetValue()));
@ -3916,7 +3918,7 @@ namespace Azure { namespace Storage { namespace Blobs {
auto request
= Azure::Core::Http::Request(Azure::Core::Http::HttpMethod::Put, url, body.get());
request.AddHeader("Content-Length", std::to_string(body->Length()));
request.AddHeader("x-ms-version", "2019-12-12");
request.AddHeader("x-ms-version", c_APIVersion);
if (options.Timeout.HasValue())
{
request.AddQueryParameter("timeout", std::to_string(options.Timeout.GetValue()));
@ -4087,7 +4089,7 @@ namespace Azure { namespace Storage { namespace Blobs {
request.AddHeader("Content-Length", std::to_string(body->Length()));
request.AddQueryParameter("comp", "block");
request.AddQueryParameter("blockid", options.BlockId);
request.AddHeader("x-ms-version", "2019-12-12");
request.AddHeader("x-ms-version", c_APIVersion);
if (options.Timeout.HasValue())
{
request.AddQueryParameter("timeout", std::to_string(options.Timeout.GetValue()));
@ -4201,7 +4203,7 @@ namespace Azure { namespace Storage { namespace Blobs {
request.AddHeader("Content-Length", "0");
request.AddQueryParameter("comp", "block");
request.AddQueryParameter("blockid", options.BlockId);
request.AddHeader("x-ms-version", "2019-12-12");
request.AddHeader("x-ms-version", c_APIVersion);
if (options.Timeout.HasValue())
{
request.AddQueryParameter("timeout", std::to_string(options.Timeout.GetValue()));
@ -4357,7 +4359,7 @@ namespace Azure { namespace Storage { namespace Blobs {
= Azure::Core::Http::Request(Azure::Core::Http::HttpMethod::Put, url, body.get());
request.AddHeader("Content-Length", std::to_string(body->Length()));
request.AddQueryParameter("comp", "blocklist");
request.AddHeader("x-ms-version", "2019-12-12");
request.AddHeader("x-ms-version", c_APIVersion);
if (options.Timeout.HasValue())
{
request.AddQueryParameter("timeout", std::to_string(options.Timeout.GetValue()));
@ -4506,7 +4508,7 @@ namespace Azure { namespace Storage { namespace Blobs {
= BlockListTypeOptionToString(options.ListType.GetValue());
request.AddQueryParameter("blocklisttype", block_list_type_option);
}
request.AddHeader("x-ms-version", "2019-12-12");
request.AddHeader("x-ms-version", c_APIVersion);
if (options.Timeout.HasValue())
{
request.AddQueryParameter("timeout", std::to_string(options.Timeout.GetValue()));
@ -4746,7 +4748,7 @@ namespace Azure { namespace Storage { namespace Blobs {
unused(body);
auto request = Azure::Core::Http::Request(Azure::Core::Http::HttpMethod::Put, url);
request.AddHeader("Content-Length", "0");
request.AddHeader("x-ms-version", "2019-12-12");
request.AddHeader("x-ms-version", c_APIVersion);
if (options.Timeout.HasValue())
{
request.AddQueryParameter("timeout", std::to_string(options.Timeout.GetValue()));
@ -4919,7 +4921,7 @@ namespace Azure { namespace Storage { namespace Blobs {
= Azure::Core::Http::Request(Azure::Core::Http::HttpMethod::Put, url, body.get());
request.AddHeader("Content-Length", std::to_string(body->Length()));
request.AddQueryParameter("comp", "page");
request.AddHeader("x-ms-version", "2019-12-12");
request.AddHeader("x-ms-version", c_APIVersion);
if (options.Timeout.HasValue())
{
request.AddQueryParameter("timeout", std::to_string(options.Timeout.GetValue()));
@ -5078,7 +5080,7 @@ 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", "page");
request.AddHeader("x-ms-version", "2019-12-12");
request.AddHeader("x-ms-version", c_APIVersion);
if (options.Timeout.HasValue())
{
request.AddQueryParameter("timeout", std::to_string(options.Timeout.GetValue()));
@ -5236,7 +5238,7 @@ 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", "page");
request.AddHeader("x-ms-version", "2019-12-12");
request.AddHeader("x-ms-version", c_APIVersion);
if (options.Timeout.HasValue())
{
request.AddQueryParameter("timeout", std::to_string(options.Timeout.GetValue()));
@ -5371,7 +5373,7 @@ 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", "2019-12-12");
request.AddHeader("x-ms-version", c_APIVersion);
if (options.Timeout.HasValue())
{
request.AddQueryParameter("timeout", std::to_string(options.Timeout.GetValue()));
@ -5489,7 +5491,7 @@ namespace Azure { namespace Storage { namespace Blobs {
{
request.AddQueryParameter("prevsnapshot", options.PreviousSnapshot.GetValue());
}
request.AddHeader("x-ms-version", "2019-12-12");
request.AddHeader("x-ms-version", c_APIVersion);
if (options.Timeout.HasValue())
{
request.AddQueryParameter("timeout", std::to_string(options.Timeout.GetValue()));
@ -5606,7 +5608,7 @@ 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", "incrementalcopy");
request.AddHeader("x-ms-version", "2019-12-12");
request.AddHeader("x-ms-version", c_APIVersion);
if (options.Timeout.HasValue())
{
request.AddQueryParameter("timeout", std::to_string(options.Timeout.GetValue()));
@ -5858,7 +5860,7 @@ namespace Azure { namespace Storage { namespace Blobs {
unused(body);
auto request = Azure::Core::Http::Request(Azure::Core::Http::HttpMethod::Put, url);
request.AddHeader("Content-Length", "0");
request.AddHeader("x-ms-version", "2019-12-12");
request.AddHeader("x-ms-version", c_APIVersion);
if (options.Timeout.HasValue())
{
request.AddQueryParameter("timeout", std::to_string(options.Timeout.GetValue()));
@ -6019,7 +6021,7 @@ namespace Azure { namespace Storage { namespace Blobs {
= Azure::Core::Http::Request(Azure::Core::Http::HttpMethod::Put, url, body.get());
request.AddHeader("Content-Length", std::to_string(body->Length()));
request.AddQueryParameter("comp", "appendblock");
request.AddHeader("x-ms-version", "2019-12-12");
request.AddHeader("x-ms-version", c_APIVersion);
if (options.Timeout.HasValue())
{
request.AddQueryParameter("timeout", std::to_string(options.Timeout.GetValue()));
@ -6164,7 +6166,7 @@ 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", "appendblock");
request.AddHeader("x-ms-version", "2019-12-12");
request.AddHeader("x-ms-version", c_APIVersion);
if (options.Timeout.HasValue())
{
request.AddQueryParameter("timeout", std::to_string(options.Timeout.GetValue()));

View File

@ -0,0 +1,268 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// SPDX-License-Identifier: MIT
#pragma once
#include <string>
#include "common/constants.hpp"
#include "common/storage_credential.hpp"
#include "nullable.hpp"
namespace Azure { namespace Storage {
/**
* @brief Defines the protocols permitted for Storage requests made with a shared access
* signature.
*/
enum class SasProtocol
{
/**
* @brief Only requests issued over HTTPS or HTTP will be permitted.
*/
HttpsAndHtttp,
/**
* @brief Only requests issued over HTTPS will be permitted.
*/
HttpsOnly,
};
/**
* @brief Specifies the resource types accessible from an account level shared access
* signature.
*/
enum class AccountSasResource
{
/**
* @brief Indicates whether service-level APIs are accessible from this shared access
* signature.
*/
Service = 1,
/**
* @brief Indicates whether blob container-level APIs are accessible from this shared
* access signature.
*/
Container = 2,
/**
* @brief Indicates whether object-level APIs for blobs, queue messages, and files are
* accessible from this shared access signature.
*/
Object = 4,
/**
* @brief Indicates all service-level APIs are accessible from this shared access
* signature.
*/
All = ~0,
};
inline AccountSasResource operator|(AccountSasResource lhs, AccountSasResource rhs)
{
using type = std::underlying_type_t<AccountSasResource>;
return static_cast<AccountSasResource>(static_cast<type>(lhs) | static_cast<type>(rhs));
}
inline AccountSasResource operator&(AccountSasResource lhs, AccountSasResource rhs)
{
using type = std::underlying_type_t<AccountSasResource>;
return static_cast<AccountSasResource>(static_cast<type>(lhs) & static_cast<type>(rhs));
}
/**
* @brief Specifies the services accessible from an account level shared access signature.
*/
enum class AccountSasServices
{
/**
* @brief Indicates whether Azure Blob Storage resources are accessible from the shared
* access signature.
*/
Blobs = 1,
/**
* @brief Indicates whether Azure Queue Storage resources are accessible from the shared
* access signature.
*/
Queue = 2,
/**
* @brief Indicates whether Azure File Storage resources are accessible from the shared
* access signature.
*/
Files = 4,
/**
* @brief Indicates all services are accessible from the shared
* access signature.
*/
All = ~0,
};
inline AccountSasServices operator|(AccountSasServices lhs, AccountSasServices rhs)
{
using type = std::underlying_type_t<AccountSasServices>;
return static_cast<AccountSasServices>(static_cast<type>(lhs) | static_cast<type>(rhs));
}
inline AccountSasServices operator&(AccountSasServices lhs, AccountSasServices rhs)
{
using type = std::underlying_type_t<AccountSasServices>;
return static_cast<AccountSasServices>(static_cast<type>(lhs) & static_cast<type>(rhs));
}
/**
* @brief The list of permissions that can be set for an account's access policy.
*/
enum class AccountSasPermissions
{
/**
* @brief Indicates that Read is permitted.
*/
Read = 1,
/**
* @brief Indicates that Write is permitted.
*/
Write = 2,
/**
* @brief Indicates that Delete is permitted.
*/
Delete = 4,
/**
* @brief Indicates that deleting previous blob version is permitted.
*/
DeleteVersion = 8,
/**
* @brief Indicates that List is permitted.
*/
List = 16,
/**
* @brief Indicates that Add is permitted.
*/
Add = 32,
/**
* @brief Indicates that Create is permitted.
*/
Create = 64,
/**
* @brief Indicates that Update is permitted.
*/
Update = 128,
/**
* @brief Indicates that Process is permitted.
*/
Process = 256,
/**
* @brief Indicates that reading and writing tags is permitted.
*/
Tags = 512,
/**
* @brief Indicates that filtering by tags is permitted.
*/
Filter = 1024,
/**
* @brief Indicates that all permissions are set.
*/
All = ~0,
};
inline AccountSasPermissions operator|(AccountSasPermissions lhs, AccountSasPermissions rhs)
{
using type = std::underlying_type_t<AccountSasPermissions>;
return static_cast<AccountSasPermissions>(static_cast<type>(lhs) | static_cast<type>(rhs));
}
inline AccountSasPermissions operator&(AccountSasPermissions lhs, AccountSasPermissions rhs)
{
using type = std::underlying_type_t<AccountSasPermissions>;
return static_cast<AccountSasPermissions>(static_cast<type>(lhs) & static_cast<type>(rhs));
}
/**
* @brief AccountSasBuilder is used to generate an account level Shared Access Signature
* (SAS) for Azure Storage services.
*/
struct AccountSasBuilder
{
/**
* @brief The storage service version to use to authenticate requests made with this
* shared access signature, and the service version to use when handling requests made with this
* shared access signature.
*/
std::string Version = Details::c_defaultSasVersion;
/**
* @brief The optional signed protocol field specifies the protocol permitted for a
* request made with the SAS.
*/
SasProtocol Protocol = SasProtocol::HttpsOnly;
/**
* @brief Optionally specify the time at which the shared access signature becomes
* valid.
*/
Azure::Core::Nullable<std::string> StartsOn;
/**
* @brief The time at which the shared access signature becomes invalid. This field must
* be omitted if it has been specified in an associated stored access policy.
*/
std::string ExpiresOn;
/**
* @brief Specifies an IP address or a range of IP addresses from which to accept
* requests. If the IP address from which the request originates does not match the IP address
* or address range specified on the SAS token, the request is not authenticated. When
* specifying a range of IP addresses, note that the range is inclusive.
*/
Azure::Core::Nullable<std::string> IPRange;
/**
* @brief The services associated with the shared access signature. The user is
* restricted to operations with the specified services.
*/
AccountSasServices Services;
/**
* The resource types associated with the shared access signature. The user is
* restricted to operations on the specified resources.
*/
AccountSasResource ResourceTypes;
/**
* @brief Sets the permissions for an account SAS.
*
* @param permissions The
* allowed permissions.
*/
void SetPermissions(AccountSasPermissions permissions);
/**
* @brief Uses the SharedKeyCredential to sign this shared access signature, to produce
* the proper SAS query parameters for authentication requests.
*
* @param credential
* The storage account's shared key credential.
* @return The SAS query parameters used for
* authenticating requests.
*/
std::string ToSasQueryParameters(const SharedKeyCredential& credential);
private:
std::string Permissions;
};
}} // namespace Azure::Storage

View File

@ -17,4 +17,5 @@ namespace Azure { namespace Storage { namespace Details {
constexpr static const char* c_HttpHeaderRequestId = "x-ms-request-id";
constexpr static const char* c_HttpHeaderClientRequestId = "x-ms-client-request-id";
constexpr static const char* c_HttpHeaderContentType = "content-type";
constexpr static const char* c_defaultSasVersion = "2019-12-12";
}}} // namespace Azure::Storage::Details

View File

@ -11,6 +11,12 @@
#include <string>
namespace Azure { namespace Storage {
struct AccountSasBuilder;
namespace Blobs {
struct BlobSasBuilder;
}
struct SharedKeyCredential
{
explicit SharedKeyCredential(std::string accountName, std::string accountKey)
@ -24,17 +30,19 @@ namespace Azure { namespace Storage {
m_accountKey = std::move(accountKey);
}
std::string AccountName;
const std::string AccountName;
private:
friend class SharedKeyPolicy;
std::string GetAccountKey()
friend struct Blobs::BlobSasBuilder;
friend struct AccountSasBuilder;
std::string GetAccountKey() const
{
std::lock_guard<std::mutex> guard(m_mutex);
return m_accountKey;
}
std::mutex m_mutex;
mutable std::mutex m_mutex;
std::string m_accountKey;
};

View File

@ -0,0 +1,173 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// SPDX-License-Identifier: MIT
#include "blobs/blob_sas_builder.hpp"
#include "common/crypt.hpp"
#include "common/storage_uri_builder.hpp"
namespace Azure { namespace Storage { namespace Blobs {
void BlobSasBuilder::SetPermissions(BlobContainerSasPermissions permissions)
{
Permissions.clear();
// The order matters
if ((permissions & BlobContainerSasPermissions::Read) == BlobContainerSasPermissions::Read)
{
Permissions += "r";
}
if ((permissions & BlobContainerSasPermissions::Add) == BlobContainerSasPermissions::Add)
{
Permissions += "a";
}
if ((permissions & BlobContainerSasPermissions::Create) == BlobContainerSasPermissions::Create)
{
Permissions += "c";
}
if ((permissions & BlobContainerSasPermissions::Write) == BlobContainerSasPermissions::Write)
{
Permissions += "w";
}
if ((permissions & BlobContainerSasPermissions::Delete) == BlobContainerSasPermissions::Delete)
{
Permissions += "d";
}
if ((permissions & BlobContainerSasPermissions::DeleteVersion)
== BlobContainerSasPermissions::DeleteVersion)
{
Permissions += "x";
}
if ((permissions & BlobContainerSasPermissions::List) == BlobContainerSasPermissions::List)
{
Permissions += "l";
}
if ((permissions & BlobContainerSasPermissions::Tags) == BlobContainerSasPermissions::Tags)
{
Permissions += "t";
}
}
void BlobSasBuilder::SetPermissions(BlobSasPermissions permissions)
{
Permissions.clear();
// The order matters
if ((permissions & BlobSasPermissions::Read) == BlobSasPermissions::Read)
{
Permissions += "r";
}
if ((permissions & BlobSasPermissions::Add) == BlobSasPermissions::Add)
{
Permissions += "a";
}
if ((permissions & BlobSasPermissions::Create) == BlobSasPermissions::Create)
{
Permissions += "c";
}
if ((permissions & BlobSasPermissions::Write) == BlobSasPermissions::Write)
{
Permissions += "w";
}
if ((permissions & BlobSasPermissions::Delete) == BlobSasPermissions::Delete)
{
Permissions += "d";
}
if ((permissions & BlobSasPermissions::DeleteVersion) == BlobSasPermissions::DeleteVersion)
{
Permissions += "x";
}
if ((permissions & BlobSasPermissions::Tags) == BlobSasPermissions::Tags)
{
Permissions += "t";
}
}
std::string BlobSasBuilder::ToSasQueryParameters(const SharedKeyCredential& credential)
{
std::string canonicalName = "/blob/" + credential.AccountName + "/" + ContainerName;
if (Resource == BlobSasResource::Blob || Resource == BlobSasResource::BlobSnapshot)
{
canonicalName += "/" + BlobName;
}
std::string protocol;
if (Protocol == SasProtocol::HttpsAndHtttp)
{
protocol = "https,http";
}
else
{
protocol = "https";
}
std::string resource;
if (Resource == BlobSasResource::Container)
{
resource = "c";
}
else if (Resource == BlobSasResource::Blob)
{
resource = "b";
}
else if (Resource == BlobSasResource::BlobSnapshot)
{
resource = "bs";
}
else if (Resource == BlobSasResource::BlobVersion)
{
resource = "bv";
}
std::string stringToSign = Permissions + "\n" + StartsOn + "\n" + ExpiresOn + "\n"
+ canonicalName + "\n" + Identifier + "\n" + (IPRange.HasValue() ? IPRange.GetValue() : "")
+ "\n" + protocol + "\n" + Version + "\n" + resource + "\n" + Snapshot + "\n" + CacheControl
+ "\n" + ContentDisposition + "\n" + ContentEncoding + "\n" + ContentLanguage + "\n"
+ ContentType;
std::string signature
= Base64Encode(HMAC_SHA256(stringToSign, Base64Decode(credential.GetAccountKey())));
UriBuilder builder;
builder.AppendQuery("sv", Version);
builder.AppendQuery("spr", protocol);
builder.AppendQuery("st", StartsOn);
builder.AppendQuery("se", ExpiresOn);
if (IPRange.HasValue())
{
builder.AppendQuery("sip", IPRange.GetValue());
}
if (!Identifier.empty())
{
builder.AppendQuery("si", Identifier);
}
builder.AppendQuery("sr", resource);
builder.AppendQuery("sp", Permissions);
builder.AppendQuery("sig", signature, true);
if (!CacheControl.empty())
{
builder.AppendQuery("rscc", CacheControl);
}
if (!ContentDisposition.empty())
{
builder.AppendQuery("rscd", ContentDisposition);
}
if (!ContentEncoding.empty())
{
builder.AppendQuery("rsce", ContentEncoding);
}
if (!ContentLanguage.empty())
{
builder.AppendQuery("rscl", ContentLanguage);
}
if (!ContentType.empty())
{
builder.AppendQuery("rsct", ContentType);
}
return builder.ToString();
}
std::string BlobSasBuilder::ToSasQueryParameters(
const UserDelegationKey& userDelegationKey,
const std::string& accountName)
{
unused(userDelegationKey, accountName);
return std::string();
}
}}} // namespace Azure::Storage::Blobs

View File

@ -0,0 +1,127 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// SPDX-License-Identifier: MIT
#include "common/account_sas_builder.hpp"
#include "common/crypt.hpp"
#include "common/storage_uri_builder.hpp"
namespace Azure { namespace Storage {
void AccountSasBuilder::SetPermissions(AccountSasPermissions permissions)
{
Permissions.clear();
if ((permissions & AccountSasPermissions::Read) == AccountSasPermissions::Read)
{
Permissions += "r";
}
if ((permissions & AccountSasPermissions::Write) == AccountSasPermissions::Write)
{
Permissions += "w";
}
if ((permissions & AccountSasPermissions::Delete) == AccountSasPermissions::Delete)
{
Permissions += "d";
}
if ((permissions & AccountSasPermissions::DeleteVersion)
== AccountSasPermissions::DeleteVersion)
{
Permissions += "x";
}
if ((permissions & AccountSasPermissions::List) == AccountSasPermissions::List)
{
Permissions += "l";
}
if ((permissions & AccountSasPermissions::Add) == AccountSasPermissions::Add)
{
Permissions += "a";
}
if ((permissions & AccountSasPermissions::Create) == AccountSasPermissions::Create)
{
Permissions += "c";
}
if ((permissions & AccountSasPermissions::Update) == AccountSasPermissions::Update)
{
Permissions += "u";
}
if ((permissions & AccountSasPermissions::Process) == AccountSasPermissions::Process)
{
Permissions += "p";
}
if ((permissions & AccountSasPermissions::Tags) == AccountSasPermissions::Tags)
{
Permissions += "t";
}
if ((permissions & AccountSasPermissions::Filter) == AccountSasPermissions::Filter)
{
Permissions += "f";
}
}
std::string AccountSasBuilder::ToSasQueryParameters(const SharedKeyCredential& credential)
{
std::string protocol;
if (Protocol == SasProtocol::HttpsAndHtttp)
{
protocol = "https,http";
}
else
{
protocol = "https";
}
std::string services;
if ((Services & AccountSasServices::Blobs) == AccountSasServices::Blobs)
{
services += "b";
}
if ((Services & AccountSasServices::Queue) == AccountSasServices::Queue)
{
services += "q";
}
if ((Services & AccountSasServices::Files) == AccountSasServices::Files)
{
services += "f";
}
std::string resourceTypes;
if ((ResourceTypes & AccountSasResource::Service) == AccountSasResource::Service)
{
resourceTypes += "s";
}
if ((ResourceTypes & AccountSasResource::Container) == AccountSasResource::Container)
{
resourceTypes += "c";
}
if ((ResourceTypes & AccountSasResource::Object) == AccountSasResource::Object)
{
resourceTypes += "o";
}
std::string stringToSign = credential.AccountName + "\n" + Permissions + "\n" + services + "\n"
+ resourceTypes + "\n" + (StartsOn.HasValue() ? StartsOn.GetValue() : "") + "\n" + ExpiresOn
+ "\n" + (IPRange.HasValue() ? IPRange.GetValue() : "") + "\n" + protocol + "\n" + Version
+ "\n";
std::string signature
= Base64Encode(HMAC_SHA256(stringToSign, Base64Decode(credential.GetAccountKey())));
UriBuilder builder;
builder.AppendQuery("sv", Version);
builder.AppendQuery("ss", services);
builder.AppendQuery("srt", resourceTypes);
builder.AppendQuery("sp", Permissions);
if (StartsOn.HasValue())
{
builder.AppendQuery("st", StartsOn.GetValue());
}
builder.AppendQuery("se", ExpiresOn);
if (IPRange.HasValue())
{
builder.AppendQuery("sip", IPRange.GetValue());
}
builder.AppendQuery("spr", protocol);
builder.AppendQuery("sig", signature, true);
return builder.ToString();
}
}} // namespace Azure::Storage

View File

@ -190,7 +190,6 @@ namespace Azure { namespace Storage {
std::string full_url;
if (!m_scheme.empty())
{
full_url += m_scheme + "://";
}
full_url += m_host;

View File

@ -16,6 +16,7 @@ add_executable (
blobs/append_blob_client_test.cpp
blobs/page_blob_client_test.hpp
blobs/page_blob_client_test.cpp
blobs/blob_sas_test.cpp
blobs/performance_benchmark.cpp
blobs/large_scale_test.cpp
datalake/service_client_test.hpp

View File

@ -0,0 +1,406 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// SPDX-License-Identifier: MIT
#include "blob_container_client_test.hpp"
#include "blobs/blob_sas_builder.hpp"
namespace Azure { namespace Storage { namespace Test {
TEST_F(BlobContainerClientTest, BlobSasTest)
{
AccountSasBuilder accountSasBuilder;
accountSasBuilder.Protocol = SasProtocol::HttpsAndHtttp;
accountSasBuilder.StartsOn
= ToISO8601(std::chrono::system_clock::now() - std::chrono::minutes(5));
accountSasBuilder.ExpiresOn
= ToISO8601(std::chrono::system_clock::now() + std::chrono::minutes(60));
accountSasBuilder.Services = AccountSasServices::Blobs;
accountSasBuilder.ResourceTypes = AccountSasResource::Object | AccountSasResource::Container;
std::string blobName = RandomString();
Blobs::BlobSasBuilder blobSasBuilder;
blobSasBuilder.Protocol = SasProtocol::HttpsAndHtttp;
blobSasBuilder.StartsOn = ToISO8601(std::chrono::system_clock::now() - std::chrono::minutes(5));
blobSasBuilder.ExpiresOn
= ToISO8601(std::chrono::system_clock::now() + std::chrono::minutes(60));
blobSasBuilder.ContainerName = m_containerName;
blobSasBuilder.BlobName = blobName;
blobSasBuilder.Resource = Blobs::BlobSasResource::Blob;
Blobs::BlobSasBuilder containerSasBuilder = blobSasBuilder;
containerSasBuilder.BlobName.clear();
containerSasBuilder.Resource = Blobs::BlobSasResource::Container;
auto keyCredential
= Details::ParseConnectionString(StandardStorageConnectionString()).KeyCredential;
auto blobServiceClient0
= Blobs::BlobServiceClient::CreateFromConnectionString(StandardStorageConnectionString());
auto blobContainerClient0 = blobServiceClient0.GetBlobContainerClient(m_containerName);
auto blobClient0 = blobContainerClient0.GetAppendBlobClient(blobName);
auto serviceUri = blobServiceClient0.GetUri();
auto containerUri = blobContainerClient0.GetUri();
auto blobUri = blobClient0.GetUri();
auto verify_blob_read = [&](const std::string& sas) {
EXPECT_NO_THROW(blobClient0.Create());
auto blobClient = Blobs::AppendBlobClient(blobUri + sas);
auto downloadedContent = blobClient.Download();
EXPECT_TRUE(ReadBodyStream(downloadedContent->BodyStream).empty());
};
auto verify_blob_write = [&](const std::string& sas) {
auto blobClient = Blobs::AppendBlobClient(blobUri + sas);
EXPECT_NO_THROW(blobClient.Create());
};
auto verify_blob_delete = [&](const std::string& sas) {
blobClient0.Create();
auto blobClient = Blobs::AppendBlobClient(blobUri + sas);
EXPECT_NO_THROW(blobClient.Delete());
};
auto verify_blob_add = [&](const std::string& sas) {
blobClient0.Create();
std::string content = "Hello world";
auto blockContent = Azure::Core::Http::MemoryBodyStream(
reinterpret_cast<const uint8_t*>(content.data()), content.size());
auto blobClient = Blobs::AppendBlobClient(blobUri + sas);
EXPECT_NO_THROW(blobClient.AppendBlock(&blockContent));
};
auto verify_blob_list = [&](const std::string& sas) {
auto blobContainerClient = Blobs::BlobContainerClient(containerUri + sas);
EXPECT_NO_THROW(blobContainerClient.ListBlobsFlat());
};
auto verify_blob_create = [&](const std::string& sas) {
try
{
blobClient0.Delete();
}
catch (StorageError&)
{
}
auto blobClient = Blobs::AppendBlobClient(blobUri + sas);
blobClient.Create();
blobClient.CreateSnapshot();
Blobs::DeleteBlobOptions options;
options.DeleteSnapshots = Blobs::DeleteSnapshotsOption::IncludeSnapshots;
blobClient0.Delete(options);
};
auto verify_blob_tags = [&](const std::string& sas) {
unused(sas);
// TODO: Add test for blob tags
};
auto verify_blob_filter = [&](const std::string& sas) {
unused(sas);
// TODO: Add test for blob tags
};
auto verify_blob_delete_version = [&](const std::string& sas) {
unused(sas);
// TODO: Add test for versions
};
for (auto permissions : {
AccountSasPermissions::All,
AccountSasPermissions::Read,
AccountSasPermissions::Write,
AccountSasPermissions::Delete,
AccountSasPermissions::DeleteVersion,
AccountSasPermissions::List,
AccountSasPermissions::Add,
AccountSasPermissions::Create,
AccountSasPermissions::Tags,
AccountSasPermissions::Filter,
})
{
accountSasBuilder.SetPermissions(permissions);
auto sasToken = accountSasBuilder.ToSasQueryParameters(*keyCredential);
if ((permissions & AccountSasPermissions::Read) == AccountSasPermissions::Read)
{
verify_blob_read(sasToken);
}
if ((permissions & AccountSasPermissions::Write) == AccountSasPermissions::Write)
{
verify_blob_write(sasToken);
}
if ((permissions & AccountSasPermissions::Delete) == AccountSasPermissions::Delete)
{
verify_blob_delete(sasToken);
}
if ((permissions & AccountSasPermissions::DeleteVersion)
== AccountSasPermissions::DeleteVersion)
{
verify_blob_delete_version(sasToken);
}
if ((permissions & AccountSasPermissions::List) == AccountSasPermissions::List)
{
verify_blob_list(sasToken);
}
if ((permissions & AccountSasPermissions::Add) == AccountSasPermissions::Add)
{
verify_blob_add(sasToken);
}
if ((permissions & AccountSasPermissions::Create) == AccountSasPermissions::Create)
{
verify_blob_create(sasToken);
}
if ((permissions & AccountSasPermissions::Tags) == AccountSasPermissions::Tags)
{
verify_blob_tags(sasToken);
}
if ((permissions & AccountSasPermissions::Filter) == AccountSasPermissions::Filter)
{
verify_blob_filter(sasToken);
}
}
for (auto permissions :
{Blobs::BlobSasPermissions::All,
Blobs::BlobSasPermissions::Read,
Blobs::BlobSasPermissions::Write,
Blobs::BlobSasPermissions::Delete,
Blobs::BlobSasPermissions::Add,
Blobs::BlobSasPermissions::Create,
Blobs::BlobSasPermissions::Tags,
Blobs::BlobSasPermissions::DeleteVersion})
{
blobSasBuilder.SetPermissions(permissions);
auto sasToken = blobSasBuilder.ToSasQueryParameters(*keyCredential);
if ((permissions & Blobs::BlobSasPermissions::Read) == Blobs::BlobSasPermissions::Read)
{
verify_blob_read(sasToken);
}
if ((permissions & Blobs::BlobSasPermissions::Write) == Blobs::BlobSasPermissions::Write)
{
verify_blob_write(sasToken);
}
if ((permissions & Blobs::BlobSasPermissions::Delete) == Blobs::BlobSasPermissions::Delete)
{
verify_blob_delete(sasToken);
}
if ((permissions & Blobs::BlobSasPermissions::Add) == Blobs::BlobSasPermissions::Add)
{
verify_blob_add(sasToken);
}
if ((permissions & Blobs::BlobSasPermissions::Create) == Blobs::BlobSasPermissions::Create)
{
verify_blob_create(sasToken);
}
if ((permissions & Blobs::BlobSasPermissions::Tags) == Blobs::BlobSasPermissions::Tags)
{
verify_blob_tags(sasToken);
}
if ((permissions & Blobs::BlobSasPermissions::DeleteVersion)
== Blobs::BlobSasPermissions::DeleteVersion)
{
verify_blob_delete_version(sasToken);
}
}
// Expires
{
AccountSasBuilder builder2 = accountSasBuilder;
builder2.SetPermissions(AccountSasPermissions::All);
builder2.StartsOn = ToISO8601(std::chrono::system_clock::now() - std::chrono::minutes(5));
builder2.ExpiresOn = ToISO8601(std::chrono::system_clock::now() - std::chrono::minutes(1));
auto sasToken = builder2.ToSasQueryParameters(*keyCredential);
EXPECT_THROW(verify_blob_create(sasToken), std::runtime_error);
}
// Without start time
{
AccountSasBuilder builder2 = accountSasBuilder;
builder2.SetPermissions(AccountSasPermissions::All);
builder2.StartsOn.Reset();
auto sasToken = builder2.ToSasQueryParameters(*keyCredential);
EXPECT_NO_THROW(verify_blob_create(sasToken));
}
// IP
{
AccountSasBuilder builder2 = accountSasBuilder;
builder2.SetPermissions(AccountSasPermissions::All);
builder2.IPRange = "1.1.1.1";
auto sasToken = builder2.ToSasQueryParameters(*keyCredential);
auto blobClient = Blobs::AppendBlobClient(blobUri + sasToken);
EXPECT_THROW(verify_blob_create(sasToken), std::runtime_error);
builder2.IPRange = "0.0.0.0-255.255.255.255";
sasToken = builder2.ToSasQueryParameters(*keyCredential);
blobClient = Blobs::AppendBlobClient(blobUri + sasToken);
EXPECT_NO_THROW(verify_blob_create(sasToken));
}
// Account SAS Service
{
AccountSasBuilder builder2 = accountSasBuilder;
builder2.SetPermissions(AccountSasPermissions::All);
builder2.Services = AccountSasServices::Files;
auto sasToken = builder2.ToSasQueryParameters(*keyCredential);
auto blobClient = Blobs::AppendBlobClient(blobUri + sasToken);
EXPECT_THROW(verify_blob_create(sasToken), std::runtime_error);
builder2.Services = AccountSasServices::All;
sasToken = builder2.ToSasQueryParameters(*keyCredential);
blobClient = Blobs::AppendBlobClient(blobUri + sasToken);
EXPECT_NO_THROW(verify_blob_create(sasToken));
}
// Account SAS Resource Types
{
AccountSasBuilder builder2 = accountSasBuilder;
builder2.SetPermissions(AccountSasPermissions::All);
builder2.ResourceTypes = AccountSasResource::Service;
auto sasToken = builder2.ToSasQueryParameters(*keyCredential);
auto blobClient = Blobs::AppendBlobClient(blobUri + sasToken);
EXPECT_THROW(verify_blob_create(sasToken), std::runtime_error);
auto serviceClient = Blobs::BlobServiceClient(serviceUri + sasToken);
EXPECT_NO_THROW(serviceClient.ListBlobContainersSegment());
}
for (auto permissions :
{Blobs::BlobContainerSasPermissions::All,
Blobs::BlobContainerSasPermissions::Read,
Blobs::BlobContainerSasPermissions::Write,
Blobs::BlobContainerSasPermissions::Delete,
Blobs::BlobContainerSasPermissions::List,
Blobs::BlobContainerSasPermissions::Add,
Blobs::BlobContainerSasPermissions::Create,
Blobs::BlobContainerSasPermissions::Tags})
{
containerSasBuilder.SetPermissions(permissions);
auto sasToken = containerSasBuilder.ToSasQueryParameters(*keyCredential);
if ((permissions & Blobs::BlobContainerSasPermissions::Read)
== Blobs::BlobContainerSasPermissions::Read)
{
verify_blob_read(sasToken);
}
if ((permissions & Blobs::BlobContainerSasPermissions::Write)
== Blobs::BlobContainerSasPermissions::Write)
{
verify_blob_write(sasToken);
}
if ((permissions & Blobs::BlobContainerSasPermissions::Delete)
== Blobs::BlobContainerSasPermissions::Delete)
{
verify_blob_delete(sasToken);
}
if ((permissions & Blobs::BlobContainerSasPermissions::List)
== Blobs::BlobContainerSasPermissions::List)
{
verify_blob_list(sasToken);
}
if ((permissions & Blobs::BlobContainerSasPermissions::Add)
== Blobs::BlobContainerSasPermissions::Add)
{
verify_blob_add(sasToken);
}
if ((permissions & Blobs::BlobContainerSasPermissions::Create)
== Blobs::BlobContainerSasPermissions::Create)
{
verify_blob_create(sasToken);
}
if ((permissions & Blobs::BlobContainerSasPermissions::Tags)
== Blobs::BlobContainerSasPermissions::Tags)
{
verify_blob_tags(sasToken);
}
}
blobSasBuilder.SetPermissions(Blobs::BlobSasPermissions::All);
// Expires
{
Blobs::BlobSasBuilder builder2 = blobSasBuilder;
builder2.StartsOn = ToISO8601(std::chrono::system_clock::now() - std::chrono::minutes(5));
builder2.ExpiresOn = ToISO8601(std::chrono::system_clock::now() - std::chrono::minutes(1));
auto sasToken = builder2.ToSasQueryParameters(*keyCredential);
EXPECT_THROW(verify_blob_create(sasToken), std::runtime_error);
}
// IP
{
Blobs::BlobSasBuilder builder2 = blobSasBuilder;
builder2.IPRange = "0.0.0.0-0.0.0.1";
auto sasToken = builder2.ToSasQueryParameters(*keyCredential);
auto blobClient = Blobs::AppendBlobClient(blobUri + sasToken);
EXPECT_THROW(verify_blob_create(sasToken), std::runtime_error);
builder2.IPRange = "0.0.0.0-255.255.255.255";
sasToken = builder2.ToSasQueryParameters(*keyCredential);
blobClient = Blobs::AppendBlobClient(blobUri + sasToken);
EXPECT_NO_THROW(verify_blob_create(sasToken));
}
// response headers override
{
Blobs::BlobHttpHeaders headers;
headers.ContentType = "application/x-binary";
headers.ContentLanguage = "en-US";
headers.ContentDisposition = "attachment";
headers.CacheControl = "no-cache";
headers.ContentEncoding = "identify";
Blobs::BlobSasBuilder builder2 = blobSasBuilder;
builder2.ContentType = "application/x-binary";
builder2.ContentLanguage = "en-US";
builder2.ContentDisposition = "attachment";
builder2.CacheControl = "no-cache";
builder2.ContentEncoding = "identify";
auto sasToken = builder2.ToSasQueryParameters(*keyCredential);
auto blobClient = Blobs::AppendBlobClient(blobUri + sasToken);
blobClient0.Create();
auto p = blobClient.GetProperties();
EXPECT_EQ(p->HttpHeaders.ContentType, headers.ContentType);
EXPECT_EQ(p->HttpHeaders.ContentLanguage, headers.ContentLanguage);
EXPECT_EQ(p->HttpHeaders.ContentDisposition, headers.ContentDisposition);
EXPECT_EQ(p->HttpHeaders.CacheControl, headers.CacheControl);
EXPECT_EQ(p->HttpHeaders.ContentEncoding, headers.ContentEncoding);
}
blobClient0.Create();
Blobs::BlobSasBuilder BlobSnapshotSasBuilder = blobSasBuilder;
BlobSnapshotSasBuilder.Resource = Blobs::BlobSasResource::BlobSnapshot;
std::string blobSnapshotUri;
auto verify_blob_snapshot_read = [&](const std::string sas) {
auto blobSnapshotClient = Blobs::AppendBlobClient(blobSnapshotUri + "&" + sas.substr(1));
auto downloadedContent = blobSnapshotClient.Download();
EXPECT_TRUE(ReadBodyStream(downloadedContent->BodyStream).empty());
};
auto verify_blob_snapshot_delete = [&](const std::string sas) {
auto blobSnapshotClient = Blobs::AppendBlobClient(blobSnapshotUri + "&" + sas.substr(1));
EXPECT_NO_THROW(blobSnapshotClient.Delete());
};
for (auto permissions : {
Blobs::BlobSasPermissions::Read | Blobs::BlobSasPermissions::Delete,
Blobs::BlobSasPermissions::Read,
Blobs::BlobSasPermissions::Delete,
})
{
std::string snapshot = blobClient0.CreateSnapshot()->Snapshot;
BlobSnapshotSasBuilder.Snapshot = snapshot;
blobSnapshotUri = blobClient0.WithSnapshot(snapshot).GetUri();
BlobSnapshotSasBuilder.SetPermissions(permissions);
auto sasToken = BlobSnapshotSasBuilder.ToSasQueryParameters(*keyCredential);
if ((permissions & Blobs::BlobSasPermissions::Read) == Blobs::BlobSasPermissions::Read)
{
verify_blob_snapshot_read(sasToken);
}
if ((permissions & Blobs::BlobSasPermissions::Delete) == Blobs::BlobSasPermissions::Delete)
{
verify_blob_snapshot_delete(sasToken);
}
}
}
}}} // namespace Azure::Storage::Test

View File

@ -11,8 +11,11 @@
#include <cctype>
#include <cstdio>
#include <cstdlib>
#include <ctime>
#include <iomanip>
#include <limits>
#include <random>
#include <sstream>
#include <string>
namespace Azure { namespace Storage { namespace Test {
@ -214,4 +217,33 @@ namespace Azure { namespace Storage { namespace Test {
return result;
}
std::string ToISO8601(const std::chrono::system_clock::time_point& time_point)
{
std::time_t epoch_seconds = std::chrono::system_clock::to_time_t(time_point);
struct tm ct;
#ifdef _WIN32
gmtime_s(&ct, &epoch_seconds);
#else
gmtime_r(&epoch_seconds, &ct);
#endif
char buff[64];
std::strftime(buff, sizeof(buff), "%Y-%m-%dT%H:%M:%SZ", &ct);
return std::string(buff);
}
std::string ToRFC1123(const std::chrono::system_clock::time_point& time_point)
{
std::time_t epoch_seconds = std::chrono::system_clock::to_time_t(time_point);
struct tm ct;
#ifdef _WIN32
gmtime_s(&ct, &epoch_seconds);
#else
gmtime_r(&epoch_seconds, &ct);
#endif
std::stringstream ss;
ss.imbue(std::locale("C"));
ss << std::put_time(&ct, "%a, %d %b %Y %H:%M:%S GMT");
return ss.str();
}
}}} // namespace Azure::Storage::Test

View File

@ -9,6 +9,8 @@
#include "gtest/gtest.h"
#include <chrono>
namespace Azure { namespace Storage { namespace Test {
const std::string& StandardStorageConnectionString();
@ -59,4 +61,7 @@ namespace Azure { namespace Storage { namespace Test {
void DeleteFile(const std::string& filename);
std::string ToISO8601(const std::chrono::system_clock::time_point& time_point);
std::string ToRFC1123(const std::chrono::system_clock::time_point& time_point);
}}} // namespace Azure::Storage::Test