Integrate Core::DateTime into storage (#1246)

* blob package can build

* test can build

* changelog

* datatime for datalake sas

* fix test error

* user delegation key doesn't support time fractional part

* add comments

* Added DateTime for DataLake and many other refinements.

* Resolved review comments.

* Added changelog

Co-authored-by: Tank Tang <kat@microsoft.com>
This commit is contained in:
JinmingHu 2020-12-28 16:00:18 +08:00 committed by GitHub
parent 9abe10b228
commit b8e5c95d21
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
39 changed files with 1492 additions and 1761 deletions

View File

@ -58,6 +58,7 @@
- `ListBlobContainersSegment` is renamed to `ListBlobContainersSinglePage`.
- `FindBlobsByTags` is renamed to `FindBlobsByTagsSinglePage`.
- `MaxResults` in list APIs are renamed to `PageSizeHint`.
- All date time related strings are now changed to `Azure::Core::DateTime` type.
## 12.0.0-beta.5 (2020-11-13)

View File

@ -16,7 +16,7 @@ namespace Azure { namespace Storage { namespace Blobs { namespace Models {
struct DownloadBlobToResult
{
std::string ETag;
std::string LastModified;
Azure::Core::DateTime LastModified;
int64_t ContentLength = 0;
BlobHttpHeaders HttpHeaders;
Storage::Metadata Metadata;
@ -36,7 +36,7 @@ namespace Azure { namespace Storage { namespace Blobs { namespace Models {
struct GetPageBlobPageRangesResult
{
std::string ETag;
std::string LastModified;
Azure::Core::DateTime LastModified;
int64_t BlobContentLength = 0;
std::vector<PageRange> PageRanges;
std::vector<PageRange> ClearRanges;

View File

@ -182,15 +182,16 @@ namespace Azure { namespace Storage { namespace Sas {
/**
* @brief Optionally specify the time at which the shared access signature becomes
* valid.
* valid. This timestamp will be truncated to second.
*/
Azure::Core::Nullable<std::string> StartsOn;
Azure::Core::Nullable<Azure::Core::DateTime> 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.
* be omitted if it has been specified in an associated stored access policy. This timestamp
* will be truncated to second.
*/
std::string ExpiresOn;
Azure::Core::DateTime ExpiresOn;
/**
* @brief Specifies an IP address or a range of IP addresses from which to accept

View File

@ -104,16 +104,16 @@ namespace Azure { namespace Storage { namespace Blobs {
* @brief Retrieves a key that can be used to delegate Active Directory authorization to
* shared access signatures.
*
* @param startsOn Start time for the key's validity, in ISO date format. The time should be
* specified in UTC.
* @param expiresOn Expiration of the key's validity, in ISO date format. The time should be
* specified in UTC.
* @param startsOn Start time for the key's validity. The time should be specified in UTC, and
* will be truncated to second.
* @param expiresOn Expiration of the key's validity. The time should be specified in UTC, and
* will be truncated to second.
* @param options Optional parameters to execute this function.
* @return A deserialized GetUserDelegationKeyResult instance.
*/
Azure::Core::Response<Models::GetUserDelegationKeyResult> GetUserDelegationKey(
const std::string& startsOn,
const std::string& expiresOn,
const Azure::Core::DateTime& startsOn,
const Azure::Core::DateTime& expiresOn,
const GetUserDelegationKeyOptions& options = GetUserDelegationKeyOptions()) const;
/**

View File

@ -128,12 +128,18 @@ namespace Azure { namespace Storage { namespace Sas {
snapshotVersion = BlobVersionId;
}
std::string stringToSign = Permissions + "\n" + (StartsOn.HasValue() ? StartsOn.GetValue() : "")
+ "\n" + ExpiresOn + "\n" + canonicalName + "\n" + Identifier + "\n"
+ (IPRange.HasValue() ? IPRange.GetValue() : "") + "\n" + protocol + "\n"
+ Storage::Details::DefaultSasVersion + "\n" + resource + "\n" + snapshotVersion + "\n"
+ CacheControl + "\n" + ContentDisposition + "\n" + ContentEncoding + "\n" + ContentLanguage
+ "\n" + ContentType;
std::string startsOnStr = StartsOn.HasValue()
? StartsOn.GetValue().GetRfc3339String(Azure::Core::DateTime::TimeFractionFormat::Truncate)
: "";
std::string expiresOnStr = Identifier.empty()
? ExpiresOn.GetRfc3339String(Azure::Core::DateTime::TimeFractionFormat::Truncate)
: "";
std::string stringToSign = Permissions + "\n" + startsOnStr + "\n" + expiresOnStr + "\n"
+ canonicalName + "\n" + Identifier + "\n" + (IPRange.HasValue() ? IPRange.GetValue() : "")
+ "\n" + protocol + "\n" + Storage::Details::DefaultSasVersion + "\n" + resource + "\n"
+ snapshotVersion + "\n" + CacheControl + "\n" + ContentDisposition + "\n" + ContentEncoding
+ "\n" + ContentLanguage + "\n" + ContentType;
std::string signature = Base64Encode(Storage::Details::HmacSha256(
std::vector<uint8_t>(stringToSign.begin(), stringToSign.end()),
@ -143,14 +149,13 @@ namespace Azure { namespace Storage { namespace Sas {
builder.AppendQueryParameter(
"sv", Storage::Details::UrlEncodeQueryParameter(Storage::Details::DefaultSasVersion));
builder.AppendQueryParameter("spr", Storage::Details::UrlEncodeQueryParameter(protocol));
if (StartsOn.HasValue())
if (!startsOnStr.empty())
{
builder.AppendQueryParameter(
"st", Storage::Details::UrlEncodeQueryParameter(StartsOn.GetValue()));
builder.AppendQueryParameter("st", Storage::Details::UrlEncodeQueryParameter(startsOnStr));
}
if (!ExpiresOn.empty())
if (!expiresOnStr.empty())
{
builder.AppendQueryParameter("se", Storage::Details::UrlEncodeQueryParameter(ExpiresOn));
builder.AppendQueryParameter("se", Storage::Details::UrlEncodeQueryParameter(expiresOnStr));
}
if (IPRange.HasValue())
{
@ -217,12 +222,21 @@ namespace Azure { namespace Storage { namespace Sas {
snapshotVersion = BlobVersionId;
}
std::string stringToSign = Permissions + "\n" + (StartsOn.HasValue() ? StartsOn.GetValue() : "")
+ "\n" + ExpiresOn + "\n" + canonicalName + "\n" + userDelegationKey.SignedObjectId + "\n"
+ userDelegationKey.SignedTenantId + "\n" + userDelegationKey.SignedStartsOn + "\n"
+ userDelegationKey.SignedExpiresOn + "\n" + userDelegationKey.SignedService + "\n"
+ userDelegationKey.SignedVersion + "\n\n\n\n"
+ (IPRange.HasValue() ? IPRange.GetValue() : "") + "\n" + protocol + "\n"
std::string startsOnStr = StartsOn.HasValue()
? StartsOn.GetValue().GetRfc3339String(Azure::Core::DateTime::TimeFractionFormat::Truncate)
: "";
std::string expiresOnStr
= ExpiresOn.GetRfc3339String(Azure::Core::DateTime::TimeFractionFormat::Truncate);
std::string signedStartsOnStr = userDelegationKey.SignedStartsOn.GetRfc3339String(
Azure::Core::DateTime::TimeFractionFormat::Truncate);
std::string signedExpiresOnStr = userDelegationKey.SignedExpiresOn.GetRfc3339String(
Azure::Core::DateTime::TimeFractionFormat::Truncate);
std::string stringToSign = Permissions + "\n" + startsOnStr + "\n" + expiresOnStr + "\n"
+ canonicalName + "\n" + userDelegationKey.SignedObjectId + "\n"
+ userDelegationKey.SignedTenantId + "\n" + signedStartsOnStr + "\n" + signedExpiresOnStr
+ "\n" + userDelegationKey.SignedService + "\n" + userDelegationKey.SignedVersion
+ "\n\n\n\n" + (IPRange.HasValue() ? IPRange.GetValue() : "") + "\n" + protocol + "\n"
+ Storage::Details::DefaultSasVersion + "\n" + resource + "\n" + snapshotVersion + "\n"
+ CacheControl + "\n" + ContentDisposition + "\n" + ContentEncoding + "\n" + ContentLanguage
+ "\n" + ContentType;
@ -235,12 +249,11 @@ namespace Azure { namespace Storage { namespace Sas {
builder.AppendQueryParameter(
"sv", Storage::Details::UrlEncodeQueryParameter(Storage::Details::DefaultSasVersion));
builder.AppendQueryParameter("sr", Storage::Details::UrlEncodeQueryParameter(resource));
if (StartsOn.HasValue())
if (!startsOnStr.empty())
{
builder.AppendQueryParameter(
"st", Storage::Details::UrlEncodeQueryParameter(StartsOn.GetValue()));
builder.AppendQueryParameter("st", Storage::Details::UrlEncodeQueryParameter(startsOnStr));
}
builder.AppendQueryParameter("se", Storage::Details::UrlEncodeQueryParameter(ExpiresOn));
builder.AppendQueryParameter("se", Storage::Details::UrlEncodeQueryParameter(expiresOnStr));
builder.AppendQueryParameter("sp", Storage::Details::UrlEncodeQueryParameter(Permissions));
if (IPRange.HasValue())
{
@ -253,9 +266,9 @@ namespace Azure { namespace Storage { namespace Sas {
builder.AppendQueryParameter(
"sktid", Storage::Details::UrlEncodeQueryParameter(userDelegationKey.SignedTenantId));
builder.AppendQueryParameter(
"skt", Storage::Details::UrlEncodeQueryParameter(userDelegationKey.SignedStartsOn));
"skt", Storage::Details::UrlEncodeQueryParameter(signedStartsOnStr));
builder.AppendQueryParameter(
"ske", Storage::Details::UrlEncodeQueryParameter(userDelegationKey.SignedExpiresOn));
"ske", Storage::Details::UrlEncodeQueryParameter(signedExpiresOnStr));
builder.AppendQueryParameter(
"sks", Storage::Details::UrlEncodeQueryParameter(userDelegationKey.SignedService));
builder.AppendQueryParameter(

View File

@ -134,8 +134,8 @@ namespace Azure { namespace Storage { namespace Blobs {
}
Azure::Core::Response<Models::GetUserDelegationKeyResult> BlobServiceClient::GetUserDelegationKey(
const std::string& startsOn,
const std::string& expiresOn,
const Azure::Core::DateTime& startsOn,
const Azure::Core::DateTime& expiresOn,
const GetUserDelegationKeyOptions& options) const
{
Details::BlobRestClient::Service::GetUserDelegationKeyOptions protocolLayerOptions;

View File

@ -44,7 +44,7 @@ namespace Azure { namespace Storage { namespace Test {
StandardStorageConnectionString(), m_containerName, RandomString());
auto blobContentInfo = appendBlobClient.Create(m_blobUploadOptions);
EXPECT_FALSE(blobContentInfo->ETag.empty());
EXPECT_FALSE(blobContentInfo->LastModified.empty());
EXPECT_TRUE(IsValidTime(blobContentInfo->LastModified));
EXPECT_TRUE(blobContentInfo->VersionId.HasValue());
EXPECT_FALSE(blobContentInfo->VersionId.GetValue().empty());
EXPECT_FALSE(blobContentInfo->EncryptionScope.HasValue());
@ -111,9 +111,9 @@ namespace Azure { namespace Storage { namespace Test {
UnmodifiedSince,
};
auto lastModifiedTime = FromRfc1123(appendBlobClient.GetProperties()->LastModified);
auto timeBeforeStr = ToRfc1123(lastModifiedTime - std::chrono::seconds(1));
auto timeAfterStr = ToRfc1123(lastModifiedTime + std::chrono::seconds(1));
auto lastModifiedTime = appendBlobClient.GetProperties()->LastModified;
auto timeBeforeStr = lastModifiedTime - std::chrono::seconds(1);
auto timeAfterStr = lastModifiedTime + std::chrono::seconds(1);
for (auto condition : {Condition::ModifiedSince, Condition::UnmodifiedSince})
{
for (auto sinceTime : {TimePoint::TimeBefore, TimePoint::TimeAfter})
@ -204,9 +204,9 @@ namespace Azure { namespace Storage { namespace Test {
= sourceBlobClient.AcquireLease(CreateUniqueLeaseId(), InfiniteLeaseDuration);
std::string leaseId = leaseResponse->LeaseId;
std::string eTag = leaseResponse->ETag;
auto lastModifiedTime = FromRfc1123(leaseResponse->LastModified);
auto timeBeforeStr = ToRfc1123(lastModifiedTime - std::chrono::seconds(1));
auto timeAfterStr = ToRfc1123(lastModifiedTime + std::chrono::seconds(1));
auto lastModifiedTime = leaseResponse->LastModified;
auto timeBeforeStr = lastModifiedTime - std::chrono::seconds(1);
auto timeAfterStr = lastModifiedTime + std::chrono::seconds(1);
auto destBlobClient = Azure::Storage::Blobs::AppendBlobClient::CreateFromConnectionString(
StandardStorageConnectionString(), m_containerName, RandomString());
@ -285,7 +285,7 @@ namespace Azure { namespace Storage { namespace Test {
sealOptions.AccessConditions.IfAppendPositionEqual = m_blobContent.size();
auto sealResult = blobClient.Seal(sealOptions);
EXPECT_FALSE(sealResult->ETag.empty());
EXPECT_FALSE(sealResult->LastModified.empty());
EXPECT_TRUE(IsValidTime(sealResult->LastModified));
EXPECT_TRUE(sealResult->IsSealed);
downloadResult = blobClient.Download();

View File

@ -25,10 +25,8 @@ namespace Azure { namespace Storage { namespace Test {
{
Sas::AccountSasBuilder accountSasBuilder;
accountSasBuilder.Protocol = Sas::SasProtocol::HttpsAndHttp;
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.StartsOn = Azure::Core::DateTime::Now() - std::chrono::minutes(5);
accountSasBuilder.ExpiresOn = Azure::Core::DateTime::Now() + std::chrono::minutes(60);
accountSasBuilder.Services = Sas::AccountSasServices::Blobs;
accountSasBuilder.ResourceTypes
= Sas::AccountSasResource::Object | Sas::AccountSasResource::BlobContainer;

View File

@ -42,7 +42,7 @@ namespace Azure { namespace Storage { namespace Test {
{
Sas::BlobSasBuilder sasBuilder;
sasBuilder.Protocol = Sas::SasProtocol::HttpsAndHttp;
sasBuilder.ExpiresOn = ToIso8601(std::chrono::system_clock::now() + std::chrono::hours(72));
sasBuilder.ExpiresOn = Azure::Core::DateTime::Now() + std::chrono::hours(72);
sasBuilder.BlobContainerName = m_containerName;
sasBuilder.Resource = Sas::BlobSasResource::BlobContainer;
sasBuilder.SetPermissions(Sas::BlobContainerSasPermissions::All);
@ -64,7 +64,7 @@ namespace Azure { namespace Storage { namespace Test {
EXPECT_FALSE(res.GetRawResponse().GetHeaders().at(Details::HttpHeaderDate).empty());
EXPECT_FALSE(res.GetRawResponse().GetHeaders().at(Details::HttpHeaderXMsVersion).empty());
EXPECT_FALSE(res->ETag.empty());
EXPECT_FALSE(res->LastModified.empty());
EXPECT_TRUE(IsValidTime(res->LastModified));
EXPECT_THROW(container_client.Create(), StorageException);
auto res2 = container_client.Delete();
@ -105,7 +105,7 @@ namespace Azure { namespace Storage { namespace Test {
EXPECT_FALSE(res.GetRawResponse().GetHeaders().at(Details::HttpHeaderDate).empty());
EXPECT_FALSE(res.GetRawResponse().GetHeaders().at(Details::HttpHeaderXMsVersion).empty());
EXPECT_FALSE(res->ETag.empty());
EXPECT_FALSE(res->LastModified.empty());
EXPECT_TRUE(IsValidTime(res->LastModified));
auto res2 = m_blobContainerClient->GetProperties();
EXPECT_FALSE(res2.GetRawResponse().GetHeaders().at(Details::HttpHeaderRequestId).empty());
@ -113,7 +113,7 @@ namespace Azure { namespace Storage { namespace Test {
EXPECT_FALSE(res2.GetRawResponse().GetHeaders().at(Details::HttpHeaderXMsVersion).empty());
auto properties = *res2;
EXPECT_FALSE(properties.ETag.empty());
EXPECT_FALSE(properties.LastModified.empty());
EXPECT_TRUE(IsValidTime(properties.LastModified));
EXPECT_EQ(properties.Metadata, metadata);
metadata.clear();
@ -174,8 +174,8 @@ namespace Azure { namespace Storage { namespace Test {
for (const auto& blob : res->Items)
{
EXPECT_FALSE(blob.Name.empty());
EXPECT_FALSE(blob.CreationTime.empty());
EXPECT_FALSE(blob.LastModified.empty());
EXPECT_TRUE(IsValidTime(blob.CreatedOn));
EXPECT_TRUE(IsValidTime(blob.LastModified));
EXPECT_FALSE(blob.ETag.empty());
EXPECT_NE(blob.BlobType, Azure::Storage::Blobs::Models::BlobType::Unknown);
if (blob.BlobType == Blobs::Models::BlobType::BlockBlob)
@ -376,19 +376,19 @@ namespace Azure { namespace Storage { namespace Test {
options.AccessType = Blobs::Models::PublicAccessType::Blob;
Blobs::Models::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.StartsOn = Azure::Core::DateTime::Now() - std::chrono::minutes(1);
identifier.ExpiresOn = Azure::Core::DateTime::Now() + std::chrono::minutes(1);
identifier.Permissions = "r";
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.StartsOn = Azure::Core::DateTime::Now() - std::chrono::minutes(2);
identifier.ExpiresOn = Azure::Core::DateTime::Now() + std::chrono::minutes(2);
identifier.Permissions = "racwdxlt";
options.SignedIdentifiers.emplace_back(identifier);
auto ret = container_client.SetAccessPolicy(options);
EXPECT_FALSE(ret->ETag.empty());
EXPECT_FALSE(ret->LastModified.empty());
EXPECT_TRUE(IsValidTime(ret->LastModified));
auto ret2 = container_client.GetAccessPolicy();
EXPECT_EQ(ret2->ETag, ret->ETag);
@ -405,11 +405,11 @@ namespace Azure { namespace Storage { namespace Test {
int32_t leaseDuration = 20;
auto aLease = *m_blobContainerClient->AcquireLease(leaseId1, leaseDuration);
EXPECT_FALSE(aLease.ETag.empty());
EXPECT_FALSE(aLease.LastModified.empty());
EXPECT_TRUE(IsValidTime(aLease.LastModified));
EXPECT_EQ(aLease.LeaseId, leaseId1);
aLease = *m_blobContainerClient->AcquireLease(leaseId1, leaseDuration);
EXPECT_FALSE(aLease.ETag.empty());
EXPECT_FALSE(aLease.LastModified.empty());
EXPECT_TRUE(IsValidTime(aLease.LastModified));
EXPECT_EQ(aLease.LeaseId, leaseId1);
auto properties = *m_blobContainerClient->GetProperties();
@ -419,32 +419,32 @@ namespace Azure { namespace Storage { namespace Test {
auto rLease = *m_blobContainerClient->RenewLease(leaseId1);
EXPECT_FALSE(rLease.ETag.empty());
EXPECT_FALSE(rLease.LastModified.empty());
EXPECT_TRUE(IsValidTime(rLease.LastModified));
EXPECT_EQ(rLease.LeaseId, leaseId1);
std::string leaseId2 = CreateUniqueLeaseId();
EXPECT_NE(leaseId1, leaseId2);
auto cLease = *m_blobContainerClient->ChangeLease(leaseId1, leaseId2);
EXPECT_FALSE(cLease.ETag.empty());
EXPECT_FALSE(cLease.LastModified.empty());
EXPECT_TRUE(IsValidTime(cLease.LastModified));
EXPECT_EQ(cLease.LeaseId, leaseId2);
auto containerInfo = *m_blobContainerClient->ReleaseLease(leaseId2);
EXPECT_FALSE(containerInfo.ETag.empty());
EXPECT_FALSE(containerInfo.LastModified.empty());
EXPECT_TRUE(IsValidTime(containerInfo.LastModified));
aLease = *m_blobContainerClient->AcquireLease(CreateUniqueLeaseId(), InfiniteLeaseDuration);
properties = *m_blobContainerClient->GetProperties();
EXPECT_FALSE(properties.LeaseDuration.GetValue().empty());
auto brokenLease = *m_blobContainerClient->BreakLease();
EXPECT_FALSE(brokenLease.ETag.empty());
EXPECT_FALSE(brokenLease.LastModified.empty());
EXPECT_TRUE(IsValidTime(brokenLease.LastModified));
EXPECT_EQ(brokenLease.LeaseTime, 0);
aLease = *m_blobContainerClient->AcquireLease(CreateUniqueLeaseId(), leaseDuration);
brokenLease = *m_blobContainerClient->BreakLease();
EXPECT_FALSE(brokenLease.ETag.empty());
EXPECT_FALSE(brokenLease.LastModified.empty());
EXPECT_TRUE(IsValidTime(brokenLease.LastModified));
EXPECT_NE(brokenLease.LeaseTime, 0);
Blobs::BreakBlobContainerLeaseOptions options;
@ -645,21 +645,21 @@ namespace Azure { namespace Storage { namespace Test {
{
for (auto sinceTime : {TimePoint::TimeBefore, TimePoint::TimeAfter})
{
auto lastModifiedTime = FromRfc1123(containerClient.GetProperties()->LastModified);
auto timeBeforeStr = ToRfc1123(lastModifiedTime - std::chrono::seconds(1));
auto timeAfterStr = ToRfc1123(lastModifiedTime + std::chrono::seconds(1));
auto lastModifiedTime = containerClient.GetProperties()->LastModified;
auto timeBefore = lastModifiedTime - std::chrono::seconds(1);
auto timeAfter = lastModifiedTime + std::chrono::seconds(1);
Blobs::SetBlobContainerAccessPolicyOptions options;
options.AccessType = Blobs::Models::PublicAccessType::Private;
if (condition == Condition::ModifiedSince)
{
options.AccessConditions.IfModifiedSince
= sinceTime == TimePoint::TimeBefore ? timeBeforeStr : timeAfterStr;
= sinceTime == TimePoint::TimeBefore ? timeBefore : timeAfter;
}
else if (condition == Condition::UnmodifiedSince)
{
options.AccessConditions.IfUnmodifiedSince
= sinceTime == TimePoint::TimeBefore ? timeBeforeStr : timeAfterStr;
= sinceTime == TimePoint::TimeBefore ? timeBefore : timeAfter;
}
bool shouldThrow
= (condition == Condition::ModifiedSince && sinceTime == TimePoint::TimeAfter)
@ -723,8 +723,8 @@ namespace Azure { namespace Storage { namespace Test {
EXPECT_TRUE(deletedContainerItem.IsDeleted);
EXPECT_TRUE(deletedContainerItem.VersionId.HasValue());
EXPECT_FALSE(deletedContainerItem.VersionId.GetValue().empty());
EXPECT_TRUE(deletedContainerItem.DeletedTime.HasValue());
EXPECT_FALSE(deletedContainerItem.DeletedTime.GetValue().empty());
EXPECT_TRUE(deletedContainerItem.DeletedOn.HasValue());
EXPECT_TRUE(IsValidTime(deletedContainerItem.DeletedOn.GetValue()));
EXPECT_TRUE(deletedContainerItem.RemainingRetentionDays.HasValue());
EXPECT_GE(deletedContainerItem.RemainingRetentionDays.GetValue(), 0);

View File

@ -10,21 +10,22 @@ namespace Azure { namespace Storage { namespace Test {
TEST_F(BlobContainerClientTest, BlobSasTest)
{
auto sasStartsOn = Azure::Core::DateTime::Now() - std::chrono::minutes(5);
auto sasExpiredOn = Azure::Core::DateTime::Now() - std::chrono::minutes(1);
auto sasExpiresOn = Azure::Core::DateTime::Now() + std::chrono::minutes(60);
Sas::AccountSasBuilder accountSasBuilder;
accountSasBuilder.Protocol = Sas::SasProtocol::HttpsAndHttp;
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.StartsOn = sasStartsOn;
accountSasBuilder.ExpiresOn = sasExpiresOn;
accountSasBuilder.Services = Sas::AccountSasServices::Blobs;
accountSasBuilder.ResourceTypes = Sas::AccountSasResource::All;
std::string blobName = RandomString();
Sas::BlobSasBuilder blobSasBuilder;
blobSasBuilder.Protocol = Sas::SasProtocol::HttpsAndHttp;
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.StartsOn = sasStartsOn;
blobSasBuilder.ExpiresOn = sasExpiresOn;
blobSasBuilder.BlobContainerName = m_containerName;
blobSasBuilder.BlobName = blobName;
blobSasBuilder.Resource = Sas::BlobSasResource::Blob;
@ -49,9 +50,7 @@ namespace Azure { namespace Storage { namespace Test {
serviceUrl,
std::make_shared<Azure::Identity::ClientSecretCredential>(
AadTenantId(), AadClientId(), AadClientSecret()));
auto userDelegationKey = *blobServiceClient1.GetUserDelegationKey(
ToIso8601(std::chrono::system_clock::now() - std::chrono::minutes(5)),
ToIso8601(std::chrono::system_clock::now() + std::chrono::minutes(60)));
auto userDelegationKey = *blobServiceClient1.GetUserDelegationKey(sasStartsOn, sasExpiresOn);
auto verify_blob_read = [&](const std::string& sas) {
EXPECT_NO_THROW(blobClient0.Create());
@ -218,8 +217,8 @@ namespace Azure { namespace Storage { namespace Test {
// Expires
{
Sas::AccountSasBuilder builder2 = accountSasBuilder;
builder2.StartsOn = ToIso8601(std::chrono::system_clock::now() - std::chrono::minutes(5));
builder2.ExpiresOn = ToIso8601(std::chrono::system_clock::now() - std::chrono::minutes(1));
builder2.StartsOn = sasStartsOn;
builder2.ExpiresOn = sasExpiredOn;
auto sasToken = builder2.GenerateSasToken(*keyCredential);
EXPECT_THROW(verify_blob_create(sasToken), StorageException);
}
@ -330,8 +329,8 @@ namespace Azure { namespace Storage { namespace Test {
// Expires
{
Sas::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));
builder2.StartsOn = sasStartsOn;
builder2.ExpiresOn = sasExpiredOn;
auto sasToken = builder2.GenerateSasToken(*keyCredential);
EXPECT_THROW(verify_blob_create(sasToken), StorageException);
@ -372,15 +371,15 @@ namespace Azure { namespace Storage { namespace Test {
options.AccessType = Blobs::Models::PublicAccessType::Blob;
Blobs::Models::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.StartsOn = sasStartsOn;
identifier.ExpiresOn = sasExpiresOn;
identifier.Permissions = "r";
options.SignedIdentifiers.emplace_back(identifier);
m_blobContainerClient->SetAccessPolicy(options);
Sas::BlobSasBuilder builder2 = blobSasBuilder;
builder2.StartsOn.Reset();
builder2.ExpiresOn.clear();
builder2.ExpiresOn = Azure::Core::DateTime();
builder2.SetPermissions(static_cast<Sas::BlobContainerSasPermissions>(0));
builder2.Identifier = identifier.Id;

View File

@ -144,10 +144,10 @@ namespace Azure { namespace Storage { namespace Test {
{
EXPECT_FALSE(container.Name.empty());
EXPECT_FALSE(container.ETag.empty());
EXPECT_FALSE(container.LastModified.empty());
EXPECT_TRUE(IsValidTime(container.LastModified));
EXPECT_FALSE(container.IsDeleted);
EXPECT_FALSE(container.VersionId.HasValue());
EXPECT_FALSE(container.DeletedTime.HasValue());
EXPECT_FALSE(container.DeletedOn.HasValue());
EXPECT_FALSE(container.RemainingRetentionDays.HasValue());
EXPECT_EQ(container.DefaultEncryptionScope, AccountEncryptionKey);
EXPECT_FALSE(container.PreventEncryptionScopeOverride);
@ -339,9 +339,9 @@ namespace Azure { namespace Storage { namespace Test {
auto serviceStatistics = *secondaryServiceClient.GetStatistics();
EXPECT_NE(
serviceStatistics.GeoReplication.Status, Blobs::Models::BlobGeoReplicationStatus::Unknown);
if (serviceStatistics.GeoReplication.LastSyncTime.HasValue())
if (serviceStatistics.GeoReplication.LastSyncedOn.HasValue())
{
EXPECT_FALSE(serviceStatistics.GeoReplication.LastSyncTime.GetValue().empty());
EXPECT_TRUE(IsValidTime(serviceStatistics.GeoReplication.LastSyncedOn.GetValue()));
}
}

View File

@ -66,7 +66,7 @@ namespace Azure { namespace Storage { namespace Test {
= Azure::Core::Http::MemoryBodyStream(m_blobContent.data(), m_blobContent.size());
auto blobContentInfo = blockBlobClient.Upload(&blobContent, m_blobUploadOptions);
EXPECT_FALSE(blobContentInfo->ETag.empty());
EXPECT_FALSE(blobContentInfo->LastModified.empty());
EXPECT_TRUE(IsValidTime(blobContentInfo->LastModified));
EXPECT_TRUE(blobContentInfo->VersionId.HasValue());
EXPECT_FALSE(blobContentInfo->VersionId.GetValue().empty());
EXPECT_FALSE(blobContentInfo->EncryptionScope.HasValue());
@ -84,8 +84,8 @@ namespace Azure { namespace Storage { namespace Test {
EXPECT_FALSE(res.GetRawResponse().GetHeaders().at(Details::HttpHeaderDate).empty());
EXPECT_FALSE(res.GetRawResponse().GetHeaders().at(Details::HttpHeaderXMsVersion).empty());
EXPECT_FALSE(res->ETag.empty());
EXPECT_FALSE(res->LastModified.empty());
EXPECT_FALSE(res->CreationTime.empty());
EXPECT_TRUE(IsValidTime(res->LastModified));
EXPECT_TRUE(IsValidTime(res->CreatedOn));
EXPECT_EQ(res->HttpHeaders, m_blobUploadOptions.HttpHeaders);
EXPECT_EQ(res->Metadata, m_blobUploadOptions.Metadata);
EXPECT_EQ(res->BlobType, Azure::Storage::Blobs::Models::BlobType::BlockBlob);
@ -106,16 +106,16 @@ namespace Azure { namespace Storage { namespace Test {
{
{
auto res = m_blockBlobClient->Download();
ASSERT_TRUE(res->LastAccessTime.HasValue());
EXPECT_FALSE(res->LastAccessTime.GetValue().empty());
ASSERT_TRUE(res->LastAccessedOn.HasValue());
EXPECT_TRUE(IsValidTime(res->LastAccessedOn.GetValue()));
}
{
auto res = m_blockBlobClient->GetProperties();
ASSERT_TRUE(res->LastAccessTime.HasValue());
EXPECT_FALSE(res->LastAccessTime.GetValue().empty());
ASSERT_TRUE(res->LastAccessedOn.HasValue());
EXPECT_TRUE(IsValidTime(res->LastAccessedOn.GetValue()));
}
{
std::string lastAccessTime;
Azure::Core::DateTime lastAccessedOn;
Azure::Storage::Blobs::ListBlobsSinglePageOptions options;
options.Prefix = m_blobName;
@ -127,13 +127,13 @@ namespace Azure { namespace Storage { namespace Test {
{
if (blob.Name == m_blobName)
{
lastAccessTime = blob.LastAccessTime.GetValue();
lastAccessedOn = blob.LastAccessedOn.GetValue();
break;
}
}
} while (!options.ContinuationToken.GetValue().empty());
EXPECT_FALSE(lastAccessTime.empty());
EXPECT_TRUE(IsValidTime(lastAccessedOn));
}
}
@ -154,7 +154,7 @@ namespace Azure { namespace Storage { namespace Test {
EXPECT_FALSE(res.GetRawResponse().GetHeaders().at(Details::HttpHeaderDate).empty());
EXPECT_FALSE(res.GetRawResponse().GetHeaders().at(Details::HttpHeaderXMsVersion).empty());
EXPECT_FALSE(res->ETag.empty());
EXPECT_FALSE(res->LastModified.empty());
EXPECT_TRUE(IsValidTime(res->LastModified));
EXPECT_EQ(res->HttpHeaders, m_blobUploadOptions.HttpHeaders);
EXPECT_EQ(res->Metadata, m_blobUploadOptions.Metadata);
EXPECT_EQ(res->BlobType, Azure::Storage::Blobs::Models::BlobType::BlockBlob);
@ -175,7 +175,7 @@ namespace Azure { namespace Storage { namespace Test {
EXPECT_FALSE(res.GetRawResponse().GetHeaders().at(Details::HttpHeaderDate).empty());
EXPECT_FALSE(res.GetRawResponse().GetHeaders().at(Details::HttpHeaderXMsVersion).empty());
EXPECT_FALSE(res->ETag.empty());
EXPECT_FALSE(res->LastModified.empty());
EXPECT_TRUE(IsValidTime(res->LastModified));
EXPECT_FALSE(res->CopyId.empty());
EXPECT_TRUE(res->VersionId.HasValue());
EXPECT_FALSE(res->VersionId.GetValue().empty());
@ -191,7 +191,7 @@ namespace Azure { namespace Storage { namespace Test {
EXPECT_FALSE(properties.CopyProgress.GetValue().empty());
if (properties.CopyStatus.GetValue() == Azure::Storage::Blobs::Models::CopyStatus::Success)
{
EXPECT_FALSE(properties.CopyCompletionTime.GetValue().empty());
EXPECT_TRUE(IsValidTime(properties.CopyCompletedOn.GetValue()));
}
}
@ -202,7 +202,7 @@ namespace Azure { namespace Storage { namespace Test {
EXPECT_FALSE(res.GetRawResponse().GetHeaders().at(Details::HttpHeaderDate).empty());
EXPECT_FALSE(res.GetRawResponse().GetHeaders().at(Details::HttpHeaderXMsVersion).empty());
EXPECT_FALSE(res->ETag.empty());
EXPECT_FALSE(res->LastModified.empty());
EXPECT_TRUE(IsValidTime(res->LastModified));
EXPECT_FALSE(res->Snapshot.empty());
EXPECT_TRUE(res->VersionId.HasValue());
EXPECT_FALSE(res->VersionId.GetValue().empty());
@ -256,14 +256,14 @@ namespace Azure { namespace Storage { namespace Test {
EXPECT_FALSE(res.GetRawResponse().GetHeaders().at(Details::HttpHeaderDate).empty());
EXPECT_FALSE(res.GetRawResponse().GetHeaders().at(Details::HttpHeaderXMsVersion).empty());
EXPECT_FALSE(res->ETag.empty());
EXPECT_FALSE(res->LastModified.empty());
EXPECT_FALSE(res->CreationTime.empty());
EXPECT_TRUE(IsValidTime(res->LastModified));
EXPECT_TRUE(IsValidTime(res->CreatedOn));
EXPECT_EQ(res->Metadata, m_blobUploadOptions.Metadata);
EXPECT_EQ(res->ContentLength, static_cast<int64_t>(m_blobContent.size()));
EXPECT_EQ(res->HttpHeaders, m_blobUploadOptions.HttpHeaders);
EXPECT_EQ(res->HttpHeaders.ContentHash.Algorithm, Storage::HashAlgorithm::Md5);
EXPECT_EQ(res->Tier.GetValue(), Azure::Storage::Blobs::Models::AccessTier::Cool);
EXPECT_FALSE(res->AccessTierChangeTime.GetValue().empty());
EXPECT_TRUE(IsValidTime(res->AccessTierChangedOn.GetValue()));
}
TEST_F(BlockBlobClientTest, StageBlock)
@ -283,7 +283,7 @@ namespace Azure { namespace Storage { namespace Test {
options.Metadata = m_blobUploadOptions.Metadata;
auto blobContentInfo = blockBlobClient.CommitBlockList({blockId1}, options);
EXPECT_FALSE(blobContentInfo->ETag.empty());
EXPECT_FALSE(blobContentInfo->LastModified.empty());
EXPECT_TRUE(IsValidTime(blobContentInfo->LastModified));
EXPECT_TRUE(blobContentInfo->VersionId.HasValue());
EXPECT_FALSE(blobContentInfo->VersionId.GetValue().empty());
auto res = blockBlobClient.GetBlockList();
@ -291,7 +291,7 @@ namespace Azure { namespace Storage { namespace Test {
EXPECT_FALSE(res.GetRawResponse().GetHeaders().at(Details::HttpHeaderDate).empty());
EXPECT_FALSE(res.GetRawResponse().GetHeaders().at(Details::HttpHeaderXMsVersion).empty());
EXPECT_FALSE(res->ETag.empty());
EXPECT_FALSE(res->LastModified.empty());
EXPECT_TRUE(IsValidTime(res->LastModified));
EXPECT_EQ(res->ContentLength, static_cast<int64_t>(block1Content.size()));
ASSERT_FALSE(res->CommittedBlocks.empty());
EXPECT_EQ(res->CommittedBlocks[0].Name, blockId1);
@ -568,14 +568,14 @@ namespace Azure { namespace Storage { namespace Test {
auto res = blockBlobClient.DownloadTo(emptyContent.data(), 0);
EXPECT_EQ(res->ContentLength, 0);
EXPECT_FALSE(res->ETag.empty());
EXPECT_FALSE(res->LastModified.empty());
EXPECT_TRUE(IsValidTime(res->LastModified));
EXPECT_EQ(res->HttpHeaders, m_blobUploadOptions.HttpHeaders);
EXPECT_EQ(res->Metadata, m_blobUploadOptions.Metadata);
EXPECT_EQ(res->BlobType, Azure::Storage::Blobs::Models::BlobType::BlockBlob);
res = blockBlobClient.DownloadTo(tempFilename);
EXPECT_EQ(res->ContentLength, 0);
EXPECT_FALSE(res->ETag.empty());
EXPECT_FALSE(res->LastModified.empty());
EXPECT_TRUE(IsValidTime(res->LastModified));
EXPECT_EQ(res->HttpHeaders, m_blobUploadOptions.HttpHeaders);
EXPECT_EQ(res->Metadata, m_blobUploadOptions.Metadata);
EXPECT_EQ(res->BlobType, Azure::Storage::Blobs::Models::BlobType::BlockBlob);
@ -585,14 +585,14 @@ namespace Azure { namespace Storage { namespace Test {
res = blockBlobClient.DownloadTo(emptyContent.data(), static_cast<std::size_t>(8_MB));
EXPECT_EQ(res->ContentLength, 0);
EXPECT_FALSE(res->ETag.empty());
EXPECT_FALSE(res->LastModified.empty());
EXPECT_TRUE(IsValidTime(res->LastModified));
EXPECT_EQ(res->HttpHeaders, m_blobUploadOptions.HttpHeaders);
EXPECT_EQ(res->Metadata, m_blobUploadOptions.Metadata);
EXPECT_EQ(res->BlobType, Azure::Storage::Blobs::Models::BlobType::BlockBlob);
res = blockBlobClient.DownloadTo(tempFilename);
EXPECT_EQ(res->ContentLength, 0);
EXPECT_FALSE(res->ETag.empty());
EXPECT_FALSE(res->LastModified.empty());
EXPECT_TRUE(IsValidTime(res->LastModified));
EXPECT_EQ(res->HttpHeaders, m_blobUploadOptions.HttpHeaders);
EXPECT_EQ(res->Metadata, m_blobUploadOptions.Metadata);
EXPECT_EQ(res->BlobType, Azure::Storage::Blobs::Models::BlobType::BlockBlob);
@ -610,14 +610,14 @@ namespace Azure { namespace Storage { namespace Test {
emptyContent.data(), static_cast<std::size_t>(8_MB), options);
EXPECT_EQ(res->ContentLength, 0);
EXPECT_FALSE(res->ETag.empty());
EXPECT_FALSE(res->LastModified.empty());
EXPECT_TRUE(IsValidTime(res->LastModified));
EXPECT_EQ(res->HttpHeaders, m_blobUploadOptions.HttpHeaders);
EXPECT_EQ(res->Metadata, m_blobUploadOptions.Metadata);
EXPECT_EQ(res->BlobType, Azure::Storage::Blobs::Models::BlobType::BlockBlob);
res = blockBlobClient.DownloadTo(tempFilename, options);
EXPECT_EQ(res->ContentLength, 0);
EXPECT_FALSE(res->ETag.empty());
EXPECT_FALSE(res->LastModified.empty());
EXPECT_TRUE(IsValidTime(res->LastModified));
EXPECT_EQ(res->HttpHeaders, m_blobUploadOptions.HttpHeaders);
EXPECT_EQ(res->Metadata, m_blobUploadOptions.Metadata);
EXPECT_EQ(res->BlobType, Azure::Storage::Blobs::Models::BlobType::BlockBlob);
@ -670,7 +670,7 @@ namespace Azure { namespace Storage { namespace Test {
auto res = blockBlobClient.UploadFrom(
blobContent.data(), static_cast<std::size_t>(blobSize), options);
EXPECT_FALSE(res->ETag.empty());
EXPECT_FALSE(res->LastModified.empty());
EXPECT_TRUE(IsValidTime(res->LastModified));
auto properties = *blockBlobClient.GetProperties();
properties.HttpHeaders.ContentHash.Value.clear();
EXPECT_EQ(properties.ContentLength, blobSize);
@ -705,7 +705,7 @@ namespace Azure { namespace Storage { namespace Test {
}
auto res = blockBlobClient.UploadFrom(tempFilename, options);
EXPECT_FALSE(res->ETag.empty());
EXPECT_FALSE(res->LastModified.empty());
EXPECT_TRUE(IsValidTime(res->LastModified));
auto properties = *blockBlobClient.GetProperties();
properties.HttpHeaders.ContentHash.Value.clear();
EXPECT_EQ(properties.ContentLength, blobSize);

View File

@ -50,7 +50,7 @@ namespace Azure { namespace Storage { namespace Test {
StandardStorageConnectionString(), m_containerName, RandomString());
auto blobContentInfo = pageBlobClient.Create(0, m_blobUploadOptions);
EXPECT_FALSE(blobContentInfo->ETag.empty());
EXPECT_FALSE(blobContentInfo->LastModified.empty());
EXPECT_TRUE(IsValidTime(blobContentInfo->LastModified));
EXPECT_TRUE(blobContentInfo->VersionId.HasValue());
EXPECT_FALSE(blobContentInfo->VersionId.GetValue().empty());
EXPECT_FALSE(blobContentInfo->EncryptionScope.HasValue());
@ -147,7 +147,7 @@ namespace Azure { namespace Storage { namespace Test {
sourceUri.AppendQueryParameters(GetSas());
auto copyInfo = pageBlobClient.StartCopyIncremental(sourceUri.GetAbsoluteUrl());
EXPECT_FALSE(copyInfo->ETag.empty());
EXPECT_FALSE(copyInfo->LastModified.empty());
EXPECT_TRUE(IsValidTime(copyInfo->LastModified));
EXPECT_FALSE(copyInfo->CopyId.empty());
EXPECT_NE(copyInfo->CopyStatus, Blobs::Models::CopyStatus::Unknown);
EXPECT_TRUE(copyInfo->VersionId.HasValue());
@ -160,11 +160,11 @@ namespace Azure { namespace Storage { namespace Test {
int32_t leaseDuration = 20;
auto aLease = *m_pageBlobClient->AcquireLease(leaseId1, leaseDuration);
EXPECT_FALSE(aLease.ETag.empty());
EXPECT_FALSE(aLease.LastModified.empty());
EXPECT_TRUE(IsValidTime(aLease.LastModified));
EXPECT_EQ(aLease.LeaseId, leaseId1);
aLease = *m_pageBlobClient->AcquireLease(leaseId1, leaseDuration);
EXPECT_FALSE(aLease.ETag.empty());
EXPECT_FALSE(aLease.LastModified.empty());
EXPECT_TRUE(IsValidTime(aLease.LastModified));
EXPECT_EQ(aLease.LeaseId, leaseId1);
auto properties = *m_pageBlobClient->GetProperties();
@ -174,32 +174,32 @@ namespace Azure { namespace Storage { namespace Test {
auto rLease = *m_pageBlobClient->RenewLease(leaseId1);
EXPECT_FALSE(rLease.ETag.empty());
EXPECT_FALSE(rLease.LastModified.empty());
EXPECT_TRUE(IsValidTime(rLease.LastModified));
EXPECT_EQ(rLease.LeaseId, leaseId1);
std::string leaseId2 = CreateUniqueLeaseId();
EXPECT_NE(leaseId1, leaseId2);
auto cLease = *m_pageBlobClient->ChangeLease(leaseId1, leaseId2);
EXPECT_FALSE(cLease.ETag.empty());
EXPECT_FALSE(cLease.LastModified.empty());
EXPECT_TRUE(IsValidTime(cLease.LastModified));
EXPECT_EQ(cLease.LeaseId, leaseId2);
auto blobInfo = *m_pageBlobClient->ReleaseLease(leaseId2);
EXPECT_FALSE(blobInfo.ETag.empty());
EXPECT_FALSE(blobInfo.LastModified.empty());
EXPECT_TRUE(IsValidTime(blobInfo.LastModified));
aLease = *m_pageBlobClient->AcquireLease(CreateUniqueLeaseId(), InfiniteLeaseDuration);
properties = *m_pageBlobClient->GetProperties();
EXPECT_FALSE(properties.LeaseDuration.GetValue().empty());
auto brokenLease = *m_pageBlobClient->BreakLease();
EXPECT_FALSE(brokenLease.ETag.empty());
EXPECT_FALSE(brokenLease.LastModified.empty());
EXPECT_TRUE(IsValidTime(brokenLease.LastModified));
EXPECT_EQ(brokenLease.LeaseTime, 0);
aLease = *m_pageBlobClient->AcquireLease(CreateUniqueLeaseId(), leaseDuration);
brokenLease = *m_pageBlobClient->BreakLease();
EXPECT_FALSE(brokenLease.ETag.empty());
EXPECT_FALSE(brokenLease.LastModified.empty());
EXPECT_TRUE(IsValidTime(brokenLease.LastModified));
EXPECT_NE(brokenLease.LeaseTime, 0);
Blobs::BreakBlobLeaseOptions options;

View File

@ -87,7 +87,9 @@ namespace Azure { namespace Storage { namespace Test {
response->AddHeader("x-ms-request-id", Core::Uuid::CreateUuid().GetUuidString());
response->AddHeader("x-ms-version", Blobs::Details::ApiVersion);
response->AddHeader("x-ms-error-code", "BlobNotFound");
response->AddHeader("date", ToRfc1123(std::chrono::system_clock::now()));
response->AddHeader(
"date",
Azure::Core::DateTime::Now().GetString(Azure::Core::DateTime::DateFormat::Rfc1123));
return response;
};
auto ConstructPreconditionFailedResponse = []() {
@ -109,36 +111,40 @@ namespace Azure { namespace Storage { namespace Test {
response->AddHeader("x-ms-request-id", Core::Uuid::CreateUuid().GetUuidString());
response->AddHeader("x-ms-version", Blobs::Details::ApiVersion);
response->AddHeader("x-ms-error-code", "ConditionNotMet");
response->AddHeader("date", ToRfc1123(std::chrono::system_clock::now()));
response->AddHeader(
"date",
Azure::Core::DateTime::Now().GetString(Azure::Core::DateTime::DateFormat::Rfc1123));
return response;
};
auto ConstructPrimaryResponse
= [requestOffset, requestLength, this, ConstructNotFoundResponse]() {
if (!m_primaryContent)
{
return ConstructNotFoundResponse();
}
auto response = std::make_unique<Core::Http::RawResponse>(
Core::Http::RawResponse(1, 1, Core::Http::HttpStatusCode::Ok, "OK"));
int64_t bodyLength = std::min(
static_cast<int64_t>(m_primaryContent->length()) - requestOffset, requestLength);
auto bodyStream = std::make_unique<Core::Http::MemoryBodyStream>(
reinterpret_cast<const uint8_t*>(m_primaryContent->data() + requestOffset),
bodyLength);
response->SetBodyStream(std::move(bodyStream));
response->AddHeader("content-length", std::to_string(bodyLength));
response->AddHeader("etag", m_primaryETag);
response->AddHeader("last-modified", "Thu 27 Aug 2001 07:00:00 GMT");
response->AddHeader("x-ms-request-id", Core::Uuid::CreateUuid().GetUuidString());
response->AddHeader("x-ms-version", Blobs::Details::ApiVersion);
response->AddHeader("x-ms-creation-time", "Thu 27 Aug 2002 07:00:00 GMT");
response->AddHeader("x-ms-lease-status", "unlocked");
response->AddHeader("x-ms-lease-state", "available");
response->AddHeader("x-ms-blob-type", "BlockBlob");
response->AddHeader("x-ms-server-encrypted", "true");
response->AddHeader("date", ToRfc1123(std::chrono::system_clock::now()));
return response;
};
auto ConstructPrimaryResponse =
[requestOffset, requestLength, this, ConstructNotFoundResponse]() {
if (!m_primaryContent)
{
return ConstructNotFoundResponse();
}
auto response = std::make_unique<Core::Http::RawResponse>(
Core::Http::RawResponse(1, 1, Core::Http::HttpStatusCode::Ok, "OK"));
int64_t bodyLength = std::min(
static_cast<int64_t>(m_primaryContent->length()) - requestOffset, requestLength);
auto bodyStream = std::make_unique<Core::Http::MemoryBodyStream>(
reinterpret_cast<const uint8_t*>(m_primaryContent->data() + requestOffset),
bodyLength);
response->SetBodyStream(std::move(bodyStream));
response->AddHeader("content-length", std::to_string(bodyLength));
response->AddHeader("etag", m_primaryETag);
response->AddHeader("last-modified", "Thu, 23 Aug 2001 07:00:00 GMT");
response->AddHeader("x-ms-request-id", Core::Uuid::CreateUuid().GetUuidString());
response->AddHeader("x-ms-version", Blobs::Details::ApiVersion);
response->AddHeader("x-ms-creation-time", "Thu, 22 Aug 2002 07:00:00 GMT");
response->AddHeader("x-ms-lease-status", "unlocked");
response->AddHeader("x-ms-lease-state", "available");
response->AddHeader("x-ms-blob-type", "BlockBlob");
response->AddHeader("x-ms-server-encrypted", "true");
response->AddHeader(
"date",
Azure::Core::DateTime::Now().GetString(Azure::Core::DateTime::DateFormat::Rfc1123));
return response;
};
auto ConstructSecondaryResponse =
[requestOffset, requestLength, this, ConstructNotFoundResponse]() {
if (!m_secondaryContent)
@ -155,15 +161,17 @@ namespace Azure { namespace Storage { namespace Test {
response->SetBodyStream(std::move(bodyStream));
response->AddHeader("content-length", std::to_string(bodyLength));
response->AddHeader("etag", m_secondaryETag);
response->AddHeader("last-modified", "Thu 27 Aug 2001 07:00:00 GMT");
response->AddHeader("last-modified", "Thu, 23 Aug 2001 07:00:00 GMT");
response->AddHeader("x-ms-request-id", Core::Uuid::CreateUuid().GetUuidString());
response->AddHeader("x-ms-version", Blobs::Details::ApiVersion);
response->AddHeader("x-ms-creation-time", "Thu 27 Aug 2002 07:00:00 GMT");
response->AddHeader("x-ms-creation-time", "Thu, 22 Aug 2002 07:00:00 GMT");
response->AddHeader("x-ms-lease-status", "unlocked");
response->AddHeader("x-ms-lease-state", "available");
response->AddHeader("x-ms-blob-type", "BlockBlob");
response->AddHeader("x-ms-server-encrypted", "true");
response->AddHeader("date", ToRfc1123(std::chrono::system_clock::now()));
response->AddHeader(
"date",
Azure::Core::DateTime::Now().GetString(Azure::Core::DateTime::DateFormat::Rfc1123));
return response;
};

View File

@ -2,12 +2,18 @@
## 12.0.0-beta.6 (Unreleased)
### New Features
- Add new type `ContentHash`.
### Breaking Changes
- Rename `SharedKeyCredential` to `StorageSharedKeyCredential`.
- Rename `StorageSharedKeyCredential::UpdateAccountKey` to `StorageSharedKeyCredential::Update`.
- Move `StorageRetryPolicy`, `StoragePerRetryPolicy` and `SharedKeyPolicy` to `Details` namespace.
- Remove `StorageRetryOptions`, use `Azure::Core::Http::RetryOptions` instead.
- Move Account SAS into `Azure::Storage::Sas` namespace.
- Add new type `ContentHash`.
- All date time related strings are now changed to `Azure::Core::DateTime` type.
## 12.0.0-beta.5 (2020-11-13)

View File

@ -18,15 +18,15 @@ namespace Azure { namespace Storage {
{
/**
* @brief Specify this header to perform the operation only if the resource has been
* modified since the specified time.
* modified since the specified time. This timestamp will be truncated to second.
*/
Azure::Core::Nullable<std::string> IfModifiedSince;
Azure::Core::Nullable<Azure::Core::DateTime> IfModifiedSince;
/**
* @brief Specify this header to perform the operation only if the resource has not been
* modified since the specified date/time.
* modified since the specified date/time. This timestamp will be truncated to second.
*/
Azure::Core::Nullable<std::string> IfUnmodifiedSince;
Azure::Core::Nullable<Azure::Core::DateTime> IfUnmodifiedSince;
};
/**

View File

@ -6,6 +6,7 @@
#include <string>
#include <type_traits>
#include <azure/core/datetime.hpp>
#include <azure/core/nullable.hpp>
#include "azure/storage/common/constants.hpp"
@ -216,13 +217,13 @@ namespace Azure { namespace Storage { namespace Sas {
* @brief Optionally specify the time at which the shared access signature becomes
* valid.
*/
Azure::Core::Nullable<std::string> StartsOn;
Azure::Core::Nullable<Azure::Core::DateTime> 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;
Azure::Core::DateTime ExpiresOn;
/**
* @brief Specifies an IP address or a range of IP addresses from which to accept

View File

@ -91,9 +91,15 @@ namespace Azure { namespace Storage { namespace Sas {
resourceTypes += "o";
}
std::string startsOnStr = StartsOn.HasValue()
? StartsOn.GetValue().GetRfc3339String(Azure::Core::DateTime::TimeFractionFormat::Truncate)
: "";
std::string expiresOnStr
= ExpiresOn.GetRfc3339String(Azure::Core::DateTime::TimeFractionFormat::Truncate);
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"
+ resourceTypes + "\n" + startsOnStr + "\n" + expiresOnStr + "\n"
+ (IPRange.HasValue() ? IPRange.GetValue() : "") + "\n" + protocol + "\n"
+ Storage::Details::DefaultSasVersion + "\n";
std::string signature = Base64Encode(Storage::Details::HmacSha256(
@ -106,12 +112,11 @@ namespace Azure { namespace Storage { namespace Sas {
builder.AppendQueryParameter("ss", Storage::Details::UrlEncodeQueryParameter(services));
builder.AppendQueryParameter("srt", Storage::Details::UrlEncodeQueryParameter(resourceTypes));
builder.AppendQueryParameter("sp", Storage::Details::UrlEncodeQueryParameter(Permissions));
if (StartsOn.HasValue())
if (!startsOnStr.empty())
{
builder.AppendQueryParameter(
"st", Storage::Details::UrlEncodeQueryParameter(StartsOn.GetValue()));
builder.AppendQueryParameter("st", startsOnStr);
}
builder.AppendQueryParameter("se", Storage::Details::UrlEncodeQueryParameter(ExpiresOn));
builder.AppendQueryParameter("se", expiresOnStr);
if (IPRange.HasValue())
{
builder.AppendQueryParameter(

View File

@ -235,66 +235,6 @@ namespace Azure { namespace Storage { namespace Test {
return result;
}
std::string ToIso8601(
const std::chrono::system_clock::time_point& timePoint,
int numDecimalDigits)
{
std::time_t epoch_seconds = std::chrono::system_clock::to_time_t(timePoint);
struct tm ct;
#if defined(AZ_PLATFORM_WINDOWS)
gmtime_s(&ct, &epoch_seconds);
#elif defined(AZ_PLATFORM_POSIX)
gmtime_r(&epoch_seconds, &ct);
#endif
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>(timePoint);
auto decimal_part = timePoint - 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& timePoint)
{
std::time_t epoch_seconds = std::chrono::system_clock::to_time_t(timePoint);
struct tm ct;
#if defined(AZ_PLATFORM_WINDOWS)
gmtime_s(&ct, &epoch_seconds);
#elif defined(AZ_PLATFORM_POSIX)
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();
}
std::chrono::system_clock::time_point FromRfc1123(const std::string& timeStr)
{
std::tm t;
std::stringstream ss(timeStr);
ss.imbue(std::locale("C"));
ss >> std::get_time(&t, "%a, %d %b %Y %H:%M:%S GMT");
#if defined(AZ_PLATFORM_WINDOWS)
time_t tt = _mkgmtime(&t);
#elif defined(AZ_PLATFORM_POSIX)
time_t tt = timegm(&t);
#endif
return std::chrono::system_clock::from_time_t(tt);
}
std::string InferSecondaryUrl(const std::string primaryUrl)
{
Azure::Core::Http::Url secondaryUri(primaryUrl);
@ -306,4 +246,12 @@ namespace Azure { namespace Storage { namespace Test {
return secondaryUri.GetAbsoluteUrl();
}
bool IsValidTime(const Azure::Core::DateTime& datetime)
{
// We assume datetime within a week is valid.
const auto minTime = Azure::Core::DateTime::Now() - std::chrono::hours(24 * 7);
const auto maxTime = Azure::Core::DateTime::Now() + std::chrono::hours(24 * 7);
return datetime > minTime && datetime < maxTime;
}
}}} // namespace Azure::Storage::Test

View File

@ -8,6 +8,7 @@
#include <limits>
#include <vector>
#include <azure/core/datetime.hpp>
#include <azure/core/http/body_stream.hpp>
#include <azure/storage/common/constants.hpp>
#include <azure/storage/common/storage_common.hpp>
@ -78,13 +79,8 @@ namespace Azure { namespace Storage { namespace Test {
void DeleteFile(const std::string& filename);
std::string ToIso8601(
const std::chrono::system_clock::time_point& timePoint,
int numDecimalDigits = 0);
std::string ToRfc1123(const std::chrono::system_clock::time_point& timePoint);
std::chrono::system_clock::time_point FromRfc1123(const std::string& timeStr);
std::string InferSecondaryUrl(const std::string primaryUri);
bool IsValidTime(const Azure::Core::DateTime& datetime);
}}} // namespace Azure::Storage::Test

View File

@ -8,6 +8,12 @@
- `EncrytionKeySha256` are changed to binary(`std::vector<uint8_t>`).
- Replaced all transactional content MD5/CRC64 with `ContentHash` struct.
- `DataLakeHttpHeaders` is renamed to `PathHttpHeaders`, and now contains `ContentHash` for the resource.
- All date time related strings are now changed to `Azure::Core::DateTime` type.
- `CreationTime` is renamed to `CreatedOn`.
- `AccessTierChangeTime` is renamed to `AccessTierChangedOn`.
- `CopyCompletionTime` is renamed to `CopyCompletedOn`.
- `ExpiryTime` is renamed to `ExpiresOn`.
- `LastAccessTime` is renamed to `LastAccessedOn`.
## 12.0.0-beta.5 (2020-11-13)

View File

@ -27,7 +27,7 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake { nam
struct GetFileSystemPropertiesResult
{
std::string ETag;
std::string LastModified;
Core::DateTime LastModified;
Storage::Metadata Metadata;
};
@ -82,8 +82,8 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake { nam
struct GetPathPropertiesResult
{
std::string ETag;
std::string LastModified;
std::string CreationTime;
Core::DateTime LastModified;
Core::DateTime CreatedOn;
int64_t ContentLength;
Storage::Metadata Metadata;
Azure::Core::Nullable<std::string> LeaseDuration;
@ -93,39 +93,39 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake { nam
Azure::Core::Nullable<bool> ServerEncrypted;
Azure::Core::Nullable<std::vector<uint8_t>> EncryptionKeySha256;
Azure::Core::Nullable<bool> AccessTierInferred;
Azure::Core::Nullable<std::string> AccessTierChangeTime;
Azure::Core::Nullable<Core::DateTime> AccessTierChangedOn;
Azure::Core::Nullable<std::string> CopyId;
Azure::Core::Nullable<std::string> CopySource;
Azure::Core::Nullable<Blobs::Models::CopyStatus> CopyStatus;
Azure::Core::Nullable<std::string> CopyProgress;
Azure::Core::Nullable<std::string> CopyCompletionTime;
Azure::Core::Nullable<std::string> ExpiryTime;
Azure::Core::Nullable<std::string> LastAccessTime;
Azure::Core::Nullable<Core::DateTime> CopyCompletedOn;
Azure::Core::Nullable<Core::DateTime> ExpiresOn;
Azure::Core::Nullable<Core::DateTime> LastAccessedOn;
};
struct GetPathAccessControlResult
{
std::string ETag;
std::string LastModified;
Core::DateTime LastModified;
std::vector<Acl> Acls;
};
struct SetPathHttpHeadersResult
{
std::string ETag;
std::string LastModified;
Core::DateTime LastModified;
};
struct SetPathMetadataResult
{
std::string ETag;
std::string LastModified;
Core::DateTime LastModified;
};
struct CreatePathResult
{
std::string ETag;
std::string LastModified;
Core::DateTime LastModified;
Azure::Core::Nullable<int64_t> ContentLength;
};
@ -146,14 +146,14 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake { nam
Azure::Core::Nullable<int64_t> RangeLength;
Azure::Core::Nullable<Storage::ContentHash> TransactionalContentHash;
std::string ETag;
std::string LastModified;
Core::DateTime LastModified;
Azure::Core::Nullable<std::string> LeaseDuration;
LeaseStateType LeaseState = LeaseStateType::Unknown;
LeaseStatusType LeaseStatus = LeaseStatusType::Unknown;
Storage::Metadata Metadata;
std::string CreationTime;
Azure::Core::Nullable<std::string> ExpiryTime;
Azure::Core::Nullable<std::string> LastAccessTime;
Core::DateTime CreatedOn;
Azure::Core::Nullable<Core::DateTime> ExpiresOn;
Azure::Core::Nullable<Core::DateTime> LastAccessedOn;
};
struct RenameFileResult
@ -167,7 +167,7 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake { nam
struct DownloadFileToResult
{
std::string ETag;
std::string LastModified;
Core::DateTime LastModified;
int64_t ContentLength = 0;
PathHttpHeaders HttpHeaders;
Storage::Metadata Metadata;

View File

@ -186,15 +186,16 @@ namespace Azure { namespace Storage { namespace Sas {
/**
* @brief Optionally specify the time at which the shared access signature becomes
* valid.
* valid. This timestamp will be truncated to second.
*/
Azure::Core::Nullable<std::string> StartsOn;
Azure::Core::Nullable<Azure::Core::DateTime> 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.
* be omitted if it has been specified in an associated stored access policy. This timestamp
* will be truncated to second.
*/
std::string ExpiresOn;
Azure::Core::DateTime ExpiresOn;
/**
* @brief Specifies an IP address or a range of IP addresses from which to accept

View File

@ -100,10 +100,10 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake {
* @brief Retrieves a key that can be used to delegate Active Directory authorization to
* shared access signatures.
*
* @param startsOn Start time for the key's validity, in ISO date format. The time should be
* specified in UTC.
* @param expiresOn Expiration of the key's validity, in ISO date format. The time should be
* specified in UTC.
* @param startsOn Start time for the key's validity. The time should be specified in UTC, and
* will be truncated to second.
* @param expiresOn Expiration of the key's validity. The time should be specified in UTC, and
* will be truncated to second.
* @param options Optional parameters to execute
* this function.
* @return Azure::Core::Response<Models::GetUserDelegationKeyResult> containing the user
@ -111,8 +111,8 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake {
* @remark This request is sent to blob endpoint.
*/
Azure::Core::Response<Models::GetUserDelegationKeyResult> GetUserDelegationKey(
const std::string& startsOn,
const std::string& expiresOn,
const Azure::Core::DateTime& startsOn,
const Azure::Core::DateTime& expiresOn,
const GetUserDelegationKeyOptions& options = GetUserDelegationKeyOptions()) const
{
return m_blobServiceClient.GetUserDelegationKey(startsOn, expiresOn, options);

View File

@ -52,6 +52,7 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake {
ret.ContentEncoding = std::move(headers.ContentEncoding);
ret.ContentLanguage = std::move(headers.ContentLanguage);
ret.ContentType = std::move(headers.ContentType);
ret.ContentHash = std::move(headers.ContentHash);
return ret;
}
@ -350,9 +351,9 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake {
? FromBlobLeaseStatus(result->LeaseStatus.GetValue())
: ret.LeaseStatus;
ret.Metadata = std::move(result->Metadata);
ret.CreationTime = std::move(result->CreationTime);
ret.ExpiryTime = std::move(result->ExpiryTime);
ret.LastAccessTime = std::move(result->LastAccessTime);
ret.CreatedOn = std::move(result->CreatedOn);
ret.ExpiresOn = std::move(result->ExpiriesOn);
ret.LastAccessedOn = std::move(result->LastAccessedOn);
return Azure::Core::Response<Models::ReadFileResult>(
std::move(ret), result.ExtractRawResponse());
}

View File

@ -248,6 +248,7 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake {
const ListPathsOptions& options) const
{
Details::DataLakeRestClient::FileSystem::ListPathsOptions protocolLayerOptions;
protocolLayerOptions.Resource = Models::FileSystemResourceType::Filesystem;
protocolLayerOptions.Upn = options.UserPrincipalName;
protocolLayerOptions.ContinuationToken = options.ContinuationToken;
protocolLayerOptions.MaxResults = options.MaxResults;

View File

@ -292,7 +292,7 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake {
Models::GetPathPropertiesResult ret;
ret.ETag = std::move(result->ETag);
ret.LastModified = std::move(result->LastModified);
ret.CreationTime = std::move(result->CreationTime);
ret.CreatedOn = std::move(result->CreatedOn);
ret.Metadata = std::move(result->Metadata);
ret.LeaseDuration = std::move(result->LeaseDuration);
ret.LeaseState = result->LeaseState.HasValue()
@ -309,14 +309,14 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake {
ret.ServerEncrypted = result->IsServerEncrypted;
ret.EncryptionKeySha256 = std::move(result->EncryptionKeySha256);
ret.AccessTierInferred = std::move(result->IsAccessTierInferred);
ret.AccessTierChangeTime = std::move(result->AccessTierChangeTime);
ret.AccessTierChangedOn = std::move(result->AccessTierChangedOn);
ret.CopyId = std::move(result->CopyId);
ret.CopySource = std::move(result->CopySource);
ret.CopyStatus = std::move(result->CopyStatus);
ret.CopyProgress = std::move(result->CopyProgress);
ret.CopyCompletionTime = std::move(result->CopyCompletionTime);
ret.ExpiryTime = std::move(result->ExpiryTime);
ret.LastAccessTime = std::move(result->LastAccessTime);
ret.CopyCompletedOn = std::move(result->CopyCompletedOn);
ret.ExpiresOn = std::move(result->ExpiriesOn);
ret.LastAccessedOn = std::move(result->LastAccessedOn);
ret.ContentLength = result->ContentLength;
return Azure::Core::Response<Models::GetPathPropertiesResult>(
std::move(ret), result.ExtractRawResponse());

View File

@ -123,11 +123,18 @@ namespace Azure { namespace Storage { namespace Sas {
std::string protocol = Details::SasProtocolToString(Protocol);
std::string resource = DataLakeSasResourceToString(Resource);
std::string stringToSign = Permissions + "\n" + (StartsOn.HasValue() ? StartsOn.GetValue() : "")
+ "\n" + ExpiresOn + "\n" + canonicalName + "\n" + Identifier + "\n"
+ (IPRange.HasValue() ? IPRange.GetValue() : "") + "\n" + protocol + "\n"
+ Storage::Details::DefaultSasVersion + "\n" + resource + "\n" + "\n" + CacheControl + "\n"
+ ContentDisposition + "\n" + ContentEncoding + "\n" + ContentLanguage + "\n" + ContentType;
std::string startsOnStr = StartsOn.HasValue()
? StartsOn.GetValue().GetRfc3339String(Azure::Core::DateTime::TimeFractionFormat::Truncate)
: "";
std::string expiresOnStr = Identifier.empty()
? ExpiresOn.GetRfc3339String(Azure::Core::DateTime::TimeFractionFormat::Truncate)
: "";
std::string stringToSign = Permissions + "\n" + startsOnStr + "\n" + expiresOnStr + "\n"
+ canonicalName + "\n" + Identifier + "\n" + (IPRange.HasValue() ? IPRange.GetValue() : "")
+ "\n" + protocol + "\n" + Storage::Details::DefaultSasVersion + "\n" + resource + "\n"
+ "\n" + CacheControl + "\n" + ContentDisposition + "\n" + ContentEncoding + "\n"
+ ContentLanguage + "\n" + ContentType;
std::string signature = Base64Encode(Storage::Details::HmacSha256(
std::vector<uint8_t>(stringToSign.begin(), stringToSign.end()),
@ -137,14 +144,13 @@ namespace Azure { namespace Storage { namespace Sas {
builder.AppendQueryParameter(
"sv", Storage::Details::UrlEncodeQueryParameter(Storage::Details::DefaultSasVersion));
builder.AppendQueryParameter("spr", Storage::Details::UrlEncodeQueryParameter(protocol));
if (StartsOn.HasValue())
if (!startsOnStr.empty())
{
builder.AppendQueryParameter(
"st", Storage::Details::UrlEncodeQueryParameter(StartsOn.GetValue()));
builder.AppendQueryParameter("st", Storage::Details::UrlEncodeQueryParameter(startsOnStr));
}
if (!ExpiresOn.empty())
if (!expiresOnStr.empty())
{
builder.AppendQueryParameter("se", Storage::Details::UrlEncodeQueryParameter(ExpiresOn));
builder.AppendQueryParameter("se", Storage::Details::UrlEncodeQueryParameter(expiresOnStr));
}
if (IPRange.HasValue())
{
@ -200,15 +206,24 @@ namespace Azure { namespace Storage { namespace Sas {
std::string protocol = Details::SasProtocolToString(Protocol);
std::string resource = DataLakeSasResourceToString(Resource);
std::string stringToSign = Permissions + "\n" + (StartsOn.HasValue() ? StartsOn.GetValue() : "")
+ "\n" + ExpiresOn + "\n" + canonicalName + "\n" + userDelegationKey.SignedObjectId + "\n"
+ userDelegationKey.SignedTenantId + "\n" + userDelegationKey.SignedStartsOn + "\n"
+ userDelegationKey.SignedExpiresOn + "\n" + userDelegationKey.SignedService + "\n"
+ userDelegationKey.SignedVersion + "\n" + PreauthorizedAgentObjectId + "\n" + AgentObjectId
+ "\n" + CorrelationId + "\n" + (IPRange.HasValue() ? IPRange.GetValue() : "") + "\n"
+ protocol + "\n" + Storage::Details::DefaultSasVersion + "\n" + resource + "\n" + "\n"
+ CacheControl + "\n" + ContentDisposition + "\n" + ContentEncoding + "\n" + ContentLanguage
+ "\n" + ContentType;
std::string startsOnStr = StartsOn.HasValue()
? StartsOn.GetValue().GetRfc3339String(Azure::Core::DateTime::TimeFractionFormat::Truncate)
: "";
std::string expiresOnStr
= ExpiresOn.GetRfc3339String(Azure::Core::DateTime::TimeFractionFormat::Truncate);
std::string signedStartsOnStr = userDelegationKey.SignedStartsOn.GetRfc3339String(
Azure::Core::DateTime::TimeFractionFormat::Truncate);
std::string signedExpiresOnStr = userDelegationKey.SignedExpiresOn.GetRfc3339String(
Azure::Core::DateTime::TimeFractionFormat::Truncate);
std::string stringToSign = Permissions + "\n" + startsOnStr + "\n" + expiresOnStr + "\n"
+ canonicalName + "\n" + userDelegationKey.SignedObjectId + "\n"
+ userDelegationKey.SignedTenantId + "\n" + signedStartsOnStr + "\n" + signedExpiresOnStr
+ "\n" + userDelegationKey.SignedService + "\n" + userDelegationKey.SignedVersion + "\n"
+ PreauthorizedAgentObjectId + "\n" + AgentObjectId + "\n" + CorrelationId + "\n"
+ (IPRange.HasValue() ? IPRange.GetValue() : "") + "\n" + protocol + "\n"
+ Storage::Details::DefaultSasVersion + "\n" + resource + "\n" + "\n" + CacheControl + "\n"
+ ContentDisposition + "\n" + ContentEncoding + "\n" + ContentLanguage + "\n" + ContentType;
std::string signature = Base64Encode(Storage::Details::HmacSha256(
std::vector<uint8_t>(stringToSign.begin(), stringToSign.end()),
@ -218,12 +233,11 @@ namespace Azure { namespace Storage { namespace Sas {
builder.AppendQueryParameter(
"sv", Storage::Details::UrlEncodeQueryParameter(Storage::Details::DefaultSasVersion));
builder.AppendQueryParameter("sr", Storage::Details::UrlEncodeQueryParameter(resource));
if (StartsOn.HasValue())
if (!startsOnStr.empty())
{
builder.AppendQueryParameter(
"st", Storage::Details::UrlEncodeQueryParameter(StartsOn.GetValue()));
builder.AppendQueryParameter("st", Storage::Details::UrlEncodeQueryParameter(startsOnStr));
}
builder.AppendQueryParameter("se", Storage::Details::UrlEncodeQueryParameter(ExpiresOn));
builder.AppendQueryParameter("se", Storage::Details::UrlEncodeQueryParameter(expiresOnStr));
builder.AppendQueryParameter("sp", Permissions);
if (IPRange.HasValue())
{
@ -236,9 +250,9 @@ namespace Azure { namespace Storage { namespace Sas {
builder.AppendQueryParameter(
"sktid", Storage::Details::UrlEncodeQueryParameter(userDelegationKey.SignedTenantId));
builder.AppendQueryParameter(
"skt", Storage::Details::UrlEncodeQueryParameter(userDelegationKey.SignedStartsOn));
"skt", Storage::Details::UrlEncodeQueryParameter(signedStartsOnStr));
builder.AppendQueryParameter(
"ske", Storage::Details::UrlEncodeQueryParameter(userDelegationKey.SignedExpiresOn));
"ske", Storage::Details::UrlEncodeQueryParameter(signedExpiresOnStr));
builder.AppendQueryParameter(
"sks", Storage::Details::UrlEncodeQueryParameter(userDelegationKey.SignedService));
builder.AppendQueryParameter(

View File

@ -59,6 +59,7 @@ namespace Azure { namespace Storage { namespace Test {
auto response = client.GetProperties();
Files::DataLake::DeleteDirectoryOptions options1;
options1.AccessConditions.IfModifiedSince = response->LastModified;
EXPECT_TRUE(IsValidTime(response->LastModified));
EXPECT_THROW(client.Delete(false, options1), StorageException);
Files::DataLake::DeleteDirectoryOptions options2;
options2.AccessConditions.IfUnmodifiedSince = response->LastModified;
@ -145,6 +146,7 @@ namespace Azure { namespace Storage { namespace Test {
auto response = client.GetProperties();
Files::DataLake::RenameDirectoryOptions options1;
options1.SourceAccessConditions.IfModifiedSince = response->LastModified;
EXPECT_TRUE(IsValidTime(response->LastModified));
EXPECT_THROW(client.Rename(LowercaseRandomString(), options1), StorageException);
Files::DataLake::RenameDirectoryOptions options2;
options2.SourceAccessConditions.IfUnmodifiedSince = response->LastModified;
@ -266,6 +268,7 @@ namespace Azure { namespace Storage { namespace Test {
auto properties1 = m_directoryClient->GetProperties();
auto properties2 = m_directoryClient->GetProperties();
EXPECT_EQ(properties1->ETag, properties2->ETag);
EXPECT_TRUE(IsValidTime(properties1->LastModified));
EXPECT_EQ(properties1->LastModified, properties2->LastModified);
// This operation changes ETag/LastModified.

View File

@ -75,6 +75,7 @@ namespace Azure { namespace Storage { namespace Test {
auto response = client.GetProperties();
Files::DataLake::FileDeleteOptions options1;
options1.AccessConditions.IfModifiedSince = response->LastModified;
EXPECT_TRUE(IsValidTime(response->LastModified));
EXPECT_THROW(client.Delete(options1), StorageException);
Files::DataLake::FileDeleteOptions options2;
options2.AccessConditions.IfUnmodifiedSince = response->LastModified;
@ -263,6 +264,7 @@ namespace Azure { namespace Storage { namespace Test {
auto properties1 = m_fileClient->GetProperties();
auto properties2 = m_fileClient->GetProperties();
EXPECT_EQ(properties1->ETag, properties2->ETag);
EXPECT_TRUE(IsValidTime(properties1->LastModified));
EXPECT_EQ(properties1->LastModified, properties2->LastModified);
// This operation changes ETag/LastModified.
@ -309,6 +311,7 @@ namespace Azure { namespace Storage { namespace Test {
auto properties2 = m_fileClient->GetProperties();
// Append does not change etag because not committed yet.
EXPECT_EQ(properties1->ETag, properties2->ETag);
EXPECT_TRUE(IsValidTime(properties1->LastModified));
EXPECT_EQ(properties1->LastModified, properties2->LastModified);
// Flush
@ -339,6 +342,7 @@ namespace Azure { namespace Storage { namespace Test {
auto properties2 = newFileClient->GetProperties();
// Append does not change etag because not committed yet.
EXPECT_EQ(properties1->ETag, properties2->ETag);
EXPECT_TRUE(IsValidTime(properties1->LastModified));
EXPECT_EQ(properties1->LastModified, properties2->LastModified);
// Flush
@ -376,6 +380,7 @@ namespace Azure { namespace Storage { namespace Test {
auto response = newFileClient->GetProperties();
Files::DataLake::ReadFileOptions options1;
options1.AccessConditions.IfModifiedSince = response->LastModified;
EXPECT_TRUE(IsValidTime(response->LastModified));
EXPECT_THROW(newFileClient->Read(options1), StorageException);
Files::DataLake::ReadFileOptions options2;
options2.AccessConditions.IfUnmodifiedSince = response->LastModified;
@ -452,13 +457,16 @@ namespace Azure { namespace Storage { namespace Test {
options.Metadata = RandomMetadata();
auto res
= fileClient.UploadFrom(fileContent.data(), static_cast<std::size_t>(fileSize), options);
auto lastModified = fileClient.GetProperties()->LastModified;
EXPECT_FALSE(res->ETag.empty());
EXPECT_FALSE(res->LastModified.empty());
EXPECT_TRUE(IsValidTime(res->LastModified));
EXPECT_EQ(res->LastModified, lastModified);
auto properties = *fileClient.GetProperties();
EXPECT_EQ(properties.ContentLength, fileSize);
EXPECT_EQ(properties.HttpHeaders, options.HttpHeaders);
EXPECT_EQ(properties.Metadata, options.Metadata);
EXPECT_EQ(properties.ETag, res->ETag);
EXPECT_TRUE(IsValidTime(res->LastModified));
EXPECT_EQ(properties.LastModified, res->LastModified);
std::vector<uint8_t> downloadContent(static_cast<std::size_t>(fileSize), '\x00');
fileClient.DownloadTo(downloadContent.data(), static_cast<std::size_t>(fileSize));
@ -483,8 +491,10 @@ namespace Azure { namespace Storage { namespace Test {
fileWriter.Write(fileContent.data(), fileSize, 0);
}
auto res = fileClient.UploadFrom(tempFilename, options);
auto lastModified = fileClient.GetProperties()->LastModified;
EXPECT_FALSE(res->ETag.empty());
EXPECT_FALSE(res->LastModified.empty());
EXPECT_TRUE(IsValidTime(res->LastModified));
EXPECT_EQ(res->LastModified, lastModified);
auto properties = *fileClient.GetProperties();
EXPECT_EQ(properties.ContentLength, fileSize);
EXPECT_EQ(properties.HttpHeaders, options.HttpHeaders);

View File

@ -241,13 +241,14 @@ namespace Azure { namespace Storage { namespace Test {
{
std::string leaseId1 = CreateUniqueLeaseId();
int32_t leaseDuration = 20;
auto lastModified = m_pathClient->GetProperties()->LastModified;
auto aLease = *m_pathClient->AcquireLease(leaseId1, leaseDuration);
EXPECT_FALSE(aLease.ETag.empty());
EXPECT_FALSE(aLease.LastModified.empty());
EXPECT_FALSE(aLease.LastModified > lastModified);
EXPECT_EQ(aLease.LeaseId, leaseId1);
aLease = *m_pathClient->AcquireLease(leaseId1, leaseDuration);
EXPECT_FALSE(aLease.ETag.empty());
EXPECT_FALSE(aLease.LastModified.empty());
EXPECT_FALSE(aLease.LastModified > lastModified);
EXPECT_EQ(aLease.LeaseId, leaseId1);
auto properties = *m_pathClient->GetProperties();
@ -255,36 +256,41 @@ namespace Azure { namespace Storage { namespace Test {
EXPECT_EQ(properties.LeaseStatus.GetValue(), Files::DataLake::Models::LeaseStatusType::Locked);
EXPECT_FALSE(properties.LeaseDuration.GetValue().empty());
lastModified = properties.LastModified;
auto rLease = *m_pathClient->RenewLease(leaseId1);
EXPECT_FALSE(rLease.ETag.empty());
EXPECT_FALSE(rLease.LastModified.empty());
EXPECT_FALSE(rLease.LastModified > lastModified);
EXPECT_EQ(rLease.LeaseId, leaseId1);
std::string leaseId2 = CreateUniqueLeaseId();
EXPECT_NE(leaseId1, leaseId2);
lastModified = m_pathClient->GetProperties()->LastModified;
auto cLease = *m_pathClient->ChangeLease(leaseId1, leaseId2);
EXPECT_FALSE(cLease.ETag.empty());
EXPECT_FALSE(cLease.LastModified.empty());
EXPECT_FALSE(cLease.LastModified > lastModified);
EXPECT_EQ(cLease.LeaseId, leaseId2);
lastModified = m_pathClient->GetProperties()->LastModified;
auto pathInfo = *m_pathClient->ReleaseLease(leaseId2);
EXPECT_FALSE(pathInfo.ETag.empty());
EXPECT_FALSE(pathInfo.LastModified.empty());
EXPECT_FALSE(pathInfo.LastModified > lastModified);
lastModified = m_pathClient->GetProperties()->LastModified;
aLease = *m_pathClient->AcquireLease(CreateUniqueLeaseId(), InfiniteLeaseDuration);
properties = *m_pathClient->GetProperties();
EXPECT_FALSE(properties.LeaseDuration.GetValue().empty());
auto brokenLease = *m_pathClient->BreakLease();
EXPECT_FALSE(brokenLease.ETag.empty());
EXPECT_FALSE(brokenLease.LastModified.empty());
EXPECT_FALSE(brokenLease.LastModified > lastModified);
EXPECT_EQ(brokenLease.LeaseTime, 0);
aLease = *m_pathClient->AcquireLease(CreateUniqueLeaseId(), leaseDuration);
Files::DataLake::BreakPathLeaseOptions breakOptions;
breakOptions.BreakPeriod = 30;
lastModified = m_pathClient->GetProperties()->LastModified;
brokenLease = *m_pathClient->BreakLease(breakOptions);
EXPECT_FALSE(brokenLease.ETag.empty());
EXPECT_FALSE(brokenLease.LastModified.empty());
EXPECT_FALSE(brokenLease.LastModified > lastModified);
EXPECT_NE(brokenLease.LeaseTime, 0);
Files::DataLake::BreakPathLeaseOptions options;

View File

@ -11,14 +11,17 @@ namespace Azure { namespace Storage { namespace Test {
TEST_F(DataLakeFileSystemClientTest, DataLakeSasTest)
{
auto sasStartsOn = Azure::Core::DateTime::Now() - std::chrono::minutes(5);
auto sasExpiredOn = Azure::Core::DateTime::Now() - std::chrono::minutes(1);
auto sasExpiresOn = Azure::Core::DateTime::Now() + std::chrono::minutes(60);
std::string directory1Name = RandomString();
std::string directory2Name = RandomString();
std::string fileName = RandomString();
Sas::DataLakeSasBuilder fileSasBuilder;
fileSasBuilder.Protocol = Sas::SasProtocol::HttpsAndHttp;
fileSasBuilder.StartsOn = ToIso8601(std::chrono::system_clock::now() - std::chrono::minutes(5));
fileSasBuilder.ExpiresOn
= ToIso8601(std::chrono::system_clock::now() + std::chrono::minutes(60));
fileSasBuilder.StartsOn = sasStartsOn;
fileSasBuilder.ExpiresOn = sasExpiresOn;
fileSasBuilder.FileSystemName = m_fileSystemName;
fileSasBuilder.Path = directory1Name + "/" + directory2Name + "/" + fileName;
fileSasBuilder.Resource = Sas::DataLakeSasResource::File;
@ -56,9 +59,7 @@ namespace Azure { namespace Storage { namespace Test {
serviceUri,
std::make_shared<Azure::Identity::ClientSecretCredential>(
AadTenantId(), AadClientId(), AadClientSecret()));
auto userDelegationKey = *serviceClient1.GetUserDelegationKey(
ToIso8601(std::chrono::system_clock::now() - std::chrono::minutes(5)),
ToIso8601(std::chrono::system_clock::now() + std::chrono::minutes(60)));
auto userDelegationKey = *serviceClient1.GetUserDelegationKey(sasStartsOn, sasExpiresOn);
auto verify_file_read = [&](const std::string& sas) {
EXPECT_NO_THROW(fileClient0.Create());
@ -332,8 +333,8 @@ namespace Azure { namespace Storage { namespace Test {
// Expires
{
Sas::DataLakeSasBuilder builder2 = fileSasBuilder;
builder2.StartsOn = ToIso8601(std::chrono::system_clock::now() - std::chrono::minutes(5));
builder2.ExpiresOn = ToIso8601(std::chrono::system_clock::now() - std::chrono::minutes(1));
builder2.StartsOn = sasStartsOn;
builder2.ExpiresOn = sasExpiredOn;
auto sasToken = builder2.GenerateSasToken(*keyCredential);
EXPECT_THROW(verify_file_create(sasToken), StorageException);
@ -383,15 +384,15 @@ namespace Azure { namespace Storage { namespace Test {
options.AccessType = Blobs::Models::PublicAccessType::Blob;
Blobs::Models::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.StartsOn = sasStartsOn;
identifier.ExpiresOn = sasExpiresOn;
identifier.Permissions = "r";
options.SignedIdentifiers.emplace_back(identifier);
containerClinet0.SetAccessPolicy(options);
Sas::DataLakeSasBuilder builder2 = fileSasBuilder;
builder2.StartsOn.Reset();
builder2.ExpiresOn.clear();
builder2.ExpiresOn = Azure::Core::DateTime();
builder2.SetPermissions(static_cast<Sas::DataLakeFileSystemSasPermissions>(0));
builder2.Identifier = identifier.Id;

View File

@ -143,10 +143,8 @@ namespace Azure { namespace Storage { namespace Test {
= Azure::Storage::Details::ParseConnectionString(AdlsGen2ConnectionString()).KeyCredential;
Sas::AccountSasBuilder accountSasBuilder;
accountSasBuilder.Protocol = Sas::SasProtocol::HttpsAndHttp;
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.StartsOn = Core::DateTime::Now() - std::chrono::minutes(5);
accountSasBuilder.ExpiresOn = Core::DateTime::Now() + std::chrono::minutes(60);
accountSasBuilder.Services = Sas::AccountSasServices::Blobs;
accountSasBuilder.ResourceTypes = Sas::AccountSasResource::All;
accountSasBuilder.SetPermissions(Sas::AccountSasPermissions::All);

View File

@ -40,7 +40,7 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares {
constexpr static const char* HeaderVersion = "x-ms-version";
constexpr static const char* HeaderRequestId = "x-ms-client-request-id";
constexpr static const char* HeaderContentLength = "content-length";
constexpr static const char* HeaderContentHash = "content-md5";
constexpr static const char* HeaderContentHashMd5 = "content-md5";
constexpr static const char* HeaderCopyActionAbortConstant = "x-ms-copy-action";
constexpr static const char* HeaderCopySource = "x-ms-copy-source";
constexpr static const char* HeaderFilePermissionCopyMode = "x-ms-file-permission-copy-mode";
@ -72,9 +72,10 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares {
constexpr static const char* HeaderRange = "x-ms-range";
constexpr static const char* HeaderRecursive = "x-ms-recursive";
constexpr static const char* HeaderQuota = "x-ms-share-quota";
constexpr static const char* HeaderSourceContentHash = "x-ms-source-content-crc64";
constexpr static const char* HeaderSourceIfMatchHash = "x-ms-source-if-match-crc64";
constexpr static const char* HeaderSourceIfNoneMatchHash = "x-ms-source-if-none-match-crc64";
constexpr static const char* HeaderSourceContentHashCrc64 = "x-ms-source-content-crc64";
constexpr static const char* HeaderSourceIfMatchHashCrc64 = "x-ms-source-if-match-crc64";
constexpr static const char* HeaderSourceIfNoneMatchHashCrc64
= "x-ms-source-if-none-match-crc64";
constexpr static const char* HeaderSourceRange = "x-ms-source-range";
constexpr static const char* HeaderErrorCode = "x-ms-error-code";
constexpr static const char* HeaderETag = "etag";
@ -105,7 +106,7 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares {
= "x-ms-number-of-handles-failed";
constexpr static const char* HeaderXMsContentLength = "x-ms-content-length";
constexpr static const char* HeaderContentRange = "content-range";
constexpr static const char* HeaderTransactionalContentHash = "content-md5";
constexpr static const char* HeaderTransactionalContentHashMd5 = "content-md5";
constexpr static const char* HeaderContentEncoding = "content-encoding";
constexpr static const char* HeaderCacheControl = "cache-control";
constexpr static const char* HeaderContentDisposition = "content-disposition";
@ -120,6 +121,7 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares {
constexpr static const char* HeaderXMsRange = "x-ms-range";
constexpr static const char* HeaderFileRangeWrite = "x-ms-write";
constexpr static const char* HeaderFileRangeWriteTypeDefault = "update";
constexpr static const char* HeaderTransactionalContentHashCrc64 = "x-ms-content-crc64";
} // namespace Details
namespace Models {
struct ShareFileHttpHeaders
@ -5351,7 +5353,7 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares {
if (createOptions.ContentMd5.HasValue())
{
request.AddHeader(
Details::HeaderContentHash,
Details::HeaderContentHashMd5,
Storage::Details::ToBase64String(createOptions.ContentMd5.GetValue()));
}
if (createOptions.FileContentDisposition.HasValue())
@ -5559,7 +5561,7 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares {
if (setHttpHeadersOptions.ContentMd5.HasValue())
{
request.AddHeader(
Details::HeaderContentHash,
Details::HeaderContentHashMd5,
Storage::Details::ToBase64String(setHttpHeadersOptions.ContentMd5.GetValue()));
}
if (setHttpHeadersOptions.FileContentDisposition.HasValue())
@ -5822,7 +5824,7 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares {
if (uploadRangeOptions.ContentMd5.HasValue())
{
request.AddHeader(
Details::HeaderContentHash,
Details::HeaderContentHashMd5,
Storage::Details::ToBase64String(uploadRangeOptions.ContentMd5.GetValue()));
}
request.AddHeader(Details::HeaderVersion, uploadRangeOptions.ApiVersionParameter);
@ -5881,21 +5883,21 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares {
if (uploadRangeFromUrlOptions.SourceContentCrc64.HasValue())
{
request.AddHeader(
Details::HeaderSourceContentHash,
Details::HeaderSourceContentHashCrc64,
Storage::Details::ToBase64String(
uploadRangeFromUrlOptions.SourceContentCrc64.GetValue()));
}
if (uploadRangeFromUrlOptions.SourceIfMatchCrc64.HasValue())
{
request.AddHeader(
Details::HeaderSourceIfMatchHash,
Details::HeaderSourceIfMatchHashCrc64,
Storage::Details::ToBase64String(
uploadRangeFromUrlOptions.SourceIfMatchCrc64.GetValue()));
}
if (uploadRangeFromUrlOptions.SourceIfNoneMatchCrc64.HasValue())
{
request.AddHeader(
Details::HeaderSourceIfNoneMatchHash,
Details::HeaderSourceIfNoneMatchHashCrc64,
Storage::Details::ToBase64String(
uploadRangeFromUrlOptions.SourceIfNoneMatchCrc64.GetValue()));
}
@ -6248,11 +6250,11 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares {
result.ContentRange = response.GetHeaders().at(Details::HeaderContentRange);
}
result.ETag = response.GetHeaders().at(Details::HeaderETag);
if (response.GetHeaders().find(Details::HeaderTransactionalContentHash)
if (response.GetHeaders().find(Details::HeaderTransactionalContentHashMd5)
!= response.GetHeaders().end())
{
result.TransactionalContentHash = Storage::Details::FromBase64String(
response.GetHeaders().at(Details::HeaderTransactionalContentHash),
response.GetHeaders().at(Details::HeaderTransactionalContentHashMd5),
HashAlgorithm::Md5);
}
if (response.GetHeaders().find(Details::HeaderContentEncoding)
@ -6313,11 +6315,11 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares {
result.CopyStatus
= CopyStatusTypeFromString(response.GetHeaders().at(Details::HeaderCopyStatus));
}
if (response.GetHeaders().find(Details::HeaderContentHash)
if (response.GetHeaders().find(Details::HeaderContentHashMd5)
!= response.GetHeaders().end())
{
result.HttpHeaders.ContentHash = Storage::Details::FromBase64String(
response.GetHeaders().at(Details::HeaderContentHash), HashAlgorithm::Md5);
response.GetHeaders().at(Details::HeaderContentHashMd5), HashAlgorithm::Md5);
}
if (response.GetHeaders().find(Details::HeaderIsServerEncrypted)
!= response.GetHeaders().end())
@ -6384,11 +6386,11 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares {
result.ContentRange = response.GetHeaders().at(Details::HeaderContentRange);
}
result.ETag = response.GetHeaders().at(Details::HeaderETag);
if (response.GetHeaders().find(Details::HeaderTransactionalContentHash)
if (response.GetHeaders().find(Details::HeaderTransactionalContentHashMd5)
!= response.GetHeaders().end())
{
result.TransactionalContentHash = Storage::Details::FromBase64String(
response.GetHeaders().at(Details::HeaderTransactionalContentHash),
response.GetHeaders().at(Details::HeaderTransactionalContentHashMd5),
HashAlgorithm::Md5);
}
if (response.GetHeaders().find(Details::HeaderContentEncoding)
@ -6449,11 +6451,11 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares {
result.CopyStatus
= CopyStatusTypeFromString(response.GetHeaders().at(Details::HeaderCopyStatus));
}
if (response.GetHeaders().find(Details::HeaderContentHash)
if (response.GetHeaders().find(Details::HeaderContentHashMd5)
!= response.GetHeaders().end())
{
result.HttpHeaders.ContentHash = Storage::Details::FromBase64String(
response.GetHeaders().at(Details::HeaderContentHash), HashAlgorithm::Md5);
response.GetHeaders().at(Details::HeaderContentHashMd5), HashAlgorithm::Md5);
}
if (response.GetHeaders().find(Details::HeaderIsServerEncrypted)
!= response.GetHeaders().end())
@ -6531,11 +6533,11 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares {
result.HttpHeaders.ContentType = response.GetHeaders().at(Details::HeaderContentType);
}
result.ETag = response.GetHeaders().at(Details::HeaderETag);
if (response.GetHeaders().find(Details::HeaderTransactionalContentHash)
if (response.GetHeaders().find(Details::HeaderTransactionalContentHashMd5)
!= response.GetHeaders().end())
{
result.HttpHeaders.ContentHash = Storage::Details::FromBase64String(
response.GetHeaders().at(Details::HeaderTransactionalContentHash),
response.GetHeaders().at(Details::HeaderTransactionalContentHashMd5),
HashAlgorithm::Md5);
}
if (response.GetHeaders().find(Details::HeaderContentEncoding)
@ -6832,11 +6834,11 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares {
result.LastModified = Core::DateTime::Parse(
response.GetHeaders().at(Details::HeaderLastModified),
Core::DateTime::DateFormat::Rfc1123);
if (response.GetHeaders().find(Details::HeaderTransactionalContentHash)
if (response.GetHeaders().find(Details::HeaderTransactionalContentHashMd5)
!= response.GetHeaders().end())
{
result.TransactionalContentHash = Storage::Details::FromBase64String(
response.GetHeaders().at(Details::HeaderTransactionalContentHash),
response.GetHeaders().at(Details::HeaderTransactionalContentHashMd5),
HashAlgorithm::Md5);
}
if (response.GetHeaders().find(Details::HeaderRequestIsServerEncrypted)
@ -6870,7 +6872,7 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares {
response.GetHeaders().at(Details::HeaderLastModified),
Core::DateTime::DateFormat::Rfc1123);
result.TransactionalContentHash = Storage::Details::FromBase64String(
response.GetHeaders().at(Details::HeaderTransactionalContentHash),
response.GetHeaders().at(Details::HeaderTransactionalContentHashCrc64),
HashAlgorithm::Crc64);
result.IsServerEncrypted
= response.GetHeaders().at(Details::HeaderRequestIsServerEncrypted) == "true";

View File

@ -135,15 +135,16 @@ namespace Azure { namespace Storage { namespace Sas {
/**
* @brief Optionally specify the time at which the shared access signature becomes
* valid.
* valid. This timestamp will be truncated to second.
*/
Azure::Core::Nullable<std::string> StartsOn;
Azure::Core::Nullable<Azure::Core::DateTime> 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.
* be omitted if it has been specified in an associated stored access policy. This timestamp
* will be truncated to second.
*/
std::string ExpiresOn;
Azure::Core::DateTime ExpiresOn;
/**
* @brief Specifies an IP address or a range of IP addresses from which to accept

View File

@ -84,11 +84,17 @@ namespace Azure { namespace Storage { namespace Sas {
std::string protocol = Details::SasProtocolToString(Protocol);
std::string resource = ShareSasResourceToString(Resource);
std::string stringToSign = Permissions + "\n" + (StartsOn.HasValue() ? StartsOn.GetValue() : "")
+ "\n" + ExpiresOn + "\n" + canonicalName + "\n" + Identifier + "\n"
+ (IPRange.HasValue() ? IPRange.GetValue() : "") + "\n" + protocol + "\n"
+ Storage::Details::DefaultSasVersion + "\n" + CacheControl + "\n" + ContentDisposition
+ "\n" + ContentEncoding + "\n" + ContentLanguage + "\n" + ContentType;
std::string startsOnStr = StartsOn.HasValue()
? StartsOn.GetValue().GetRfc3339String(Azure::Core::DateTime::TimeFractionFormat::Truncate)
: "";
std::string expiresOnStr = Identifier.empty()
? ExpiresOn.GetRfc3339String(Azure::Core::DateTime::TimeFractionFormat::Truncate)
: "";
std::string stringToSign = Permissions + "\n" + startsOnStr + "\n" + expiresOnStr + "\n"
+ canonicalName + "\n" + Identifier + "\n" + (IPRange.HasValue() ? IPRange.GetValue() : "")
+ "\n" + protocol + "\n" + Storage::Details::DefaultSasVersion + "\n" + CacheControl + "\n"
+ ContentDisposition + "\n" + ContentEncoding + "\n" + ContentLanguage + "\n" + ContentType;
std::string signature = Base64Encode(Storage::Details::HmacSha256(
std::vector<uint8_t>(stringToSign.begin(), stringToSign.end()),
@ -98,14 +104,13 @@ namespace Azure { namespace Storage { namespace Sas {
builder.AppendQueryParameter(
"sv", Storage::Details::UrlEncodeQueryParameter(Storage::Details::DefaultSasVersion));
builder.AppendQueryParameter("spr", Storage::Details::UrlEncodeQueryParameter(protocol));
if (StartsOn.HasValue())
if (!startsOnStr.empty())
{
builder.AppendQueryParameter(
"st", Storage::Details::UrlEncodeQueryParameter(StartsOn.GetValue()));
builder.AppendQueryParameter("st", Storage::Details::UrlEncodeQueryParameter(startsOnStr));
}
if (!ExpiresOn.empty())
if (!expiresOnStr.empty())
{
builder.AppendQueryParameter("se", Storage::Details::UrlEncodeQueryParameter(ExpiresOn));
builder.AppendQueryParameter("se", Storage::Details::UrlEncodeQueryParameter(expiresOnStr));
}
if (IPRange.HasValue())
{

View File

@ -9,12 +9,15 @@ namespace Azure { namespace Storage { namespace Test {
TEST_F(FileShareClientTest, FileSasTest)
{
auto sasStartsOn = Azure::Core::DateTime::Now() - std::chrono::minutes(5);
auto sasExpiredOn = Azure::Core::DateTime::Now() - std::chrono::minutes(1);
auto sasExpiresOn = Azure::Core::DateTime::Now() + std::chrono::minutes(60);
std::string fileName = RandomString();
Sas::ShareSasBuilder fileSasBuilder;
fileSasBuilder.Protocol = Sas::SasProtocol::HttpsAndHttp;
fileSasBuilder.StartsOn = ToIso8601(std::chrono::system_clock::now() - std::chrono::minutes(5));
fileSasBuilder.ExpiresOn
= ToIso8601(std::chrono::system_clock::now() + std::chrono::minutes(60));
fileSasBuilder.StartsOn = sasStartsOn;
fileSasBuilder.ExpiresOn = sasExpiresOn;
fileSasBuilder.ShareName = m_shareName;
fileSasBuilder.FilePath = fileName;
fileSasBuilder.Resource = Sas::ShareSasResource::File;
@ -137,8 +140,8 @@ namespace Azure { namespace Storage { namespace Test {
// Expires
{
Sas::ShareSasBuilder builder2 = fileSasBuilder;
builder2.StartsOn = ToIso8601(std::chrono::system_clock::now() - std::chrono::minutes(5));
builder2.ExpiresOn = ToIso8601(std::chrono::system_clock::now() - std::chrono::minutes(1));
builder2.StartsOn = sasStartsOn;
builder2.ExpiresOn = sasExpiredOn;
auto sasToken = builder2.GenerateSasToken(*keyCredential);
EXPECT_THROW(verifyFileRead(sasToken), StorageException);
}
@ -168,14 +171,14 @@ namespace Azure { namespace Storage { namespace Test {
{
Files::Shares::Models::SignedIdentifier identifier;
identifier.Id = RandomString(64);
identifier.Policy.StartsOn = Core::DateTime::Now() - std::chrono::minutes(5);
identifier.Policy.ExpiresOn = Core::DateTime::Now() + std::chrono::minutes(60);
identifier.Policy.StartsOn = sasStartsOn;
identifier.Policy.ExpiresOn = sasExpiresOn;
identifier.Policy.Permission = "r";
m_shareClient->SetAccessPolicy({identifier});
Sas::ShareSasBuilder builder2 = fileSasBuilder;
builder2.StartsOn.Reset();
builder2.ExpiresOn.clear();
builder2.ExpiresOn = Azure::Core::DateTime();
builder2.SetPermissions(static_cast<Sas::ShareSasPermissions>(0));
builder2.Identifier = identifier.Id;