diff --git a/sdk/storage/azure-storage-files-datalake/inc/azure/storage/files/datalake/datalake_sas_builder.hpp b/sdk/storage/azure-storage-files-datalake/inc/azure/storage/files/datalake/datalake_sas_builder.hpp index 85c85100ac..dba43e533e 100644 --- a/sdk/storage/azure-storage-files-datalake/inc/azure/storage/files/datalake/datalake_sas_builder.hpp +++ b/sdk/storage/azure-storage-files-datalake/inc/azure/storage/files/datalake/datalake_sas_builder.hpp @@ -293,6 +293,11 @@ namespace Azure { namespace Storage { namespace Sas { */ std::string CorrelationId; + /** + * @brief Optional encryption scope to use when sending requests authorized with this SAS url. + */ + std::string EncryptionScope; + /** * @brief Sets the permissions for the filesystem SAS. * diff --git a/sdk/storage/azure-storage-files-datalake/src/datalake_sas_builder.cpp b/sdk/storage/azure-storage-files-datalake/src/datalake_sas_builder.cpp index 53aadf9246..aecadd89da 100644 --- a/sdk/storage/azure-storage-files-datalake/src/datalake_sas_builder.cpp +++ b/sdk/storage/azure-storage-files-datalake/src/datalake_sas_builder.cpp @@ -10,7 +10,7 @@ namespace Azure { namespace Storage { namespace Sas { namespace { - constexpr static const char* SasVersion = "2020-02-10"; + constexpr static const char* SasVersion = "2021-06-08"; std::string DataLakeSasResourceToString(DataLakeSasResource resource) { @@ -138,8 +138,9 @@ namespace Azure { namespace Storage { namespace Sas { std::string stringToSign = Permissions + "\n" + startsOnStr + "\n" + expiresOnStr + "\n" + canonicalName + "\n" + Identifier + "\n" + (IPRange.HasValue() ? IPRange.Value() : "") - + "\n" + protocol + "\n" + SasVersion + "\n" + resource + "\n" + "\n" + CacheControl + "\n" - + ContentDisposition + "\n" + ContentEncoding + "\n" + ContentLanguage + "\n" + ContentType; + + "\n" + protocol + "\n" + SasVersion + "\n" + resource + "\n" + "\n" + EncryptionScope + + "\n" + CacheControl + "\n" + ContentDisposition + "\n" + ContentEncoding + "\n" + + ContentLanguage + "\n" + ContentType; std::string signature = Azure::Core::Convert::Base64Encode(_internal::HmacSha256( std::vector(stringToSign.begin(), stringToSign.end()), @@ -190,6 +191,10 @@ namespace Azure { namespace Storage { namespace Sas { { builder.AppendQueryParameter("rsct", _internal::UrlEncodeQueryParameter(ContentType)); } + if (!EncryptionScope.empty()) + { + builder.AppendQueryParameter("ses", _internal::UrlEncodeQueryParameter(EncryptionScope)); + } return builder.GetAbsoluteUrl(); } @@ -223,8 +228,8 @@ namespace Azure { namespace Storage { namespace Sas { + "\n" + userDelegationKey.SignedService + "\n" + userDelegationKey.SignedVersion + "\n" + PreauthorizedAgentObjectId + "\n" + AgentObjectId + "\n" + CorrelationId + "\n" + (IPRange.HasValue() ? IPRange.Value() : "") + "\n" + protocol + "\n" + SasVersion + "\n" - + resource + "\n" + "\n" + CacheControl + "\n" + ContentDisposition + "\n" + ContentEncoding - + "\n" + ContentLanguage + "\n" + ContentType; + + resource + "\n" + "\n" + EncryptionScope + "\n" + CacheControl + "\n" + ContentDisposition + + "\n" + ContentEncoding + "\n" + ContentLanguage + "\n" + ContentType; std::string signature = Azure::Core::Convert::Base64Encode(_internal::HmacSha256( std::vector(stringToSign.begin(), stringToSign.end()), @@ -292,6 +297,10 @@ namespace Azure { namespace Storage { namespace Sas { { builder.AppendQueryParameter("rsct", _internal::UrlEncodeQueryParameter(ContentType)); } + if (!EncryptionScope.empty()) + { + builder.AppendQueryParameter("ses", _internal::UrlEncodeQueryParameter(EncryptionScope)); + } builder.AppendQueryParameter("sig", _internal::UrlEncodeQueryParameter(signature)); return builder.GetAbsoluteUrl(); diff --git a/sdk/storage/azure-storage-files-datalake/test/ut/datalake_sas_test.cpp b/sdk/storage/azure-storage-files-datalake/test/ut/datalake_sas_test.cpp index 4615159f45..75c25f8404 100644 --- a/sdk/storage/azure-storage-files-datalake/test/ut/datalake_sas_test.cpp +++ b/sdk/storage/azure-storage-files-datalake/test/ut/datalake_sas_test.cpp @@ -441,6 +441,39 @@ namespace Azure { namespace Storage { namespace Test { EXPECT_EQ(p.Value.HttpHeaders.CacheControl, headers.CacheControl); EXPECT_EQ(p.Value.HttpHeaders.ContentEncoding, headers.ContentEncoding); } + + // Encryption scope + const auto encryptionScope = GetTestEncryptionScope(); + { + auto sasBuilderWithEncryptionScope = fileSasBuilder; + sasBuilderWithEncryptionScope.EncryptionScope = encryptionScope; + sasBuilderWithEncryptionScope.SetPermissions(Sas::DataLakeSasPermissions::All); + auto fileClientEncryptionScopeSas = Files::DataLake::DataLakeFileClient( + fileUrl + sasBuilderWithEncryptionScope.GenerateSasToken(*keyCredential)); + fileClientEncryptionScopeSas.Create(); + auto pRawResponse = fileClientEncryptionScopeSas.GetProperties().RawResponse; + ASSERT_TRUE(pRawResponse->GetHeaders().count("x-ms-encryption-scope") != 0); + EXPECT_EQ(pRawResponse->GetHeaders().at("x-ms-encryption-scope"), encryptionScope); + + fileClientEncryptionScopeSas = Files::DataLake::DataLakeFileClient( + fileUrl + sasBuilderWithEncryptionScope.GenerateSasToken(userDelegationKey, accountName)); + fileClientEncryptionScopeSas.Create(); + pRawResponse = fileClientEncryptionScopeSas.GetProperties().RawResponse; + ASSERT_TRUE(pRawResponse->GetHeaders().count("x-ms-encryption-scope") != 0); + EXPECT_EQ(pRawResponse->GetHeaders().at("x-ms-encryption-scope"), encryptionScope); + } + { + auto sasBuilderWithEncryptionScope = directorySasBuilder; + sasBuilderWithEncryptionScope.EncryptionScope = encryptionScope; + sasBuilderWithEncryptionScope.SetPermissions(Sas::DataLakeSasPermissions::All); + auto directoryClientEncryptionScopeSas = Files::DataLake::DataLakeDirectoryClient( + directory1Url + + sasBuilderWithEncryptionScope.GenerateSasToken(userDelegationKey, accountName)); + directoryClientEncryptionScopeSas.Create(); + auto pRawResponse = directoryClientEncryptionScopeSas.GetProperties().RawResponse; + ASSERT_TRUE(pRawResponse->GetHeaders().count("x-ms-encryption-scope") != 0); + EXPECT_EQ(pRawResponse->GetHeaders().at("x-ms-encryption-scope"), encryptionScope); + } } }}} // namespace Azure::Storage::Test diff --git a/sdk/storage/test-resources-post.ps1 b/sdk/storage/test-resources-post.ps1 index 539c242e10..a02df05f80 100644 --- a/sdk/storage/test-resources-post.ps1 +++ b/sdk/storage/test-resources-post.ps1 @@ -8,6 +8,8 @@ param( New-AzStorageEncryptionScope -ResourceGroupName $ResourceGroupName -StorageAccountName $DeploymentOutputs['ACCOUNT_NAME'] -EncryptionScopeName "EncryptionScopeForTest" -StorageEncryption +New-AzStorageEncryptionScope -ResourceGroupName $ResourceGroupName -StorageAccountName $DeploymentOutputs['DATALAKE_ACCOUNT_NAME'] -EncryptionScopeName "EncryptionScopeForTest" -StorageEncryption + # This script is used to wait until XCache is refreshed for the service properties (30s), and role assignment takes effect (300s). Start-Sleep -s 300