From b77518067e174e4b181c72596059e1a0d5e4a664 Mon Sep 17 00:00:00 2001 From: JinmingHu Date: Wed, 5 Jul 2023 13:38:14 +0800 Subject: [PATCH] Refactor blob SAS tests and queue SAS test (#4748) * Refactor blob sas tests and queue sas test * fix build errors * fix failed cases --- sdk/storage/assets.json | 2 +- .../test/ut/blob_sas_test.cpp | 1091 +++++++++-------- .../test/ut/queue_sas_test.cpp | 346 ++++-- 3 files changed, 827 insertions(+), 612 deletions(-) diff --git a/sdk/storage/assets.json b/sdk/storage/assets.json index a258176d8..e5c7fccda 100644 --- a/sdk/storage/assets.json +++ b/sdk/storage/assets.json @@ -2,5 +2,5 @@ "AssetsRepo": "Azure/azure-sdk-assets", "AssetsRepoPrefixPath": "cpp", "TagPrefix": "cpp/storage", - "Tag": "cpp/storage_d053ec409e" + "Tag": "cpp/storage_a52a3c6352" } diff --git a/sdk/storage/azure-storage-blobs/test/ut/blob_sas_test.cpp b/sdk/storage/azure-storage-blobs/test/ut/blob_sas_test.cpp index 5c4ec9248..3bf192299 100644 --- a/sdk/storage/azure-storage-blobs/test/ut/blob_sas_test.cpp +++ b/sdk/storage/azure-storage-blobs/test/ut/blob_sas_test.cpp @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // SPDX-License-Identifier: MIT -#include "blob_container_client_test.hpp" +#include "block_blob_client_test.hpp" #include #include @@ -10,13 +10,94 @@ namespace Azure { namespace Storage { namespace Test { - TEST_F(BlobContainerClientTest, BlobSasTest_LIVEONLY_) - { - auto blobContainerClient = GetBlobContainerClientForTest(LowercaseRandomString()); - blobContainerClient.CreateIfNotExists(); + class BlobSasTest : public BlockBlobClientTest { + public: + template T GetSasAuthenticatedClient(const T& blobClient, const std::string& sasToken) + { + T blobClient1( + AppendQueryParameters(Azure::Core::Url(blobClient.GetUrl()), sasToken), + InitStorageClientOptions()); + return blobClient1; + } + void VerifyBlobSasRead(const Blobs::BlobClient& blobClient, const std::string& sasToken) + { + auto blobClient1 = GetSasAuthenticatedClient(blobClient, sasToken); + EXPECT_NO_THROW(blobClient1.GetProperties()); + } + + void VerifyBlobSasNonRead(const Blobs::BlobClient& blobClient, const std::string& sasToken) + { + auto blobClient1 = GetSasAuthenticatedClient(blobClient, sasToken); + EXPECT_THROW(blobClient1.GetProperties(), StorageException); + } + + void VerifyBlobSasWrite(const Blobs::BlobClient& blobClient, const std::string& sasToken) + { + auto blobClient1 = GetSasAuthenticatedClient(blobClient.AsBlockBlobClient(), sasToken); + EXPECT_NO_THROW(blobClient1.UploadFrom(reinterpret_cast("a"), 1)); + } + + void VerifyBlobSasDelete(const Blobs::BlobClient& blobClient, const std::string& sasToken) + { + auto blobClient1 = GetSasAuthenticatedClient(blobClient.AsBlockBlobClient(), sasToken); + Blobs::DeleteBlobOptions options; + options.DeleteSnapshots = Blobs::Models::DeleteSnapshotsOption::IncludeSnapshots; + EXPECT_NO_THROW(blobClient1.Delete(options)); + blobClient.AsBlockBlobClient().UploadFrom(reinterpret_cast("a"), 1); + } + + void VerifyBlobSasAdd(const Blobs::AppendBlobClient& blobClient, const std::string& sasToken) + { + blobClient.CreateIfNotExists(); + auto blobClient1 = GetSasAuthenticatedClient(blobClient, sasToken); + const std::string content = "Hello world"; + auto blockContent = Azure::Core::IO::MemoryBodyStream( + reinterpret_cast(content.data()), content.size()); + EXPECT_NO_THROW(blobClient1.AppendBlock(blockContent)); + } + + void VerifyBlobSasList( + const Blobs::BlobContainerClient& blobContainerClient, + const std::string& sasToken) + { + auto blobContainerClient1 = GetSasAuthenticatedClient(blobContainerClient, sasToken); + EXPECT_NO_THROW(blobContainerClient1.ListBlobs()); + } + + void VerifyBlobSasCreate(const Blobs::BlobClient& blobClient, const std::string& sasToken) + { + auto blobClient1 = GetSasAuthenticatedClient(blobClient, sasToken); + EXPECT_NO_THROW(blobClient1.CreateSnapshot()); + } + + void VerifyBlobSasTags(const Blobs::BlobClient& blobClient, const std::string& sasToken) + { + std::map tags = {{"tag_key1", "tag_value1"}}; + blobClient.SetTags(tags); + auto blobClient1 = GetSasAuthenticatedClient(blobClient, sasToken); + EXPECT_NO_THROW(blobClient1.GetTags()); + } + + void VerifyBlobSasFilter( + const Blobs::BlobContainerClient& blobContainerClient, + const std::string& sasToken) + { + auto blobContainerClient1 = GetSasAuthenticatedClient(blobContainerClient, sasToken); + EXPECT_NO_THROW(blobContainerClient1.FindBlobsByTags("\"tag_key1\" = 'tag_value1'")); + } + + void VerifyBlobSasImmutability(const Blobs::BlobClient& blobClient, const std::string& sasToken) + { + (void)blobClient; + (void)sasToken; + // Disabled because there's no way to enable immutability on a container with dataplane API + } + }; + + TEST_F(BlobSasTest, AccountSasPermissions) + { auto sasStartsOn = std::chrono::system_clock::now() - std::chrono::minutes(5); - auto sasExpiredOn = std::chrono::system_clock::now() - std::chrono::minutes(1); auto sasExpiresOn = std::chrono::system_clock::now() + std::chrono::minutes(60); Sas::AccountSasBuilder accountSasBuilder; @@ -26,114 +107,11 @@ namespace Azure { namespace Storage { namespace Test { accountSasBuilder.Services = Sas::AccountSasServices::Blobs; accountSasBuilder.ResourceTypes = Sas::AccountSasResource::All; - std::string blobName = RandomString(); - Sas::BlobSasBuilder blobSasBuilder; - blobSasBuilder.Protocol = Sas::SasProtocol::HttpsAndHttp; - blobSasBuilder.StartsOn = sasStartsOn; - blobSasBuilder.ExpiresOn = sasExpiresOn; - blobSasBuilder.BlobContainerName = m_containerName; - blobSasBuilder.BlobName = blobName; - blobSasBuilder.Resource = Sas::BlobSasResource::Blob; - - Sas::BlobSasBuilder containerSasBuilder = blobSasBuilder; - containerSasBuilder.BlobName.clear(); - containerSasBuilder.Resource = Sas::BlobSasResource::BlobContainer; - auto keyCredential = _internal::ParseConnectionString(StandardStorageConnectionString()).KeyCredential; - auto accountName = keyCredential->AccountName; - auto blobServiceClient0 - = Blobs::BlobServiceClient::CreateFromConnectionString(StandardStorageConnectionString()); - auto blobContainerClient0 = blobServiceClient0.GetBlobContainerClient(m_containerName); - auto blobClient0 = blobContainerClient0.GetAppendBlobClient(blobName); - auto serviceUrl = blobServiceClient0.GetUrl(); - auto containerUrl = blobContainerClient0.GetUrl(); - auto blobUrl = blobClient0.GetUrl(); - - auto blobServiceClient1 = Blobs::BlobServiceClient( - serviceUrl, - std::make_shared( - AadTenantId(), AadClientId(), AadClientSecret())); - auto userDelegationKey = blobServiceClient1.GetUserDelegationKey(sasExpiresOn).Value; - - auto verify_blob_read = [&](const std::string& sas) { - EXPECT_NO_THROW(blobClient0.Create()); - auto blobClient = Blobs::AppendBlobClient(blobUrl + sas); - auto downloadedContent = blobClient.Download(); - EXPECT_TRUE(ReadBodyStream(downloadedContent.Value.BodyStream).empty()); - blobClient0.Delete(); - }; - - auto verify_blob_write = [&](const std::string& sas) { - auto blobClient = Blobs::AppendBlobClient(blobUrl + sas); - EXPECT_NO_THROW(blobClient.Create()); - blobClient0.Delete(); - }; - - auto verify_blob_delete = [&](const std::string& sas) { - blobClient0.Create(); - auto blobClient = Blobs::AppendBlobClient(blobUrl + sas); - EXPECT_NO_THROW(blobClient.Delete()); - }; - - auto verify_blob_add = [&](const std::string& sas) { - blobClient0.Create(); - std::string content = "Hello world"; - auto blockContent = Azure::Core::IO::MemoryBodyStream( - reinterpret_cast(content.data()), content.size()); - auto blobClient = Blobs::AppendBlobClient(blobUrl + sas); - EXPECT_NO_THROW(blobClient.AppendBlock(blockContent)); - blobClient0.Delete(); - }; - - auto verify_blob_list = [&](const std::string& sas) { - auto blobContainerClient = Blobs::BlobContainerClient(containerUrl + sas); - EXPECT_NO_THROW(blobContainerClient.ListBlobs()); - }; - - auto verify_blob_create = [&](const std::string& sas) { - try - { - blobClient0.Delete(); - } - catch (const StorageException&) - { - } - auto blobClient = Blobs::AppendBlobClient(blobUrl + sas); - blobClient.Create(); - blobClient.CreateSnapshot(); - Blobs::DeleteBlobOptions options; - options.DeleteSnapshots = Blobs::Models::DeleteSnapshotsOption::IncludeSnapshots; - blobClient0.Delete(options); - }; - - auto verify_blob_tags = [&](const std::string& sas) { - blobClient0.Create(); - std::map tags = {{"tag_key1", "tag_value1"}}; - blobClient0.SetTags(tags); - auto blobClient = Blobs::AppendBlobClient(blobUrl + sas); - EXPECT_NO_THROW(blobClient.GetTags()); - blobClient0.Delete(); - }; - - auto verify_blob_filter = [&](const std::string& sas) { - auto serviceClient = Blobs::BlobServiceClient(serviceUrl + sas); - EXPECT_NO_THROW(serviceClient.FindBlobsByTags("\"tag_key1\" = 'tag_value1'")); - }; - - auto verify_blob_immutability = [&](const std::string& sas) { - (void)sas; - // Disabled because there's no way to enable immutability on a container with dataplane API - // blobClient0.Create(); - // auto blobClient = Blobs::AppendBlobClient(blobUrl + sas); - // Blobs::Models::BlobImmutabilityPolicy policy; - // policy.ExpiresOn = std::chrono::system_clock::now() + std::chrono::minutes(10); - // policy.PolicyMode = Blobs::Models::BlobImmutabilityPolicyMode::Unlocked; - // EXPECT_NO_THROW(blobClient.SetImmutabilityPolicy(policy)); - // blobClient0.DeleteImmutabilityPolicy(); - // blobClient0.Delete(); - }; + auto blobContainerClient = *m_blobContainerClient; + auto blobClient = *m_blockBlobClient; for (auto permissions : { Sas::AccountSasPermissions::All, @@ -154,149 +132,71 @@ namespace Azure { namespace Storage { namespace Test { if ((permissions & Sas::AccountSasPermissions::Read) == Sas::AccountSasPermissions::Read) { - verify_blob_read(sasToken); + VerifyBlobSasRead(blobClient, sasToken); } if ((permissions & Sas::AccountSasPermissions::Write) == Sas::AccountSasPermissions::Write) { - verify_blob_write(sasToken); + VerifyBlobSasWrite(blobClient, sasToken); } if ((permissions & Sas::AccountSasPermissions::Delete) == Sas::AccountSasPermissions::Delete) { - verify_blob_delete(sasToken); + VerifyBlobSasDelete(blobClient, sasToken); } if ((permissions & Sas::AccountSasPermissions::List) == Sas::AccountSasPermissions::List) { - verify_blob_list(sasToken); + VerifyBlobSasList(blobContainerClient, sasToken); } if ((permissions & Sas::AccountSasPermissions::Add) == Sas::AccountSasPermissions::Add) { - verify_blob_add(sasToken); + VerifyBlobSasAdd(blobContainerClient.GetAppendBlobClient(RandomString()), sasToken); } if ((permissions & Sas::AccountSasPermissions::Create) == Sas::AccountSasPermissions::Create) { - verify_blob_create(sasToken); + VerifyBlobSasCreate(blobClient, sasToken); } if ((permissions & Sas::AccountSasPermissions::Tags) == Sas::AccountSasPermissions::Tags) { - verify_blob_tags(sasToken); + VerifyBlobSasTags(blobClient, sasToken); } if ((permissions & Sas::AccountSasPermissions::Filter) == Sas::AccountSasPermissions::Filter) { - verify_blob_filter(sasToken); + VerifyBlobSasFilter(blobContainerClient, sasToken); } if ((permissions & Sas::AccountSasPermissions::SetImmutabilityPolicy) == Sas::AccountSasPermissions::SetImmutabilityPolicy) { - verify_blob_immutability(sasToken); + VerifyBlobSasImmutability(blobClient, sasToken); } } + } - for (auto permissions : { - Sas::BlobSasPermissions::All, - Sas::BlobSasPermissions::Read, - Sas::BlobSasPermissions::Write, - Sas::BlobSasPermissions::Delete, - Sas::BlobSasPermissions::Add, - Sas::BlobSasPermissions::Create, - Sas::BlobSasPermissions::Tags, - Sas::BlobSasPermissions::DeleteVersion, - Sas::BlobSasPermissions::SetImmutabilityPolicy, - }) + TEST_F(BlobSasTest, ServiceContainerSasPermissions_LIVEONLY_) + { + auto sasStartsOn = std::chrono::system_clock::now() - std::chrono::minutes(5); + auto sasExpiresOn = std::chrono::system_clock::now() + std::chrono::minutes(60); + + auto keyCredential + = _internal::ParseConnectionString(StandardStorageConnectionString()).KeyCredential; + auto accountName = keyCredential->AccountName; + + Blobs::Models::UserDelegationKey userDelegationKey; { - blobSasBuilder.SetPermissions(permissions); - auto sasToken = blobSasBuilder.GenerateSasToken(*keyCredential); - auto sasToken2 = blobSasBuilder.GenerateSasToken(userDelegationKey, accountName); - - if ((permissions & Sas::BlobSasPermissions::Read) == Sas::BlobSasPermissions::Read) - { - verify_blob_read(sasToken); - verify_blob_read(sasToken2); - } - if ((permissions & Sas::BlobSasPermissions::Write) == Sas::BlobSasPermissions::Write) - { - verify_blob_write(sasToken); - verify_blob_write(sasToken2); - } - if ((permissions & Sas::BlobSasPermissions::Delete) == Sas::BlobSasPermissions::Delete) - { - verify_blob_delete(sasToken); - verify_blob_delete(sasToken2); - } - if ((permissions & Sas::BlobSasPermissions::Add) == Sas::BlobSasPermissions::Add) - { - verify_blob_add(sasToken); - verify_blob_add(sasToken2); - } - if ((permissions & Sas::BlobSasPermissions::Create) == Sas::BlobSasPermissions::Create) - { - verify_blob_create(sasToken); - verify_blob_create(sasToken2); - } - if ((permissions & Sas::BlobSasPermissions::Tags) == Sas::BlobSasPermissions::Tags) - { - verify_blob_tags(sasToken); - verify_blob_tags(sasToken2); - } - if ((permissions & Sas::BlobSasPermissions::SetImmutabilityPolicy) - == Sas::BlobSasPermissions::SetImmutabilityPolicy) - { - verify_blob_immutability(sasToken); - } + auto blobServiceClient = Blobs::BlobServiceClient( + m_blobServiceClient->GetUrl(), + std::make_shared( + AadTenantId(), AadClientId(), AadClientSecret())); + userDelegationKey = blobServiceClient.GetUserDelegationKey(sasExpiresOn).Value; } - accountSasBuilder.SetPermissions(Sas::AccountSasPermissions::All); - // Expires - { - Sas::AccountSasBuilder builder2 = accountSasBuilder; - builder2.StartsOn = sasStartsOn; - builder2.ExpiresOn = sasExpiredOn; - auto sasToken = builder2.GenerateSasToken(*keyCredential); - EXPECT_THROW(verify_blob_create(sasToken), StorageException); - } + auto blobContainerClient = *m_blobContainerClient; + auto blobClient = *m_blockBlobClient; - // Without start time - { - Sas::AccountSasBuilder builder2 = accountSasBuilder; - builder2.StartsOn.Reset(); - auto sasToken = builder2.GenerateSasToken(*keyCredential); - EXPECT_NO_THROW(verify_blob_create(sasToken)); - } - - // IP - { - Sas::AccountSasBuilder builder2 = accountSasBuilder; - builder2.IPRange = "1.1.1.1"; - auto sasToken = builder2.GenerateSasToken(*keyCredential); - EXPECT_THROW(verify_blob_create(sasToken), StorageException); - - // TODO: Add this test case back with support to contain IPv6 ranges when service is ready. - // builder2.IPRange = "0.0.0.0-255.255.255.255"; - // sasToken = builder2.GenerateSasToken(*keyCredential); - // EXPECT_NO_THROW(verify_blob_create(sasToken)); - } - - // Account SAS Service - { - Sas::AccountSasBuilder builder2 = accountSasBuilder; - builder2.Services = Sas::AccountSasServices::Files; - auto sasToken = builder2.GenerateSasToken(*keyCredential); - EXPECT_THROW(verify_blob_create(sasToken), StorageException); - - builder2.Services = Sas::AccountSasServices::All; - sasToken = builder2.GenerateSasToken(*keyCredential); - EXPECT_NO_THROW(verify_blob_create(sasToken)); - } - - // Account SAS Resource Types - { - Sas::AccountSasBuilder builder2 = accountSasBuilder; - builder2.ResourceTypes = Sas::AccountSasResource::Service; - auto sasToken = builder2.GenerateSasToken(*keyCredential); - EXPECT_THROW(verify_blob_create(sasToken), StorageException); - - auto serviceClient = Blobs::BlobServiceClient(serviceUrl + sasToken); - EXPECT_NO_THROW(serviceClient.ListBlobContainers()); - } + Sas::BlobSasBuilder containerSasBuilder; + containerSasBuilder.Protocol = Sas::SasProtocol::HttpsAndHttp; + containerSasBuilder.StartsOn = sasStartsOn; + containerSasBuilder.ExpiresOn = sasExpiresOn; + containerSasBuilder.BlobContainerName = m_containerName; + containerSasBuilder.Resource = Sas::BlobSasResource::BlobContainer; for (auto permissions : { Sas::BlobContainerSasPermissions::All, @@ -317,301 +217,526 @@ namespace Azure { namespace Storage { namespace Test { if ((permissions & Sas::BlobContainerSasPermissions::Read) == Sas::BlobContainerSasPermissions::Read) { - verify_blob_read(sasToken); - verify_blob_read(sasToken2); + VerifyBlobSasRead(blobClient, sasToken); + VerifyBlobSasRead(blobClient, sasToken2); } if ((permissions & Sas::BlobContainerSasPermissions::Write) == Sas::BlobContainerSasPermissions::Write) { - verify_blob_write(sasToken); - verify_blob_write(sasToken2); + VerifyBlobSasWrite(blobClient, sasToken); + VerifyBlobSasWrite(blobClient, sasToken2); } if ((permissions & Sas::BlobContainerSasPermissions::Delete) == Sas::BlobContainerSasPermissions::Delete) { - verify_blob_delete(sasToken); - verify_blob_delete(sasToken2); + VerifyBlobSasDelete(blobClient, sasToken); + VerifyBlobSasDelete(blobClient, sasToken2); } if ((permissions & Sas::BlobContainerSasPermissions::List) == Sas::BlobContainerSasPermissions::List) { - verify_blob_list(sasToken); - verify_blob_list(sasToken2); - } - if ((permissions & Sas::BlobContainerSasPermissions::Add) - == Sas::BlobContainerSasPermissions::Add) - { - verify_blob_add(sasToken); - verify_blob_add(sasToken2); + VerifyBlobSasList(blobContainerClient, sasToken); + VerifyBlobSasList(blobContainerClient, sasToken2); } if ((permissions & Sas::BlobContainerSasPermissions::Create) == Sas::BlobContainerSasPermissions::Create) { - verify_blob_create(sasToken); - verify_blob_create(sasToken2); + VerifyBlobSasCreate(blobClient, sasToken); + VerifyBlobSasCreate(blobClient, sasToken2); } if ((permissions & Sas::BlobContainerSasPermissions::Tags) == Sas::BlobContainerSasPermissions::Tags) { - verify_blob_tags(sasToken); - verify_blob_tags(sasToken2); + VerifyBlobSasTags(blobClient, sasToken); + VerifyBlobSasTags(blobClient, sasToken2); } if ((permissions & Sas::BlobContainerSasPermissions::SetImmutabilityPolicy) == Sas::BlobContainerSasPermissions::SetImmutabilityPolicy) { - verify_blob_immutability(sasToken); + VerifyBlobSasImmutability(blobClient, sasToken); + VerifyBlobSasImmutability(blobClient, sasToken2); } } - blobSasBuilder.SetPermissions(Sas::BlobSasPermissions::All); - // Expires - { - Sas::BlobSasBuilder builder2 = blobSasBuilder; - builder2.StartsOn = sasStartsOn; - builder2.ExpiresOn = sasExpiredOn; - auto sasToken = builder2.GenerateSasToken(*keyCredential); - EXPECT_THROW(verify_blob_create(sasToken), StorageException); - - auto sasToken2 = builder2.GenerateSasToken(userDelegationKey, accountName); - EXPECT_THROW(verify_blob_create(sasToken2), StorageException); - } - - // Without start time - { - Sas::BlobSasBuilder builder2 = blobSasBuilder; - builder2.StartsOn.Reset(); - auto sasToken = builder2.GenerateSasToken(*keyCredential); - EXPECT_NO_THROW(verify_blob_create(sasToken)); - auto sasToken2 = builder2.GenerateSasToken(userDelegationKey, accountName); - EXPECT_NO_THROW(verify_blob_create(sasToken2)); - } - - // IP - { - Sas::BlobSasBuilder builder2 = blobSasBuilder; - builder2.IPRange = "0.0.0.0-0.0.0.1"; - auto sasToken = builder2.GenerateSasToken(*keyCredential); - EXPECT_THROW(verify_blob_create(sasToken), StorageException); - auto sasToken2 = builder2.GenerateSasToken(userDelegationKey, accountName); - EXPECT_THROW(verify_blob_create(sasToken2), StorageException); - - // TODO: Add this test case back with support to contain IPv6 ranges when service is ready. - // builder2.IPRange = "0.0.0.0-255.255.255.255"; - // sasToken = builder2.GenerateSasToken(*keyCredential); - // EXPECT_NO_THROW(verify_blob_create(sasToken)); - // sasToken2 = builder2.GenerateSasToken(userDelegationKey, accountName); - // EXPECT_NO_THROW(verify_blob_create(sasToken2)); - } - - // Identifier - { - Blobs::SetBlobContainerAccessPolicyOptions options; - options.AccessType = Blobs::Models::PublicAccessType::Blob; - Blobs::Models::SignedIdentifier identifier; - identifier.Id = RandomString(64); - identifier.StartsOn = sasStartsOn; - identifier.ExpiresOn = sasExpiresOn; - identifier.Permissions = "r"; - options.SignedIdentifiers.emplace_back(identifier); - blobContainerClient.SetAccessPolicy(options); - - Sas::BlobSasBuilder builder2 = blobSasBuilder; - builder2.StartsOn.Reset(); - builder2.ExpiresOn = Azure::DateTime(); - builder2.SetPermissions(static_cast(0)); - builder2.Identifier = identifier.Id; - - auto sasToken = builder2.GenerateSasToken(*keyCredential); - // TODO: looks like a server bug, the identifier doesn't work sometimes. - // EXPECT_NO_THROW(verify_blob_read(sasToken)); - } - - // response headers override - { - Blobs::Models::BlobHttpHeaders headers; - headers.ContentType = "application/x-binary"; - headers.ContentLanguage = "en-US"; - headers.ContentDisposition = "attachment"; - headers.CacheControl = "no-cache"; - headers.ContentEncoding = "identify"; - - Sas::BlobSasBuilder builder2 = blobSasBuilder; - builder2.ContentType = "application/x-binary"; - builder2.ContentLanguage = "en-US"; - builder2.ContentDisposition = "attachment"; - builder2.CacheControl = "no-cache"; - builder2.ContentEncoding = "identify"; - auto sasToken = builder2.GenerateSasToken(*keyCredential); - auto blobClient = Blobs::AppendBlobClient(blobUrl + sasToken); - blobClient0.Create(); - auto p = blobClient.GetProperties(); - EXPECT_EQ(p.Value.HttpHeaders.ContentType, headers.ContentType); - EXPECT_EQ(p.Value.HttpHeaders.ContentLanguage, headers.ContentLanguage); - EXPECT_EQ(p.Value.HttpHeaders.ContentDisposition, headers.ContentDisposition); - EXPECT_EQ(p.Value.HttpHeaders.CacheControl, headers.CacheControl); - EXPECT_EQ(p.Value.HttpHeaders.ContentEncoding, headers.ContentEncoding); - - auto sasToken2 = builder2.GenerateSasToken(userDelegationKey, accountName); - blobClient = Blobs::AppendBlobClient(blobUrl + sasToken2); - p = blobClient.GetProperties(); - EXPECT_EQ(p.Value.HttpHeaders.ContentType, headers.ContentType); - EXPECT_EQ(p.Value.HttpHeaders.ContentLanguage, headers.ContentLanguage); - EXPECT_EQ(p.Value.HttpHeaders.ContentDisposition, headers.ContentDisposition); - EXPECT_EQ(p.Value.HttpHeaders.CacheControl, headers.CacheControl); - EXPECT_EQ(p.Value.HttpHeaders.ContentEncoding, headers.ContentEncoding); - blobClient0.Delete(); - } - - blobClient0.Create(); - Sas::BlobSasBuilder BlobSnapshotSasBuilder = blobSasBuilder; - BlobSnapshotSasBuilder.Resource = Sas::BlobSasResource::BlobSnapshot; - - std::string blobSnapshotUrl; - - auto create_snapshot = [&]() { - std::string snapshot = blobClient0.CreateSnapshot().Value.Snapshot; - BlobSnapshotSasBuilder.Snapshot = snapshot; - blobSnapshotUrl = blobClient0.WithSnapshot(snapshot).GetUrl(); - }; - - auto verify_blob_snapshot_read = [&](const std::string sas) { - Azure::Core::Url blobSnapshotUrlWithSas(blobSnapshotUrl); - auto blobSnapshotClient - = Blobs::AppendBlobClient(AppendQueryParameters(blobSnapshotUrlWithSas, sas)); - auto downloadedContent = blobSnapshotClient.Download(); - EXPECT_TRUE(ReadBodyStream(downloadedContent.Value.BodyStream).empty()); - }; - - auto verify_blob_snapshot_delete = [&](const std::string sas) { - Azure::Core::Url blobSnapshotUrlWithSas(blobSnapshotUrl); - auto blobSnapshotClient - = Blobs::AppendBlobClient(AppendQueryParameters(blobSnapshotUrlWithSas, sas)); - EXPECT_NO_THROW(blobSnapshotClient.Delete()); - }; + const auto appendBlobName = RandomString(); + auto appendBlobClient = blobContainerClient.GetAppendBlobClient(appendBlobName); + containerSasBuilder.BlobName = appendBlobName; for (auto permissions : { - Sas::BlobSasPermissions::Read | Sas::BlobSasPermissions::Delete, - Sas::BlobSasPermissions::Read, - Sas::BlobSasPermissions::Delete, + Sas::BlobContainerSasPermissions::All, + Sas::BlobContainerSasPermissions::Add, }) { - create_snapshot(); - BlobSnapshotSasBuilder.SetPermissions(permissions); - auto sasToken = BlobSnapshotSasBuilder.GenerateSasToken(*keyCredential); - auto sasToken2 = BlobSnapshotSasBuilder.GenerateSasToken(userDelegationKey, accountName); - - if ((permissions & Sas::BlobSasPermissions::Read) == Sas::BlobSasPermissions::Read) + containerSasBuilder.SetPermissions(permissions); + auto sasToken = containerSasBuilder.GenerateSasToken(*keyCredential); + auto sasToken2 = containerSasBuilder.GenerateSasToken(userDelegationKey, accountName); + if ((permissions & Sas::BlobContainerSasPermissions::Add) + == Sas::BlobContainerSasPermissions::Add) { - verify_blob_snapshot_read(sasToken); - verify_blob_snapshot_read(sasToken2); + VerifyBlobSasAdd(appendBlobClient, sasToken); + VerifyBlobSasAdd(appendBlobClient, sasToken2); } - if ((permissions & Sas::BlobSasPermissions::Delete) == Sas::BlobSasPermissions::Delete) - { - create_snapshot(); - sasToken = BlobSnapshotSasBuilder.GenerateSasToken(*keyCredential); - verify_blob_snapshot_delete(sasToken); - create_snapshot(); - sasToken2 = BlobSnapshotSasBuilder.GenerateSasToken(userDelegationKey, accountName); - verify_blob_snapshot_delete(sasToken2); - } - } - - { - Blobs::DeleteBlobOptions options; - options.DeleteSnapshots = Blobs::Models::DeleteSnapshotsOption::IncludeSnapshots; - blobClient0.Delete(options); - } - - blobClient0.Create(); - Sas::BlobSasBuilder BlobVersionSasBuilder = blobSasBuilder; - BlobVersionSasBuilder.Resource = Sas::BlobSasResource::BlobVersion; - - std::string blobVersionUrl; - - auto create_version = [&]() { - std::string versionId = blobClient0.CreateSnapshot().Value.VersionId.Value(); - BlobVersionSasBuilder.BlobVersionId = versionId; - blobVersionUrl = blobClient0.WithVersionId(versionId).GetUrl(); - blobClient0.SetMetadata({}); - }; - - auto verify_blob_version_read = [&](const std::string sas) { - Azure::Core::Url blobVersionUrlWithSas(blobVersionUrl); - auto blobVersionClient - = Blobs::AppendBlobClient(AppendQueryParameters(blobVersionUrlWithSas, sas)); - auto downloadedContent = blobVersionClient.Download(); - EXPECT_TRUE(ReadBodyStream(downloadedContent.Value.BodyStream).empty()); - }; - - auto verify_blob_delete_version = [&](const std::string& sas) { - Azure::Core::Url blobVersionUrlWithSas(blobVersionUrl); - auto blobVersionClient - = Blobs::AppendBlobClient(AppendQueryParameters(blobVersionUrlWithSas, sas)); - blobVersionClient.Delete(); - }; - - for (auto permissions : { - Sas::BlobSasPermissions::Read | Sas::BlobSasPermissions::DeleteVersion, - Sas::BlobSasPermissions::Read, - Sas::BlobSasPermissions::DeleteVersion, - }) - { - create_version(); - BlobVersionSasBuilder.SetPermissions(permissions); - auto sasToken = BlobVersionSasBuilder.GenerateSasToken(*keyCredential); - auto sasToken2 = BlobVersionSasBuilder.GenerateSasToken(userDelegationKey, accountName); - - if ((permissions & Sas::BlobSasPermissions::Read) == Sas::BlobSasPermissions::Read) - { - verify_blob_version_read(sasToken); - verify_blob_version_read(sasToken2); - } - if ((permissions & Sas::BlobSasPermissions::DeleteVersion) - == Sas::BlobSasPermissions::DeleteVersion) - { - create_version(); - sasToken = BlobVersionSasBuilder.GenerateSasToken(*keyCredential); - verify_blob_delete_version(sasToken); - create_version(); - sasToken2 = BlobVersionSasBuilder.GenerateSasToken(userDelegationKey, accountName); - verify_blob_delete_version(sasToken2); - } - } - { - Blobs::DeleteBlobOptions options; - options.DeleteSnapshots = Blobs::Models::DeleteSnapshotsOption::IncludeSnapshots; - blobClient0.Delete(options); - } - - // Encryption scope - const auto encryptionScope = GetTestEncryptionScope(); - { - auto sasBuilderWithEncryptionScope = blobSasBuilder; - sasBuilderWithEncryptionScope.EncryptionScope = encryptionScope; - auto blobClientEncryptionScopeSas = Blobs::AppendBlobClient( - blobUrl + sasBuilderWithEncryptionScope.GenerateSasToken(*keyCredential)); - blobClientEncryptionScopeSas.Create(); - auto blobProperties = blobClientEncryptionScopeSas.GetProperties().Value; - ASSERT_TRUE(blobProperties.EncryptionScope.HasValue()); - EXPECT_EQ(blobProperties.EncryptionScope.Value(), encryptionScope); - - blobClientEncryptionScopeSas = Blobs::AppendBlobClient( - blobUrl + sasBuilderWithEncryptionScope.GenerateSasToken(userDelegationKey, accountName)); - blobClientEncryptionScopeSas.Create(); - blobProperties = blobClientEncryptionScopeSas.GetProperties().Value; - ASSERT_TRUE(blobProperties.EncryptionScope.HasValue()); - EXPECT_EQ(blobProperties.EncryptionScope.Value(), encryptionScope); - } - { - auto sasBuilderWithEncryptionScope = accountSasBuilder; - sasBuilderWithEncryptionScope.EncryptionScope = encryptionScope; - auto blobClientEncryptionScopeSas = Blobs::AppendBlobClient( - blobUrl + sasBuilderWithEncryptionScope.GenerateSasToken(*keyCredential)); - blobClientEncryptionScopeSas.Create(); - auto blobProperties = blobClientEncryptionScopeSas.GetProperties().Value; - ASSERT_TRUE(blobProperties.EncryptionScope.HasValue()); - EXPECT_EQ(blobProperties.EncryptionScope.Value(), encryptionScope); } } + TEST_F(BlobSasTest, ServiceBlobSasPermissions_LIVEONLY_) + { + auto sasStartsOn = std::chrono::system_clock::now() - std::chrono::minutes(5); + auto sasExpiresOn = std::chrono::system_clock::now() + std::chrono::minutes(60); + + auto keyCredential + = _internal::ParseConnectionString(StandardStorageConnectionString()).KeyCredential; + auto accountName = keyCredential->AccountName; + + Blobs::Models::UserDelegationKey userDelegationKey; + { + auto blobServiceClient = Blobs::BlobServiceClient( + m_blobServiceClient->GetUrl(), + std::make_shared( + AadTenantId(), AadClientId(), AadClientSecret())); + userDelegationKey = blobServiceClient.GetUserDelegationKey(sasExpiresOn).Value; + } + + auto blobContainerClient = *m_blobContainerClient; + auto blobClient = *m_blockBlobClient; + const std::string blobName = m_blobName; + + Sas::BlobSasBuilder blobSasBuilder; + blobSasBuilder.Protocol = Sas::SasProtocol::HttpsAndHttp; + blobSasBuilder.StartsOn = sasStartsOn; + blobSasBuilder.ExpiresOn = sasExpiresOn; + blobSasBuilder.BlobContainerName = m_containerName; + blobSasBuilder.BlobName = blobName; + blobSasBuilder.Resource = Sas::BlobSasResource::Blob; + + for (auto permissions : { + Sas::BlobSasPermissions::All, + Sas::BlobSasPermissions::Read, + Sas::BlobSasPermissions::Write, + Sas::BlobSasPermissions::Delete, + Sas::BlobSasPermissions::Add, + Sas::BlobSasPermissions::Create, + Sas::BlobSasPermissions::Tags, + Sas::BlobSasPermissions::DeleteVersion, + Sas::BlobSasPermissions::SetImmutabilityPolicy, + }) + { + blobSasBuilder.SetPermissions(permissions); + auto sasToken = blobSasBuilder.GenerateSasToken(*keyCredential); + auto sasToken2 = blobSasBuilder.GenerateSasToken(userDelegationKey, accountName); + + if ((permissions & Sas::BlobSasPermissions::Read) == Sas::BlobSasPermissions::Read) + { + VerifyBlobSasRead(blobClient, sasToken); + VerifyBlobSasRead(blobClient, sasToken2); + } + if ((permissions & Sas::BlobSasPermissions::Write) == Sas::BlobSasPermissions::Write) + { + VerifyBlobSasWrite(blobClient, sasToken); + VerifyBlobSasWrite(blobClient, sasToken2); + } + if ((permissions & Sas::BlobSasPermissions::Delete) == Sas::BlobSasPermissions::Delete) + { + VerifyBlobSasDelete(blobClient, sasToken); + VerifyBlobSasDelete(blobClient, sasToken2); + } + if ((permissions & Sas::BlobSasPermissions::Create) == Sas::BlobSasPermissions::Create) + { + VerifyBlobSasCreate(blobClient, sasToken); + VerifyBlobSasCreate(blobClient, sasToken2); + } + if ((permissions & Sas::BlobSasPermissions::Tags) == Sas::BlobSasPermissions::Tags) + { + VerifyBlobSasTags(blobClient, sasToken); + VerifyBlobSasTags(blobClient, sasToken2); + } + if ((permissions & Sas::BlobSasPermissions::SetImmutabilityPolicy) + == Sas::BlobSasPermissions::SetImmutabilityPolicy) + { + VerifyBlobSasImmutability(blobClient, sasToken); + VerifyBlobSasImmutability(blobClient, sasToken2); + } + } + + const auto appendBlobName = RandomString(); + auto appendBlobClient = blobContainerClient.GetAppendBlobClient(appendBlobName); + blobSasBuilder.BlobName = appendBlobName; + + for (auto permissions : { + Sas::BlobSasPermissions::All, + Sas::BlobSasPermissions::Add, + }) + { + blobSasBuilder.SetPermissions(permissions); + auto sasToken = blobSasBuilder.GenerateSasToken(*keyCredential); + auto sasToken2 = blobSasBuilder.GenerateSasToken(userDelegationKey, accountName); + if ((permissions & Sas::BlobSasPermissions::Add) == Sas::BlobSasPermissions::Add) + { + VerifyBlobSasAdd(appendBlobClient, sasToken); + VerifyBlobSasAdd(appendBlobClient, sasToken2); + } + } + } + + TEST_F(BlobSasTest, AccountSasExpired) + { + auto sasStartsOn = std::chrono::system_clock::now() - std::chrono::minutes(5); + auto sasExpiredOn = std::chrono::system_clock::now() - std::chrono::minutes(1); + auto sasExpiresOn = std::chrono::system_clock::now() + std::chrono::minutes(60); + + auto keyCredential + = _internal::ParseConnectionString(StandardStorageConnectionString()).KeyCredential; + + auto blobClient = *m_blockBlobClient; + + Sas::AccountSasBuilder accountSasBuilder; + accountSasBuilder.Protocol = Sas::SasProtocol::HttpsAndHttp; + accountSasBuilder.StartsOn = sasStartsOn; + accountSasBuilder.ExpiresOn = sasExpiredOn; + accountSasBuilder.Services = Sas::AccountSasServices::Blobs; + accountSasBuilder.ResourceTypes = Sas::AccountSasResource::All; + accountSasBuilder.SetPermissions(Sas::AccountSasPermissions::All); + + auto sasToken = accountSasBuilder.GenerateSasToken(*keyCredential); + VerifyBlobSasNonRead(blobClient, sasToken); + + accountSasBuilder.ExpiresOn = sasExpiresOn; + sasToken = accountSasBuilder.GenerateSasToken(*keyCredential); + VerifyBlobSasRead(blobClient, sasToken); + } + + TEST_F(BlobSasTest, ServiceSasExpired) + { + auto sasStartsOn = std::chrono::system_clock::now() - std::chrono::minutes(5); + auto sasExpiredOn = std::chrono::system_clock::now() - std::chrono::minutes(1); + auto sasExpiresOn = std::chrono::system_clock::now() + std::chrono::minutes(60); + + auto keyCredential + = _internal::ParseConnectionString(StandardStorageConnectionString()).KeyCredential; + + auto blobClient = *m_blockBlobClient; + const std::string blobName = m_blobName; + + Sas::BlobSasBuilder blobSasBuilder; + blobSasBuilder.Protocol = Sas::SasProtocol::HttpsAndHttp; + blobSasBuilder.StartsOn = sasStartsOn; + blobSasBuilder.ExpiresOn = sasExpiredOn; + blobSasBuilder.BlobContainerName = m_containerName; + blobSasBuilder.BlobName = blobName; + blobSasBuilder.Resource = Sas::BlobSasResource::Blob; + blobSasBuilder.SetPermissions(Sas::BlobSasPermissions::All); + + auto sasToken = blobSasBuilder.GenerateSasToken(*keyCredential); + VerifyBlobSasNonRead(blobClient, sasToken); + + blobSasBuilder.ExpiresOn = sasExpiresOn; + sasToken = blobSasBuilder.GenerateSasToken(*keyCredential); + VerifyBlobSasRead(blobClient, sasToken); + } + + TEST_F(BlobSasTest, AccountSasWithoutStarttime) + { + auto sasExpiresOn = std::chrono::system_clock::now() + std::chrono::minutes(60); + + auto keyCredential + = _internal::ParseConnectionString(StandardStorageConnectionString()).KeyCredential; + + auto blobClient = *m_blockBlobClient; + + Sas::AccountSasBuilder accountSasBuilder; + accountSasBuilder.Protocol = Sas::SasProtocol::HttpsAndHttp; + accountSasBuilder.ExpiresOn = sasExpiresOn; + accountSasBuilder.Services = Sas::AccountSasServices::Blobs; + accountSasBuilder.ResourceTypes = Sas::AccountSasResource::All; + accountSasBuilder.SetPermissions(Sas::AccountSasPermissions::All); + + auto sasToken = accountSasBuilder.GenerateSasToken(*keyCredential); + VerifyBlobSasRead(blobClient, sasToken); + } + + TEST_F(BlobSasTest, ServiceSasWithoutStartTime) + { + auto sasExpiresOn = std::chrono::system_clock::now() + std::chrono::minutes(60); + + auto keyCredential + = _internal::ParseConnectionString(StandardStorageConnectionString()).KeyCredential; + + auto blobClient = *m_blockBlobClient; + const std::string blobName = m_blobName; + + Sas::BlobSasBuilder blobSasBuilder; + blobSasBuilder.Protocol = Sas::SasProtocol::HttpsAndHttp; + blobSasBuilder.ExpiresOn = sasExpiresOn; + blobSasBuilder.BlobContainerName = m_containerName; + blobSasBuilder.BlobName = blobName; + blobSasBuilder.Resource = Sas::BlobSasResource::Blob; + blobSasBuilder.SetPermissions(Sas::BlobSasPermissions::All); + + auto sasToken = blobSasBuilder.GenerateSasToken(*keyCredential); + VerifyBlobSasRead(blobClient, sasToken); + } + + TEST_F(BlobSasTest, AccountSasWithIP) + { + auto sasExpiresOn = std::chrono::system_clock::now() + std::chrono::minutes(60); + + auto keyCredential + = _internal::ParseConnectionString(StandardStorageConnectionString()).KeyCredential; + + auto blobClient = *m_blockBlobClient; + + Sas::AccountSasBuilder accountSasBuilder; + accountSasBuilder.Protocol = Sas::SasProtocol::HttpsAndHttp; + accountSasBuilder.ExpiresOn = sasExpiresOn; + accountSasBuilder.Services = Sas::AccountSasServices::Blobs; + accountSasBuilder.ResourceTypes = Sas::AccountSasResource::All; + accountSasBuilder.SetPermissions(Sas::AccountSasPermissions::All); + + auto sasToken = accountSasBuilder.GenerateSasToken(*keyCredential); + VerifyBlobSasRead(blobClient, sasToken); + + accountSasBuilder.IPRange = "0.0.0.0-0.0.0.1"; + sasToken = accountSasBuilder.GenerateSasToken(*keyCredential); + VerifyBlobSasNonRead(blobClient, sasToken); + } + + TEST_F(BlobSasTest, ServiceSasWithIP) + { + auto sasStartsOn = std::chrono::system_clock::now() - std::chrono::minutes(5); + auto sasExpiresOn = std::chrono::system_clock::now() + std::chrono::minutes(60); + + auto keyCredential + = _internal::ParseConnectionString(StandardStorageConnectionString()).KeyCredential; + + auto blobClient = *m_blockBlobClient; + const std::string blobName = m_blobName; + + Sas::BlobSasBuilder blobSasBuilder; + blobSasBuilder.Protocol = Sas::SasProtocol::HttpsAndHttp; + blobSasBuilder.StartsOn = sasStartsOn; + blobSasBuilder.ExpiresOn = sasExpiresOn; + blobSasBuilder.BlobContainerName = m_containerName; + blobSasBuilder.BlobName = blobName; + blobSasBuilder.Resource = Sas::BlobSasResource::Blob; + blobSasBuilder.SetPermissions(Sas::BlobSasPermissions::All); + + auto sasToken = blobSasBuilder.GenerateSasToken(*keyCredential); + VerifyBlobSasRead(blobClient, sasToken); + + blobSasBuilder.IPRange = "0.0.0.0-0.0.0.1"; + sasToken = blobSasBuilder.GenerateSasToken(*keyCredential); + VerifyBlobSasNonRead(blobClient, sasToken); + } + + TEST_F(BlobSasTest, AccountSasService) + { + auto sasExpiresOn = std::chrono::system_clock::now() + std::chrono::minutes(60); + + auto keyCredential + = _internal::ParseConnectionString(StandardStorageConnectionString()).KeyCredential; + + auto blobClient = *m_blockBlobClient; + + Sas::AccountSasBuilder accountSasBuilder; + accountSasBuilder.Protocol = Sas::SasProtocol::HttpsAndHttp; + accountSasBuilder.ExpiresOn = sasExpiresOn; + accountSasBuilder.Services = Sas::AccountSasServices::Blobs; + accountSasBuilder.ResourceTypes = Sas::AccountSasResource::All; + accountSasBuilder.SetPermissions(Sas::AccountSasPermissions::All); + accountSasBuilder.Services = Sas::AccountSasServices::Files; + + auto sasToken = accountSasBuilder.GenerateSasToken(*keyCredential); + VerifyBlobSasNonRead(blobClient, sasToken); + + accountSasBuilder.Services = Sas::AccountSasServices::All; + sasToken = accountSasBuilder.GenerateSasToken(*keyCredential); + VerifyBlobSasRead(blobClient, sasToken); + } + + TEST_F(BlobSasTest, AccountSasResourceTypes) + { + auto sasExpiresOn = std::chrono::system_clock::now() + std::chrono::minutes(60); + + auto keyCredential + = _internal::ParseConnectionString(StandardStorageConnectionString()).KeyCredential; + + auto blobClient = *m_blockBlobClient; + + Sas::AccountSasBuilder accountSasBuilder; + accountSasBuilder.Protocol = Sas::SasProtocol::HttpsAndHttp; + accountSasBuilder.ExpiresOn = sasExpiresOn; + accountSasBuilder.Services = Sas::AccountSasServices::Blobs; + accountSasBuilder.SetPermissions(Sas::AccountSasPermissions::All); + accountSasBuilder.ResourceTypes = Sas::AccountSasResource::Service; + + auto sasToken = accountSasBuilder.GenerateSasToken(*keyCredential); + VerifyBlobSasNonRead(blobClient, sasToken); + + auto blobServiceClient1 = GetSasAuthenticatedClient(*m_blobServiceClient, sasToken); + EXPECT_NO_THROW(blobServiceClient1.ListBlobContainers()); + } + + TEST_F(BlobSasTest, BlobSasWithIdentifier) + { + auto sasStartsOn = std::chrono::system_clock::now() - std::chrono::minutes(5); + auto sasExpiresOn = std::chrono::system_clock::now() + std::chrono::minutes(60); + + auto keyCredential + = _internal::ParseConnectionString(StandardStorageConnectionString()).KeyCredential; + + auto blobContainerClient = *m_blobContainerClient; + auto blobClient = *m_blockBlobClient; + const std::string blobName = m_blobName; + + Blobs::SetBlobContainerAccessPolicyOptions options; + options.AccessType = Blobs::Models::PublicAccessType::None; + Blobs::Models::SignedIdentifier identifier; + identifier.Id = RandomString(64); + identifier.StartsOn = sasStartsOn; + identifier.ExpiresOn = sasExpiresOn; + identifier.Permissions = "r"; + options.SignedIdentifiers.emplace_back(identifier); + blobContainerClient.SetAccessPolicy(options); + + Sas::BlobSasBuilder blobSasBuilder; + blobSasBuilder.Protocol = Sas::SasProtocol::HttpsAndHttp; + blobSasBuilder.ExpiresOn = sasExpiresOn; + blobSasBuilder.BlobContainerName = m_containerName; + blobSasBuilder.BlobName = blobName; + blobSasBuilder.Resource = Sas::BlobSasResource::Blob; + blobSasBuilder.SetPermissions(static_cast(0)); + blobSasBuilder.Identifier = identifier.Id; + + TestSleep(std::chrono::seconds(30)); + + auto sasToken = blobSasBuilder.GenerateSasToken(*keyCredential); + VerifyBlobSasRead(blobClient, sasToken); + } + + TEST_F(BlobSasTest, BlobSasResponseHeadersOverride) + { + auto sasStartsOn = std::chrono::system_clock::now() - std::chrono::minutes(5); + auto sasExpiresOn = std::chrono::system_clock::now() + std::chrono::minutes(60); + + auto keyCredential + = _internal::ParseConnectionString(StandardStorageConnectionString()).KeyCredential; + + auto blobClient = *m_blockBlobClient; + const std::string blobName = m_blobName; + + Sas::BlobSasBuilder blobSasBuilder; + blobSasBuilder.Protocol = Sas::SasProtocol::HttpsAndHttp; + blobSasBuilder.StartsOn = sasStartsOn; + blobSasBuilder.ExpiresOn = sasExpiresOn; + blobSasBuilder.BlobContainerName = m_containerName; + blobSasBuilder.BlobName = blobName; + blobSasBuilder.Resource = Sas::BlobSasResource::Blob; + blobSasBuilder.SetPermissions(Sas::BlobSasPermissions::All); + blobSasBuilder.ContentType = "application/x-binary"; + blobSasBuilder.ContentLanguage = "en-US"; + blobSasBuilder.ContentDisposition = "attachment"; + blobSasBuilder.CacheControl = "no-cache"; + blobSasBuilder.ContentEncoding = "identify"; + + auto sasToken = blobSasBuilder.GenerateSasToken(*keyCredential); + auto blobClient1 = GetSasAuthenticatedClient(blobClient, sasToken); + auto properties = blobClient1.GetProperties().Value; + EXPECT_EQ(properties.HttpHeaders.ContentType, blobSasBuilder.ContentType); + EXPECT_EQ(properties.HttpHeaders.ContentLanguage, blobSasBuilder.ContentLanguage); + EXPECT_EQ(properties.HttpHeaders.ContentDisposition, blobSasBuilder.ContentDisposition); + EXPECT_EQ(properties.HttpHeaders.CacheControl, blobSasBuilder.CacheControl); + EXPECT_EQ(properties.HttpHeaders.ContentEncoding, blobSasBuilder.ContentEncoding); + } + + TEST_F(BlobSasTest, AccountSasEncryptionScope) + { + const auto encryptionScope = GetTestEncryptionScope(); + + auto sasExpiresOn = std::chrono::system_clock::now() + std::chrono::minutes(60); + + auto keyCredential + = _internal::ParseConnectionString(StandardStorageConnectionString()).KeyCredential; + + auto blobContainerClient = *m_blobContainerClient; + + Sas::AccountSasBuilder accountSasBuilder; + accountSasBuilder.Protocol = Sas::SasProtocol::HttpsAndHttp; + accountSasBuilder.ExpiresOn = sasExpiresOn; + accountSasBuilder.Services = Sas::AccountSasServices::Blobs; + accountSasBuilder.ResourceTypes = Sas::AccountSasResource::All; + accountSasBuilder.SetPermissions(Sas::AccountSasPermissions::All); + accountSasBuilder.EncryptionScope = encryptionScope; + + auto sasToken = accountSasBuilder.GenerateSasToken(*keyCredential); + auto blobContainerClient1 = GetSasAuthenticatedClient(blobContainerClient, sasToken); + auto appendBlobClient1 = blobContainerClient1.GetAppendBlobClient(RandomString()); + + appendBlobClient1.Create(); + auto properties = appendBlobClient1.GetProperties().Value; + ASSERT_TRUE(properties.EncryptionScope.HasValue()); + EXPECT_EQ(properties.EncryptionScope.Value(), encryptionScope); + } + + TEST_F(BlobSasTest, ServiceSasEncryptionScope) + { + const auto encryptionScope = GetTestEncryptionScope(); + + auto sasStartsOn = std::chrono::system_clock::now() - std::chrono::minutes(5); + auto sasExpiresOn = std::chrono::system_clock::now() + std::chrono::minutes(60); + + auto keyCredential + = _internal::ParseConnectionString(StandardStorageConnectionString()).KeyCredential; + + auto blobContainerClient = *m_blobContainerClient; + + Sas::BlobSasBuilder containerSasBuilder; + containerSasBuilder.Protocol = Sas::SasProtocol::HttpsAndHttp; + containerSasBuilder.StartsOn = sasStartsOn; + containerSasBuilder.ExpiresOn = sasExpiresOn; + containerSasBuilder.BlobContainerName = m_containerName; + containerSasBuilder.Resource = Sas::BlobSasResource::BlobContainer; + containerSasBuilder.SetPermissions(Sas::BlobSasPermissions::All); + containerSasBuilder.EncryptionScope = encryptionScope; + + auto sasToken = containerSasBuilder.GenerateSasToken(*keyCredential); + auto blobContainerClient1 = GetSasAuthenticatedClient(blobContainerClient, sasToken); + auto appendBlobClient1 = blobContainerClient1.GetAppendBlobClient(RandomString()); + + appendBlobClient1.Create(); + auto properties = appendBlobClient1.GetProperties().Value; + ASSERT_TRUE(properties.EncryptionScope.HasValue()); + EXPECT_EQ(properties.EncryptionScope.Value(), encryptionScope); + } + + TEST_F(BlobSasTest, ServiceSasPermissionDeleteVersion) + { + auto sasStartsOn = std::chrono::system_clock::now() - std::chrono::minutes(5); + auto sasExpiresOn = std::chrono::system_clock::now() + std::chrono::minutes(60); + + auto keyCredential + = _internal::ParseConnectionString(StandardStorageConnectionString()).KeyCredential; + + auto blobClient = *m_blockBlobClient; + const std::string blobName = m_blobName; + auto response = blobClient.GetProperties(); + const std::string versionId = response.Value.VersionId.Value(); + blobClient.SetMetadata({}); // need to modify the blob so that the version id above does not + // point to the root blob. + + Sas::BlobSasBuilder blobSasBuilder; + blobSasBuilder.Protocol = Sas::SasProtocol::HttpsAndHttp; + blobSasBuilder.StartsOn = sasStartsOn; + blobSasBuilder.ExpiresOn = sasExpiresOn; + blobSasBuilder.BlobContainerName = m_containerName; + blobSasBuilder.BlobName = blobName; + blobSasBuilder.Resource = Sas::BlobSasResource::BlobVersion; + blobSasBuilder.SetPermissions(Sas::BlobSasPermissions::DeleteVersion); + blobSasBuilder.BlobVersionId = versionId; + auto sasToken = blobSasBuilder.GenerateSasToken(*keyCredential); + + auto blobClient1 = GetSasAuthenticatedClient(blobClient, sasToken); + blobClient1 = blobClient1.WithVersionId(versionId); + EXPECT_NO_THROW(blobClient1.Delete()); + } + }}} // namespace Azure::Storage::Test diff --git a/sdk/storage/azure-storage-queues/test/ut/queue_sas_test.cpp b/sdk/storage/azure-storage-queues/test/ut/queue_sas_test.cpp index 08d329f86..d0a6d832b 100644 --- a/sdk/storage/azure-storage-queues/test/ut/queue_sas_test.cpp +++ b/sdk/storage/azure-storage-queues/test/ut/queue_sas_test.cpp @@ -9,14 +9,105 @@ namespace Azure { namespace Storage { namespace Test { - TEST_F(QueueClientTest, QueueSasTest_LIVEONLY_) + class QueueSasTest : public QueueClientTest { + public: + template + T GetSasAuthenticatedClient(const T& queueClient, const std::string& sasToken) + { + T queueClient1( + AppendQueryParameters(Azure::Core::Url(queueClient.GetUrl()), sasToken), + InitStorageClientOptions()); + return queueClient1; + } + + void VerifyQueueSasRead(const Queues::QueueClient& queueClient, const std::string& sasToken) + { + auto queueClient1 = GetSasAuthenticatedClient(queueClient, sasToken); + EXPECT_NO_THROW(queueClient1.GetProperties()); + } + + void VerifyQueueSasNonRead(const Queues::QueueClient& queueClient, const std::string& sasToken) + { + auto queueClient1 = GetSasAuthenticatedClient(queueClient, sasToken); + EXPECT_THROW(queueClient1.GetProperties(), StorageException); + } + + void VerifyQueueSasAdd(const Queues::QueueClient& queueClient, const std::string& sasToken) + { + auto queueClient1 = GetSasAuthenticatedClient(queueClient, sasToken); + EXPECT_NO_THROW(queueClient1.EnqueueMessage("message1")); + } + + void VerifyQueueSasUpdate(const Queues::QueueClient& queueClient, const std::string& sasToken) + { + auto sendReceipt = queueClient.EnqueueMessage("message0").Value; + auto queueClient1 = GetSasAuthenticatedClient(queueClient, sasToken); + auto updateReceipt + = queueClient1 + .UpdateMessage( + sendReceipt.MessageId, sendReceipt.PopReceipt, std::chrono::seconds(0)) + .Value; + queueClient.DeleteMessage(sendReceipt.MessageId, updateReceipt.PopReceipt); + } + + void VerifyQueueSasProcess(const Queues::QueueClient& queueClient, const std::string& sasToken) + { + auto sendReceipt = queueClient.EnqueueMessage("message0").Value; + auto queueClient1 = GetSasAuthenticatedClient(queueClient, sasToken); + // Message deletion requires "p" permission + queueClient1.DeleteMessage(sendReceipt.MessageId, sendReceipt.PopReceipt); + } + + void VerifyQueueSasWrite(const Queues::QueueClient& queueClient, const std::string& sasToken) + { + auto queueClient1 = GetSasAuthenticatedClient(queueClient, sasToken); + Metadata m; + m["key1"] = "meta1"; + EXPECT_NO_THROW(queueClient1.SetMetadata(m)); + } + + void VerifyQueueSasList( + const Queues::QueueServiceClient& queueServiceClient, + const std::string& sasToken) + { + auto queueServiceClient1 = GetSasAuthenticatedClient(queueServiceClient, sasToken); + EXPECT_NO_THROW(queueServiceClient.ListQueues()); + } + + void VerifyQueueSasCreate( + const Queues::QueueServiceClient& queueServiceClient, + const std::string& newQueueName, + const std::string& sasToken) + { + auto queueServiceClient1 = GetSasAuthenticatedClient(queueServiceClient, sasToken); + EXPECT_NO_THROW(queueServiceClient1.CreateQueue(newQueueName)); + queueServiceClient.DeleteQueue(newQueueName); + } + + void VerifyQueueSasDelete( + const Queues::QueueServiceClient& queueServiceClient, + const std::string& newQueueName, + const std::string& sasToken) + + { + queueServiceClient.CreateQueue(newQueueName); + auto queueServiceClient1 = GetSasAuthenticatedClient(queueServiceClient, sasToken); + EXPECT_NO_THROW(queueServiceClient1.DeleteQueue(newQueueName)); + try + { + queueServiceClient.DeleteQueue(newQueueName); + } + catch (Azure::Storage::StorageException&) + { + } + } + }; + + TEST_F(QueueSasTest, AccountSasPermissions) { auto sasStartsOn = std::chrono::system_clock::now() - std::chrono::minutes(5); - auto sasExpiredOn = std::chrono::system_clock::now() - std::chrono::minutes(1); auto sasExpiresOn = std::chrono::system_clock::now() + std::chrono::minutes(60); - std::string queueName = LowercaseRandomString(); - Sas::AccountSasBuilder accountSasBuilder; accountSasBuilder.Protocol = Sas::SasProtocol::HttpsAndHttp; accountSasBuilder.StartsOn = sasStartsOn; @@ -24,75 +115,11 @@ namespace Azure { namespace Storage { namespace Test { accountSasBuilder.Services = Sas::AccountSasServices::Queue; accountSasBuilder.ResourceTypes = Sas::AccountSasResource::All; - Sas::QueueSasBuilder queueSasBuilder; - queueSasBuilder.Protocol = Sas::SasProtocol::HttpsAndHttp; - queueSasBuilder.StartsOn = sasStartsOn; - queueSasBuilder.ExpiresOn = sasExpiresOn; - queueSasBuilder.QueueName = queueName; - auto keyCredential = _internal::ParseConnectionString(StandardStorageConnectionString()).KeyCredential; - auto accountName = keyCredential->AccountName; - auto queueServiceClient0 - = Queues::QueueServiceClient::CreateFromConnectionString(StandardStorageConnectionString()); - auto queueClient0 = queueServiceClient0.GetQueueClient(queueName); - queueClient0.Create(); - std::string queueServiceUrl = queueServiceClient0.GetUrl(); - std::string queueUrl = queueClient0.GetUrl(); - - auto verifyQueueRead = [&](const std::string& sas) { - auto queueClient = Queues::QueueClient(queueUrl + sas); - queueClient.GetProperties(); - }; - - auto verifyQueueAdd = [&](const std::string& sas) { - auto queueClient = Queues::QueueClient(queueUrl + sas); - queueClient.EnqueueMessage("message1"); - }; - - auto verifyQueueUpdate = [&](const std::string& sas) { - auto sendReceipt = queueClient0.EnqueueMessage("message 0").Value; - auto queueClient = Queues::QueueClient(queueUrl + sas); - auto updateReceipt - = queueClient - .UpdateMessage( - sendReceipt.MessageId, sendReceipt.PopReceipt, std::chrono::seconds(0)) - .Value; - queueClient0.DeleteMessage(sendReceipt.MessageId, updateReceipt.PopReceipt); - }; - - auto verifyQueueProcess = [&](const std::string& sas) { - auto sendReceipt = queueClient0.EnqueueMessage("message 0").Value; - auto queueClient = Queues::QueueClient(queueUrl + sas); - queueClient.DeleteMessage(sendReceipt.MessageId, sendReceipt.PopReceipt); - }; - - auto verifyQueueWrite = [&](const std::string& sas) { - auto queueClient = Queues::QueueClient(queueUrl + sas); - Metadata m; - m["key1"] = RandomString(); - EXPECT_NO_THROW(queueClient.SetMetadata(m)); - }; - - auto verifyQueueList = [&](const std::string& sas) { - auto queueServiceClient = Queues::QueueServiceClient(queueServiceUrl + sas); - EXPECT_NO_THROW(queueServiceClient.ListQueues()); - }; - - auto verifyQueueCreate = [&](const std::string& sas) { - auto queueServiceClient = Queues::QueueServiceClient(queueServiceUrl + sas); - const std::string newQueueName = LowercaseRandomString(); - EXPECT_NO_THROW(queueServiceClient.CreateQueue(newQueueName)); - queueServiceClient0.GetQueueClient(newQueueName).Delete(); - }; - - auto verifyQueueDelete = [&](const std::string& sas) { - const std::string newQueueName = LowercaseRandomString(); - queueServiceClient0.CreateQueue(newQueueName); - auto queueServiceClient = Queues::QueueServiceClient(queueServiceUrl + sas); - EXPECT_NO_THROW(queueServiceClient.DeleteQueue(newQueueName)); - }; + auto queueClient = *m_queueClient; + auto queueServiceClient = *m_queueServiceClient; for (auto permissions : { Sas::AccountSasPermissions::All, @@ -111,38 +138,55 @@ namespace Azure { namespace Storage { namespace Test { if ((permissions & Sas::AccountSasPermissions::Read) == Sas::AccountSasPermissions::Read) { - verifyQueueRead(sasToken); + VerifyQueueSasRead(queueClient, sasToken); } if ((permissions & Sas::AccountSasPermissions::Write) == Sas::AccountSasPermissions::Write) { - verifyQueueWrite(sasToken); + VerifyQueueSasWrite(queueClient, sasToken); } if ((permissions & Sas::AccountSasPermissions::List) == Sas::AccountSasPermissions::List) { - verifyQueueList(sasToken); + VerifyQueueSasList(queueServiceClient, sasToken); } if ((permissions & Sas::AccountSasPermissions::Create) == Sas::AccountSasPermissions::Create) { - verifyQueueCreate(sasToken); + VerifyQueueSasCreate(queueServiceClient, LowercaseRandomString(), sasToken); } if ((permissions & Sas::AccountSasPermissions::Delete) == Sas::AccountSasPermissions::Delete) { - verifyQueueDelete(sasToken); + VerifyQueueSasDelete(queueServiceClient, LowercaseRandomString(), sasToken); } if ((permissions & Sas::AccountSasPermissions::Add) == Sas::AccountSasPermissions::Add) { - verifyQueueAdd(sasToken); + VerifyQueueSasAdd(queueClient, sasToken); } if ((permissions & Sas::AccountSasPermissions::Process) == Sas::AccountSasPermissions::Process) { - verifyQueueProcess(sasToken); + VerifyQueueSasProcess(queueClient, sasToken); } if ((permissions & Sas::AccountSasPermissions::Update) == Sas::AccountSasPermissions::Update) { - verifyQueueUpdate(sasToken); + VerifyQueueSasUpdate(queueClient, sasToken); } } + } + + TEST_F(QueueSasTest, ServiceSasPermissions) + { + auto sasStartsOn = std::chrono::system_clock::now() - std::chrono::minutes(5); + auto sasExpiresOn = std::chrono::system_clock::now() + std::chrono::minutes(60); + + Sas::QueueSasBuilder queueSasBuilder; + queueSasBuilder.Protocol = Sas::SasProtocol::HttpsAndHttp; + queueSasBuilder.StartsOn = sasStartsOn; + queueSasBuilder.ExpiresOn = sasExpiresOn; + queueSasBuilder.QueueName = m_queueName; + + auto keyCredential + = _internal::ParseConnectionString(StandardStorageConnectionString()).KeyCredential; + + auto queueClient = *m_queueClient; for (auto permissions : {Sas::QueueSasPermissions::Read, @@ -156,75 +200,121 @@ namespace Azure { namespace Storage { namespace Test { if ((permissions & Sas::QueueSasPermissions::Read) == Sas::QueueSasPermissions::Read) { - verifyQueueRead(sasToken); + VerifyQueueSasRead(queueClient, sasToken); } if ((permissions & Sas::QueueSasPermissions::Add) == Sas::QueueSasPermissions::Add) { - verifyQueueAdd(sasToken); + VerifyQueueSasAdd(queueClient, sasToken); } if ((permissions & Sas::QueueSasPermissions::Update) == Sas::QueueSasPermissions::Update) { - verifyQueueUpdate(sasToken); + VerifyQueueSasUpdate(queueClient, sasToken); } if ((permissions & Sas::QueueSasPermissions::Process) == Sas::QueueSasPermissions::Process) { - verifyQueueProcess(sasToken); + VerifyQueueSasProcess(queueClient, sasToken); } } + } + TEST_F(QueueSasTest, QueueSasExpired) + { + auto sasStartsOn = std::chrono::system_clock::now() - std::chrono::minutes(5); + auto sasExpiredOn = std::chrono::system_clock::now() - std::chrono::minutes(1); + auto sasExpiresOn = std::chrono::system_clock::now() + std::chrono::minutes(60); + + Sas::QueueSasBuilder queueSasBuilder; + queueSasBuilder.Protocol = Sas::SasProtocol::HttpsAndHttp; + queueSasBuilder.StartsOn = sasStartsOn; + queueSasBuilder.ExpiresOn = sasExpiredOn; + queueSasBuilder.QueueName = m_queueName; queueSasBuilder.SetPermissions(Sas::QueueSasPermissions::All); - // Expires - { - Sas::QueueSasBuilder builder2 = queueSasBuilder; - builder2.StartsOn = sasStartsOn; - builder2.ExpiresOn = sasExpiredOn; - auto sasToken = builder2.GenerateSasToken(*keyCredential); - EXPECT_THROW(verifyQueueRead(sasToken), StorageException); - } - // Without start time - { - Sas::QueueSasBuilder builder2 = queueSasBuilder; - builder2.StartsOn.Reset(); - auto sasToken = builder2.GenerateSasToken(*keyCredential); - EXPECT_NO_THROW(verifyQueueRead(sasToken)); - } + auto keyCredential + = _internal::ParseConnectionString(StandardStorageConnectionString()).KeyCredential; - // IP - { - Sas::QueueSasBuilder builder2 = queueSasBuilder; - builder2.IPRange = "0.0.0.0-0.0.0.1"; - auto sasToken = builder2.GenerateSasToken(*keyCredential); - EXPECT_THROW(verifyQueueRead(sasToken), StorageException); + auto queueClient = *m_queueClient; - // TODO: Add this test case back with support to contain IPv6 ranges when service is ready. - // builder2.IPRange = "0.0.0.0-255.255.255.255"; - // sasToken = builder2.GenerateSasToken(*keyCredential); - // EXPECT_NO_THROW(verifyQueueRead(sasToken)); - } + auto sasToken = queueSasBuilder.GenerateSasToken(*keyCredential); + VerifyQueueSasNonRead(queueClient, sasToken); - // Identifier - { - Queues::Models::SignedIdentifier identifier; - identifier.Id = RandomString(64); - identifier.StartsOn = sasStartsOn; - identifier.ExpiresOn = sasExpiresOn; - identifier.Permissions = "r"; - Queues::Models::QueueAccessPolicy accessPolicy; - accessPolicy.SignedIdentifiers.push_back(identifier); - queueClient0.SetAccessPolicy(accessPolicy); + queueSasBuilder.ExpiresOn = sasExpiresOn; + sasToken = queueSasBuilder.GenerateSasToken(*keyCredential); + VerifyQueueSasRead(queueClient, sasToken); + } - Sas::QueueSasBuilder builder2 = queueSasBuilder; - builder2.StartsOn.Reset(); - builder2.ExpiresOn = Azure::DateTime(); - builder2.SetPermissions(static_cast(0)); - builder2.Identifier = identifier.Id; + TEST_F(QueueSasTest, QueueSasWithoutStartTime) + { + auto sasExpiresOn = std::chrono::system_clock::now() + std::chrono::minutes(60); - auto sasToken = builder2.GenerateSasToken(*keyCredential); - // TODO: looks like a server bug, the identifier doesn't work sometimes. - // EXPECT_NO_THROW(verifyQueueRead(sasToken)); - } - queueClient0.Delete(); + Sas::QueueSasBuilder queueSasBuilder; + queueSasBuilder.Protocol = Sas::SasProtocol::HttpsAndHttp; + queueSasBuilder.ExpiresOn = sasExpiresOn; + queueSasBuilder.QueueName = m_queueName; + queueSasBuilder.SetPermissions(Sas::QueueSasPermissions::All); + + auto keyCredential + = _internal::ParseConnectionString(StandardStorageConnectionString()).KeyCredential; + auto sasToken = queueSasBuilder.GenerateSasToken(*keyCredential); + + auto queueClient = *m_queueClient; + VerifyQueueSasRead(queueClient, sasToken); + } + + TEST_F(QueueSasTest, QueueSasWithIP) + { + auto sasStartsOn = std::chrono::system_clock::now() - std::chrono::minutes(5); + auto sasExpiredOn = std::chrono::system_clock::now() + std::chrono::minutes(60); + + auto queueClient = *m_queueClient; + + Sas::QueueSasBuilder queueSasBuilder; + queueSasBuilder.Protocol = Sas::SasProtocol::HttpsAndHttp; + queueSasBuilder.StartsOn = sasStartsOn; + queueSasBuilder.ExpiresOn = sasExpiredOn; + queueSasBuilder.SetPermissions(Sas::QueueSasPermissions::All); + queueSasBuilder.QueueName = m_queueName; + + auto keyCredential + = _internal::ParseConnectionString(StandardStorageConnectionString()).KeyCredential; + auto sasToken = queueSasBuilder.GenerateSasToken(*keyCredential); + + VerifyQueueSasRead(queueClient, sasToken); + + queueSasBuilder.IPRange = "0.0.0.0-0.0.0.1"; + sasToken = queueSasBuilder.GenerateSasToken(*keyCredential); + VerifyQueueSasNonRead(queueClient, sasToken); + } + + TEST_F(QueueSasTest, QueueSasWithIdentifier) + { + auto sasStartsOn = std::chrono::system_clock::now() - std::chrono::minutes(5); + auto sasExpiresOn = std::chrono::system_clock::now() + std::chrono::minutes(60); + + auto queueClient = *m_queueClient; + Queues::Models::SignedIdentifier identifier; + identifier.Id = RandomString(64); + identifier.StartsOn = sasStartsOn; + identifier.ExpiresOn = sasExpiresOn; + identifier.Permissions = "r"; + Queues::Models::QueueAccessPolicy accessPolicy; + accessPolicy.SignedIdentifiers.push_back(identifier); + queueClient.SetAccessPolicy(accessPolicy); + + Sas::QueueSasBuilder queueSasBuilder; + queueSasBuilder.Protocol = Sas::SasProtocol::HttpsAndHttp; + queueSasBuilder.ExpiresOn = sasExpiresOn; + queueSasBuilder.SetPermissions(static_cast(0)); + queueSasBuilder.Identifier = identifier.Id; + queueSasBuilder.QueueName = m_queueName; + + auto keyCredential + = _internal::ParseConnectionString(StandardStorageConnectionString()).KeyCredential; + auto sasToken = queueSasBuilder.GenerateSasToken(*keyCredential); + + TestSleep(std::chrono::seconds(30)); + + VerifyQueueSasRead(queueClient, sasToken); } }}} // namespace Azure::Storage::Test