[Storage Blob Service] Get/SetBlobContainerAccessPolicy (#381)
* Get/SetBlobContainerAccessPolicy * Remove ResetPermissions()
This commit is contained in:
parent
18b02094ed
commit
8867c7abcb
@ -203,6 +203,32 @@ namespace Azure { namespace Storage { namespace Blobs {
|
||||
const std::string& delimiter,
|
||||
const ListBlobsOptions& options = ListBlobsOptions()) const;
|
||||
|
||||
/**
|
||||
* @brief Gets the permissions for this container. The permissions indicate whether
|
||||
* container data may be accessed publicly.
|
||||
*
|
||||
* @param options Optional parameters to
|
||||
* execute this function.
|
||||
* @return A BlobContainerAccessPolicy describing the container's
|
||||
* access policy.
|
||||
*/
|
||||
Azure::Core::Response<BlobContainerAccessPolicy> GetAccessPolicy(
|
||||
const GetBlobContainerAccessPolicyOptions& options
|
||||
= GetBlobContainerAccessPolicyOptions()) const;
|
||||
|
||||
/**
|
||||
* @brief Sets the permissions for the specified container. The permissions indicate
|
||||
* whether blob container data may be accessed publicly.
|
||||
*
|
||||
* @param options Optional
|
||||
* parameters to execute this function.
|
||||
* @return A BlobContainerInfo describing the
|
||||
* updated container.
|
||||
*/
|
||||
Azure::Core::Response<BlobContainerInfo> SetAccessPolicy(
|
||||
const SetBlobContainerAccessPolicyOptions& options
|
||||
= SetBlobContainerAccessPolicyOptions()) const;
|
||||
|
||||
private:
|
||||
UriBuilder m_containerUrl;
|
||||
std::shared_ptr<Azure::Core::Http::HttpPipeline> m_pipeline;
|
||||
|
||||
@ -306,6 +306,50 @@ namespace Azure { namespace Storage { namespace Blobs {
|
||||
ListBlobsIncludeItem Include = ListBlobsIncludeItem::None;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Optional parameters for BlobContainerClient::GetAccessPolicy.
|
||||
*/
|
||||
struct GetBlobContainerAccessPolicyOptions
|
||||
{
|
||||
/**
|
||||
* @brief Context for cancelling long running operations.
|
||||
*/
|
||||
Azure::Core::Context Context;
|
||||
|
||||
/**
|
||||
* @brief Optional conditions that must be met to perform this operation.
|
||||
*/
|
||||
LeaseAccessConditions AccessConditions;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Optional parameters for BlobContainerClient::SetAccessPolicy.
|
||||
*/
|
||||
struct SetBlobContainerAccessPolicyOptions
|
||||
{
|
||||
/**
|
||||
* @brief Context for cancelling long running operations.
|
||||
*/
|
||||
Azure::Core::Context Context;
|
||||
|
||||
/**
|
||||
* @brief Specifies whether data in the container may be accessed publicly and the level
|
||||
* of access.
|
||||
*/
|
||||
Azure::Core::Nullable<PublicAccessType> AccessType;
|
||||
|
||||
/**
|
||||
* @brief Stored access policies that you can use to provide fine grained control over
|
||||
* container permissions.
|
||||
*/
|
||||
std::vector<BlobSignedIdentifier> SignedIdentifiers;
|
||||
|
||||
/**
|
||||
* @brief Optional conditions that must be met to perform this operation.
|
||||
*/
|
||||
ContainerAccessConditions AccessConditions;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Blob client options used to initalize BlobClient.
|
||||
*/
|
||||
|
||||
@ -109,6 +109,8 @@ namespace Azure { namespace Storage { namespace Blobs {
|
||||
static_cast<type>(lhs) & static_cast<type>(rhs));
|
||||
}
|
||||
|
||||
std::string BlobContainerSasPermissionsToString(BlobContainerSasPermissions permissions);
|
||||
|
||||
/**
|
||||
* @brief The list of permissions that can be set for a blob's access policy.
|
||||
*/
|
||||
@ -271,7 +273,10 @@ namespace Azure { namespace Storage { namespace Blobs {
|
||||
* @param
|
||||
* permissions The allowed permissions.
|
||||
*/
|
||||
void SetPermissions(BlobContainerSasPermissions permissions);
|
||||
void SetPermissions(BlobContainerSasPermissions permissions)
|
||||
{
|
||||
Permissions = BlobContainerSasPermissionsToString(permissions);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the permissions for the blob SAS.
|
||||
|
||||
@ -478,6 +478,14 @@ namespace Azure { namespace Storage { namespace Blobs {
|
||||
Azure::Core::Nullable<int32_t> Days;
|
||||
}; // struct BlobRetentionPolicy
|
||||
|
||||
struct BlobSignedIdentifier
|
||||
{
|
||||
std::string Id;
|
||||
std::string StartsOn;
|
||||
std::string ExpiresOn;
|
||||
std::string Permissions;
|
||||
}; // struct BlobSignedIdentifier
|
||||
|
||||
struct BlobSnapshotInfo
|
||||
{
|
||||
std::string Snapshot;
|
||||
@ -1081,6 +1089,14 @@ namespace Azure { namespace Storage { namespace Blobs {
|
||||
std::vector<BlobBlock> UncommittedBlocks;
|
||||
}; // struct BlobBlockListInfo
|
||||
|
||||
struct BlobContainerAccessPolicy
|
||||
{
|
||||
PublicAccessType AccessType;
|
||||
std::string ETag;
|
||||
std::string LastModified;
|
||||
std::vector<BlobSignedIdentifier> SignedIdentifiers;
|
||||
}; // struct BlobContainerAccessPolicy
|
||||
|
||||
struct BlobContainerItem
|
||||
{
|
||||
std::string Name;
|
||||
@ -3093,7 +3109,177 @@ namespace Azure { namespace Storage { namespace Blobs {
|
||||
std::move(response), std::move(pHttpResponse));
|
||||
}
|
||||
|
||||
struct GetAccessPolicyOptions
|
||||
{
|
||||
Azure::Core::Nullable<int32_t> Timeout;
|
||||
Azure::Core::Nullable<std::string> LeaseId;
|
||||
}; // struct GetAccessPolicyOptions
|
||||
|
||||
static Azure::Core::Response<BlobContainerAccessPolicy> GetAccessPolicy(
|
||||
Azure::Core::Context context,
|
||||
Azure::Core::Http::HttpPipeline& pipeline,
|
||||
const std::string& url,
|
||||
const GetAccessPolicyOptions& options)
|
||||
{
|
||||
unused(options);
|
||||
auto request = Azure::Core::Http::Request(Azure::Core::Http::HttpMethod::Get, url);
|
||||
request.AddHeader("x-ms-version", c_APIVersion);
|
||||
if (options.Timeout.HasValue())
|
||||
{
|
||||
request.AddQueryParameter("timeout", std::to_string(options.Timeout.GetValue()));
|
||||
}
|
||||
request.AddQueryParameter("restype", "container");
|
||||
request.AddQueryParameter("comp", "acl");
|
||||
auto pHttpResponse = pipeline.Send(context, request);
|
||||
Azure::Core::Http::RawResponse& httpResponse = *pHttpResponse;
|
||||
BlobContainerAccessPolicy response;
|
||||
auto http_status_code
|
||||
= static_cast<std::underlying_type<Azure::Core::Http::HttpStatusCode>::type>(
|
||||
httpResponse.GetStatusCode());
|
||||
if (!(http_status_code == 200))
|
||||
{
|
||||
throw StorageError::CreateFromResponse(context, std::move(pHttpResponse));
|
||||
}
|
||||
{
|
||||
const auto& httpResponseBody = httpResponse.GetBody();
|
||||
XmlReader reader(
|
||||
reinterpret_cast<const char*>(httpResponseBody.data()), httpResponseBody.size());
|
||||
response = BlobContainerAccessPolicyFromXml(reader);
|
||||
}
|
||||
response.ETag = httpResponse.GetHeaders().at("etag");
|
||||
response.LastModified = httpResponse.GetHeaders().at("last-modified");
|
||||
response.AccessType
|
||||
= PublicAccessTypeFromString(httpResponse.GetHeaders().at("x-ms-blob-public-access"));
|
||||
return Azure::Core::Response<BlobContainerAccessPolicy>(
|
||||
std::move(response), std::move(pHttpResponse));
|
||||
}
|
||||
|
||||
struct SetAccessPolicyOptions
|
||||
{
|
||||
Azure::Core::Nullable<int32_t> Timeout;
|
||||
Azure::Core::Nullable<PublicAccessType> AccessType;
|
||||
Azure::Core::Nullable<std::string> LeaseId;
|
||||
Azure::Core::Nullable<std::string> IfModifiedSince;
|
||||
Azure::Core::Nullable<std::string> IfUnmodifiedSince;
|
||||
std::vector<BlobSignedIdentifier> SignedIdentifiers;
|
||||
}; // struct SetAccessPolicyOptions
|
||||
|
||||
static Azure::Core::Response<BlobContainerInfo> SetAccessPolicy(
|
||||
Azure::Core::Context context,
|
||||
Azure::Core::Http::HttpPipeline& pipeline,
|
||||
const std::string& url,
|
||||
const SetAccessPolicyOptions& options)
|
||||
{
|
||||
unused(options);
|
||||
std::string xml_body;
|
||||
{
|
||||
XmlWriter writer;
|
||||
SetAccessPolicyOptionsToXml(writer, options);
|
||||
xml_body = writer.GetDocument();
|
||||
writer.Write(XmlNode{XmlNodeType::End});
|
||||
}
|
||||
Azure::Core::Http::MemoryBodyStream xml_body_stream(
|
||||
reinterpret_cast<const uint8_t*>(xml_body.data()), xml_body.length());
|
||||
auto request
|
||||
= Azure::Core::Http::Request(Azure::Core::Http::HttpMethod::Put, url, &xml_body_stream);
|
||||
request.AddHeader("Content-Length", std::to_string(xml_body_stream.Length()));
|
||||
request.AddHeader("x-ms-version", c_APIVersion);
|
||||
if (options.Timeout.HasValue())
|
||||
{
|
||||
request.AddQueryParameter("timeout", std::to_string(options.Timeout.GetValue()));
|
||||
}
|
||||
request.AddQueryParameter("restype", "container");
|
||||
request.AddQueryParameter("comp", "acl");
|
||||
if (options.AccessType.HasValue())
|
||||
{
|
||||
request.AddHeader(
|
||||
"x-ms-blob-public-access", PublicAccessTypeToString(options.AccessType.GetValue()));
|
||||
}
|
||||
if (options.LeaseId.HasValue())
|
||||
{
|
||||
request.AddHeader("x-ms-lease-id", options.LeaseId.GetValue());
|
||||
}
|
||||
if (options.IfModifiedSince.HasValue())
|
||||
{
|
||||
request.AddHeader("If-Modified-Since", options.IfModifiedSince.GetValue());
|
||||
}
|
||||
if (options.IfUnmodifiedSince.HasValue())
|
||||
{
|
||||
request.AddHeader("If-Unmodified-Since", options.IfUnmodifiedSince.GetValue());
|
||||
}
|
||||
auto pHttpResponse = pipeline.Send(context, request);
|
||||
Azure::Core::Http::RawResponse& httpResponse = *pHttpResponse;
|
||||
BlobContainerInfo response;
|
||||
auto http_status_code
|
||||
= static_cast<std::underlying_type<Azure::Core::Http::HttpStatusCode>::type>(
|
||||
httpResponse.GetStatusCode());
|
||||
if (!(http_status_code == 200))
|
||||
{
|
||||
throw StorageError::CreateFromResponse(context, std::move(pHttpResponse));
|
||||
}
|
||||
response.ETag = httpResponse.GetHeaders().at("etag");
|
||||
response.LastModified = httpResponse.GetHeaders().at("last-modified");
|
||||
return Azure::Core::Response<BlobContainerInfo>(
|
||||
std::move(response), std::move(pHttpResponse));
|
||||
}
|
||||
|
||||
private:
|
||||
static BlobContainerAccessPolicy BlobContainerAccessPolicyFromXml(XmlReader& reader)
|
||||
{
|
||||
BlobContainerAccessPolicy ret;
|
||||
enum class XmlTagName
|
||||
{
|
||||
k_SignedIdentifiers,
|
||||
k_SignedIdentifier,
|
||||
k_Unknown,
|
||||
};
|
||||
std::vector<XmlTagName> path;
|
||||
while (true)
|
||||
{
|
||||
auto node = reader.Read();
|
||||
if (node.Type == XmlNodeType::End)
|
||||
{
|
||||
break;
|
||||
}
|
||||
else if (node.Type == XmlNodeType::EndTag)
|
||||
{
|
||||
if (path.size() > 0)
|
||||
{
|
||||
path.pop_back();
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (node.Type == XmlNodeType::StartTag)
|
||||
{
|
||||
if (std::strcmp(node.Name, "SignedIdentifiers") == 0)
|
||||
{
|
||||
path.emplace_back(XmlTagName::k_SignedIdentifiers);
|
||||
}
|
||||
else if (std::strcmp(node.Name, "SignedIdentifier") == 0)
|
||||
{
|
||||
path.emplace_back(XmlTagName::k_SignedIdentifier);
|
||||
}
|
||||
else
|
||||
{
|
||||
path.emplace_back(XmlTagName::k_Unknown);
|
||||
}
|
||||
if (path.size() == 2 && path[0] == XmlTagName::k_SignedIdentifiers
|
||||
&& path[1] == XmlTagName::k_SignedIdentifier)
|
||||
{
|
||||
ret.SignedIdentifiers.emplace_back(BlobSignedIdentifierFromXml(reader));
|
||||
path.pop_back();
|
||||
}
|
||||
}
|
||||
else if (node.Type == XmlNodeType::Text)
|
||||
{
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static BlobsFlatSegment BlobsFlatSegmentFromXml(XmlReader& reader)
|
||||
{
|
||||
BlobsFlatSegment ret;
|
||||
@ -3660,6 +3846,93 @@ namespace Azure { namespace Storage { namespace Blobs {
|
||||
return ret;
|
||||
}
|
||||
|
||||
static BlobSignedIdentifier BlobSignedIdentifierFromXml(XmlReader& reader)
|
||||
{
|
||||
BlobSignedIdentifier ret;
|
||||
enum class XmlTagName
|
||||
{
|
||||
k_Id,
|
||||
k_AccessPolicy,
|
||||
k_Start,
|
||||
k_Expiry,
|
||||
k_Permission,
|
||||
k_Unknown,
|
||||
};
|
||||
std::vector<XmlTagName> path;
|
||||
while (true)
|
||||
{
|
||||
auto node = reader.Read();
|
||||
if (node.Type == XmlNodeType::End)
|
||||
{
|
||||
break;
|
||||
}
|
||||
else if (node.Type == XmlNodeType::EndTag)
|
||||
{
|
||||
if (path.size() > 0)
|
||||
{
|
||||
path.pop_back();
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (node.Type == XmlNodeType::StartTag)
|
||||
{
|
||||
if (std::strcmp(node.Name, "Id") == 0)
|
||||
{
|
||||
path.emplace_back(XmlTagName::k_Id);
|
||||
}
|
||||
else if (std::strcmp(node.Name, "AccessPolicy") == 0)
|
||||
{
|
||||
path.emplace_back(XmlTagName::k_AccessPolicy);
|
||||
}
|
||||
else if (std::strcmp(node.Name, "Start") == 0)
|
||||
{
|
||||
path.emplace_back(XmlTagName::k_Start);
|
||||
}
|
||||
else if (std::strcmp(node.Name, "Expiry") == 0)
|
||||
{
|
||||
path.emplace_back(XmlTagName::k_Expiry);
|
||||
}
|
||||
else if (std::strcmp(node.Name, "Permission") == 0)
|
||||
{
|
||||
path.emplace_back(XmlTagName::k_Permission);
|
||||
}
|
||||
else
|
||||
{
|
||||
path.emplace_back(XmlTagName::k_Unknown);
|
||||
}
|
||||
}
|
||||
else if (node.Type == XmlNodeType::Text)
|
||||
{
|
||||
if (path.size() == 1 && path[0] == XmlTagName::k_Id)
|
||||
{
|
||||
ret.Id = node.Value;
|
||||
}
|
||||
else if (
|
||||
path.size() == 2 && path[0] == XmlTagName::k_AccessPolicy
|
||||
&& path[1] == XmlTagName::k_Start)
|
||||
{
|
||||
ret.StartsOn = node.Value;
|
||||
}
|
||||
else if (
|
||||
path.size() == 2 && path[0] == XmlTagName::k_AccessPolicy
|
||||
&& path[1] == XmlTagName::k_Expiry)
|
||||
{
|
||||
ret.ExpiresOn = node.Value;
|
||||
}
|
||||
else if (
|
||||
path.size() == 2 && path[0] == XmlTagName::k_AccessPolicy
|
||||
&& path[1] == XmlTagName::k_Permission)
|
||||
{
|
||||
ret.Permissions = node.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static std::map<std::string, std::string> MetadataFromXml(XmlReader& reader)
|
||||
{
|
||||
std::map<std::string, std::string> ret;
|
||||
@ -3694,6 +3967,38 @@ namespace Azure { namespace Storage { namespace Blobs {
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void SetAccessPolicyOptionsToXml(
|
||||
XmlWriter& writer,
|
||||
const SetAccessPolicyOptions& options)
|
||||
{
|
||||
writer.Write(XmlNode{XmlNodeType::StartTag, "SignedIdentifiers"});
|
||||
for (const auto& i : options.SignedIdentifiers)
|
||||
{
|
||||
BlobSignedIdentifierToXml(writer, i);
|
||||
}
|
||||
writer.Write(XmlNode{XmlNodeType::EndTag});
|
||||
}
|
||||
|
||||
static void BlobSignedIdentifierToXml(XmlWriter& writer, const BlobSignedIdentifier& options)
|
||||
{
|
||||
writer.Write(XmlNode{XmlNodeType::StartTag, "SignedIdentifier"});
|
||||
writer.Write(XmlNode{XmlNodeType::StartTag, "Id"});
|
||||
writer.Write(XmlNode{XmlNodeType::Text, nullptr, options.Id.data()});
|
||||
writer.Write(XmlNode{XmlNodeType::EndTag});
|
||||
writer.Write(XmlNode{XmlNodeType::StartTag, "AccessPolicy"});
|
||||
writer.Write(XmlNode{XmlNodeType::StartTag, "Start"});
|
||||
writer.Write(XmlNode{XmlNodeType::Text, nullptr, options.StartsOn.data()});
|
||||
writer.Write(XmlNode{XmlNodeType::EndTag});
|
||||
writer.Write(XmlNode{XmlNodeType::StartTag, "Expiry"});
|
||||
writer.Write(XmlNode{XmlNodeType::Text, nullptr, options.ExpiresOn.data()});
|
||||
writer.Write(XmlNode{XmlNodeType::EndTag});
|
||||
writer.Write(XmlNode{XmlNodeType::StartTag, "Permission"});
|
||||
writer.Write(XmlNode{XmlNodeType::Text, nullptr, options.Permissions.data()});
|
||||
writer.Write(XmlNode{XmlNodeType::EndTag});
|
||||
writer.Write(XmlNode{XmlNodeType::EndTag});
|
||||
writer.Write(XmlNode{XmlNodeType::EndTag});
|
||||
}
|
||||
|
||||
}; // class Container
|
||||
|
||||
class Blob {
|
||||
|
||||
@ -204,4 +204,26 @@ namespace Azure { namespace Storage { namespace Blobs {
|
||||
options.Context, *m_pipeline, m_containerUrl.ToString(), protocolLayerOptions);
|
||||
}
|
||||
|
||||
Azure::Core::Response<BlobContainerAccessPolicy> BlobContainerClient::GetAccessPolicy(
|
||||
const GetBlobContainerAccessPolicyOptions& options) const
|
||||
{
|
||||
BlobRestClient::Container::GetAccessPolicyOptions protocolLayerOptions;
|
||||
protocolLayerOptions.LeaseId = options.AccessConditions.LeaseId;
|
||||
return BlobRestClient::Container::GetAccessPolicy(
|
||||
options.Context, *m_pipeline, m_containerUrl.ToString(), protocolLayerOptions);
|
||||
}
|
||||
|
||||
Azure::Core::Response<BlobContainerInfo> BlobContainerClient::SetAccessPolicy(
|
||||
const SetBlobContainerAccessPolicyOptions& options) const
|
||||
{
|
||||
BlobRestClient::Container::SetAccessPolicyOptions protocolLayerOptions;
|
||||
protocolLayerOptions.AccessType = options.AccessType;
|
||||
protocolLayerOptions.SignedIdentifiers = options.SignedIdentifiers;
|
||||
protocolLayerOptions.LeaseId = options.AccessConditions.LeaseId;
|
||||
protocolLayerOptions.IfModifiedSince = options.AccessConditions.IfModifiedSince;
|
||||
protocolLayerOptions.IfUnmodifiedSince = options.AccessConditions.IfUnmodifiedSince;
|
||||
return BlobRestClient::Container::SetAccessPolicy(
|
||||
options.Context, *m_pipeline, m_containerUrl.ToString(), protocolLayerOptions);
|
||||
}
|
||||
|
||||
}}} // namespace Azure::Storage::Blobs
|
||||
|
||||
@ -33,43 +33,44 @@ namespace Azure { namespace Storage { namespace Blobs {
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void BlobSasBuilder::SetPermissions(BlobContainerSasPermissions permissions)
|
||||
std::string BlobContainerSasPermissionsToString(BlobContainerSasPermissions permissions)
|
||||
{
|
||||
Permissions.clear();
|
||||
std::string permissions_str;
|
||||
// The order matters
|
||||
if ((permissions & BlobContainerSasPermissions::Read) == BlobContainerSasPermissions::Read)
|
||||
{
|
||||
Permissions += "r";
|
||||
permissions_str += "r";
|
||||
}
|
||||
if ((permissions & BlobContainerSasPermissions::Add) == BlobContainerSasPermissions::Add)
|
||||
{
|
||||
Permissions += "a";
|
||||
permissions_str += "a";
|
||||
}
|
||||
if ((permissions & BlobContainerSasPermissions::Create) == BlobContainerSasPermissions::Create)
|
||||
{
|
||||
Permissions += "c";
|
||||
permissions_str += "c";
|
||||
}
|
||||
if ((permissions & BlobContainerSasPermissions::Write) == BlobContainerSasPermissions::Write)
|
||||
{
|
||||
Permissions += "w";
|
||||
permissions_str += "w";
|
||||
}
|
||||
if ((permissions & BlobContainerSasPermissions::Delete) == BlobContainerSasPermissions::Delete)
|
||||
{
|
||||
Permissions += "d";
|
||||
permissions_str += "d";
|
||||
}
|
||||
if ((permissions & BlobContainerSasPermissions::DeleteVersion)
|
||||
== BlobContainerSasPermissions::DeleteVersion)
|
||||
{
|
||||
Permissions += "x";
|
||||
permissions_str += "x";
|
||||
}
|
||||
if ((permissions & BlobContainerSasPermissions::List) == BlobContainerSasPermissions::List)
|
||||
{
|
||||
Permissions += "l";
|
||||
permissions_str += "l";
|
||||
}
|
||||
if ((permissions & BlobContainerSasPermissions::Tags) == BlobContainerSasPermissions::Tags)
|
||||
{
|
||||
Permissions += "t";
|
||||
permissions_str += "t";
|
||||
}
|
||||
return permissions_str;
|
||||
}
|
||||
|
||||
void BlobSasBuilder::SetPermissions(BlobSasPermissions permissions)
|
||||
@ -132,7 +133,10 @@ namespace Azure { namespace Storage { namespace Blobs {
|
||||
{
|
||||
builder.AppendQuery("st", StartsOn.GetValue());
|
||||
}
|
||||
builder.AppendQuery("se", ExpiresOn);
|
||||
if (!ExpiresOn.empty())
|
||||
{
|
||||
builder.AppendQuery("se", ExpiresOn);
|
||||
}
|
||||
if (IPRange.HasValue())
|
||||
{
|
||||
builder.AppendQuery("sip", IPRange.GetValue());
|
||||
@ -142,7 +146,10 @@ namespace Azure { namespace Storage { namespace Blobs {
|
||||
builder.AppendQuery("si", Identifier);
|
||||
}
|
||||
builder.AppendQuery("sr", resource);
|
||||
builder.AppendQuery("sp", Permissions);
|
||||
if (!Permissions.empty())
|
||||
{
|
||||
builder.AppendQuery("sp", Permissions);
|
||||
}
|
||||
builder.AppendQuery("sig", signature, true);
|
||||
if (!CacheControl.empty())
|
||||
{
|
||||
|
||||
@ -4,6 +4,18 @@
|
||||
#include "blob_container_client_test.hpp"
|
||||
#include "blobs/blob_sas_builder.hpp"
|
||||
|
||||
namespace Azure { namespace Storage { namespace Blobs {
|
||||
|
||||
bool operator==(
|
||||
const Azure::Storage::Blobs::BlobSignedIdentifier& lhs,
|
||||
const Azure::Storage::Blobs::BlobSignedIdentifier& rhs)
|
||||
{
|
||||
return lhs.Id == rhs.Id && lhs.StartsOn == rhs.StartsOn && lhs.ExpiresOn == rhs.ExpiresOn
|
||||
&& lhs.Permissions == rhs.Permissions;
|
||||
}
|
||||
|
||||
}}} // namespace Azure::Storage::Blobs
|
||||
|
||||
namespace Azure { namespace Storage { namespace Test {
|
||||
|
||||
std::shared_ptr<Azure::Storage::Blobs::BlobContainerClient>
|
||||
@ -225,4 +237,39 @@ namespace Azure { namespace Storage { namespace Test {
|
||||
EXPECT_EQ(items, blobs);
|
||||
}
|
||||
|
||||
TEST_F(BlobContainerClientTest, AccessControlList)
|
||||
{
|
||||
auto container_client = Azure::Storage::Blobs::BlobContainerClient::CreateFromConnectionString(
|
||||
StandardStorageConnectionString(), LowercaseRandomString());
|
||||
container_client.Create();
|
||||
|
||||
Blobs::SetBlobContainerAccessPolicyOptions options;
|
||||
options.AccessType = Blobs::PublicAccessType::Blob;
|
||||
Blobs::BlobSignedIdentifier identifier;
|
||||
identifier.Id = RandomString(64);
|
||||
identifier.StartsOn = ToISO8601(std::chrono::system_clock::now() - std::chrono::minutes(1), 7);
|
||||
identifier.ExpiresOn = ToISO8601(std::chrono::system_clock::now() + std::chrono::minutes(1), 7);
|
||||
identifier.Permissions
|
||||
= Blobs::BlobContainerSasPermissionsToString(Blobs::BlobContainerSasPermissions::Read);
|
||||
options.SignedIdentifiers.emplace_back(identifier);
|
||||
identifier.Id = RandomString(64);
|
||||
identifier.StartsOn = ToISO8601(std::chrono::system_clock::now() - std::chrono::minutes(2), 7);
|
||||
identifier.ExpiresOn = ToISO8601(std::chrono::system_clock::now() + std::chrono::minutes(2), 7);
|
||||
identifier.Permissions
|
||||
= Blobs::BlobContainerSasPermissionsToString(Blobs::BlobContainerSasPermissions::All);
|
||||
options.SignedIdentifiers.emplace_back(identifier);
|
||||
|
||||
auto ret = container_client.SetAccessPolicy(options);
|
||||
EXPECT_FALSE(ret->ETag.empty());
|
||||
EXPECT_FALSE(ret->LastModified.empty());
|
||||
|
||||
auto ret2 = container_client.GetAccessPolicy();
|
||||
EXPECT_EQ(ret2->ETag, ret->ETag);
|
||||
EXPECT_EQ(ret2->LastModified, ret->LastModified);
|
||||
EXPECT_EQ(ret2->AccessType, options.AccessType.GetValue());
|
||||
EXPECT_EQ(ret2->SignedIdentifiers, options.SignedIdentifiers);
|
||||
|
||||
container_client.Delete();
|
||||
}
|
||||
|
||||
}}} // namespace Azure::Storage::Test
|
||||
|
||||
@ -340,7 +340,7 @@ namespace Azure { namespace Storage { namespace Test {
|
||||
builder2.ExpiresOn = ToISO8601(std::chrono::system_clock::now() - std::chrono::minutes(1));
|
||||
auto sasToken = builder2.ToSasQueryParameters(*keyCredential);
|
||||
EXPECT_THROW(verify_blob_create(sasToken), StorageError);
|
||||
|
||||
|
||||
auto sasToken2 = builder2.ToSasQueryParameters(userDelegationKey, accountName);
|
||||
EXPECT_THROW(verify_blob_create(sasToken2), StorageError);
|
||||
}
|
||||
@ -371,6 +371,29 @@ namespace Azure { namespace Storage { namespace Test {
|
||||
EXPECT_NO_THROW(verify_blob_create(sasToken2));
|
||||
}
|
||||
|
||||
// Identifier
|
||||
{
|
||||
Blobs::SetBlobContainerAccessPolicyOptions options;
|
||||
options.AccessType = Blobs::PublicAccessType::Blob;
|
||||
Blobs::BlobSignedIdentifier identifier;
|
||||
identifier.Id = RandomString(64);
|
||||
identifier.StartsOn = ToISO8601(std::chrono::system_clock::now() - std::chrono::minutes(5));
|
||||
identifier.ExpiresOn = ToISO8601(std::chrono::system_clock::now() + std::chrono::minutes(60));
|
||||
identifier.Permissions
|
||||
= Blobs::BlobContainerSasPermissionsToString(Blobs::BlobContainerSasPermissions::Read);
|
||||
options.SignedIdentifiers.emplace_back(identifier);
|
||||
m_blobContainerClient->SetAccessPolicy(options);
|
||||
|
||||
Blobs::BlobSasBuilder builder2 = blobSasBuilder;
|
||||
builder2.StartsOn.Reset();
|
||||
builder2.ExpiresOn.clear();
|
||||
builder2.SetPermissions(static_cast<Blobs::BlobContainerSasPermissions>(0));
|
||||
builder2.Identifier = identifier.Id;
|
||||
|
||||
auto sasToken = builder2.ToSasQueryParameters(*keyCredential);
|
||||
EXPECT_NO_THROW(verify_blob_read(sasToken));
|
||||
}
|
||||
|
||||
// response headers override
|
||||
{
|
||||
Blobs::BlobHttpHeaders headers;
|
||||
|
||||
@ -217,7 +217,9 @@ namespace Azure { namespace Storage { namespace Test {
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string ToISO8601(const std::chrono::system_clock::time_point& time_point)
|
||||
std::string ToISO8601(
|
||||
const std::chrono::system_clock::time_point& time_point,
|
||||
int numDecimalDigits)
|
||||
{
|
||||
std::time_t epoch_seconds = std::chrono::system_clock::to_time_t(time_point);
|
||||
struct tm ct;
|
||||
@ -226,9 +228,24 @@ namespace Azure { namespace Storage { namespace Test {
|
||||
#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 time_str;
|
||||
time_str.resize(64);
|
||||
std::strftime(&time_str[0], time_str.length(), "%Y-%m-%dT%H:%M:%S", &ct);
|
||||
time_str = time_str.data();
|
||||
if (numDecimalDigits != 0)
|
||||
{
|
||||
time_str += ".";
|
||||
auto time_point_second = std::chrono::time_point_cast<std::chrono::seconds>(time_point);
|
||||
auto decimal_part = time_point - time_point_second;
|
||||
uint64_t num_nanoseconds
|
||||
= std::chrono::duration_cast<std::chrono::nanoseconds>(decimal_part).count();
|
||||
std::string decimal_part_str = std::to_string(num_nanoseconds);
|
||||
decimal_part_str = std::string(9 - decimal_part_str.length(), '0') + decimal_part_str;
|
||||
decimal_part_str.resize(numDecimalDigits);
|
||||
time_str += decimal_part_str;
|
||||
}
|
||||
time_str += "Z";
|
||||
return time_str;
|
||||
}
|
||||
|
||||
std::string ToRFC1123(const std::chrono::system_clock::time_point& time_point)
|
||||
|
||||
@ -61,7 +61,9 @@ 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 ToISO8601(
|
||||
const std::chrono::system_clock::time_point& time_point,
|
||||
int numDecimalDigits = 0);
|
||||
std::string ToRFC1123(const std::chrono::system_clock::time_point& time_point);
|
||||
|
||||
}}} // namespace Azure::Storage::Test
|
||||
|
||||
Loading…
Reference in New Issue
Block a user