diff --git a/sdk/storage/azure-storage-files-datalake/inc/azure/storage/files/datalake/datalake_options.hpp b/sdk/storage/azure-storage-files-datalake/inc/azure/storage/files/datalake/datalake_options.hpp index f2209f88fa..5318df89ed 100644 --- a/sdk/storage/azure-storage-files-datalake/inc/azure/storage/files/datalake/datalake_options.hpp +++ b/sdk/storage/azure-storage-files-datalake/inc/azure/storage/files/datalake/datalake_options.hpp @@ -634,6 +634,14 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake { * Specify the access condition for the path. */ PathAccessConditions AccessConditions; + + /** + * Encryption context of the file. Encryption context is metadata that is not encrypted when + * stored on the file. The primary application of this field is to store non-encrypted data that + * can be used to derive the customer-provided key for a file. + * Not applicable for directories. + */ + Azure::Nullable EncryptionContext; }; /** diff --git a/sdk/storage/azure-storage-files-datalake/inc/azure/storage/files/datalake/datalake_responses.hpp b/sdk/storage/azure-storage-files-datalake/inc/azure/storage/files/datalake/datalake_responses.hpp index 0fd6a2dc4c..2073510f8b 100644 --- a/sdk/storage/azure-storage-files-datalake/inc/azure/storage/files/datalake/datalake_responses.hpp +++ b/sdk/storage/azure-storage-files-datalake/inc/azure/storage/files/datalake/datalake_responses.hpp @@ -266,6 +266,14 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake { */ Nullable EncryptionScope; + /** + * Encryption context of the file. Encryption context is metadata that is not encrypted when + * stored on the file. The primary application of this field is to store non-encrypted data + * that can be used to derive the customer-provided key for a file. + * Not applicable for directories. + */ + Nullable EncryptionContext; + /** * The creation time of the path. */ @@ -349,6 +357,14 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake { */ Nullable EncryptionScope; + /** + * Encryption context of the file. Encryption context is metadata that is not encrypted when + * stored on the file. The primary application of this field is to store non-encrypted data + * that can be used to derive the customer-provided key for a file. + * Not applicable for directories. + */ + Nullable EncryptionContext; + /** * The copy ID of the path, if the path is created from a copy operation. */ @@ -664,6 +680,14 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake { * The encryption scope. */ Azure::Nullable EncryptionScope; + + /* + * Encryption context of the file. Encryption context is metadata that is not encrypted when + * stored on the file. The primary application of this field is to store non-encrypted data + * that can be used to derive the customer-provided key for a file. + * Not applicable for directories. + */ + Nullable EncryptionContext; }; /** diff --git a/sdk/storage/azure-storage-files-datalake/src/datalake_file_client.cpp b/sdk/storage/azure-storage-files-datalake/src/datalake_file_client.cpp index aa9018b9d1..815c5ee2e3 100644 --- a/sdk/storage/azure-storage-files-datalake/src/datalake_file_client.cpp +++ b/sdk/storage/azure-storage-files-datalake/src/datalake_file_client.cpp @@ -211,6 +211,9 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake { ret.Details.EncryptionKeySha256 = std::move(response.Value.Details.EncryptionKeySha256); ret.Details.EncryptionScope = std::move(response.Value.Details.EncryptionScope); ret.Details.IsServerEncrypted = response.Value.Details.IsServerEncrypted; + ret.Details.EncryptionContext + = Azure::Core::Http::_internal::HttpShared::GetHeaderOrEmptyString( + response.RawResponse->GetHeaders(), _detail::EncryptionContextHeaderName); return Azure::Response( std::move(ret), std::move(response.RawResponse)); } diff --git a/sdk/storage/azure-storage-files-datalake/src/datalake_file_system_client.cpp b/sdk/storage/azure-storage-files-datalake/src/datalake_file_system_client.cpp index e08f274c7b..5e7b089490 100644 --- a/sdk/storage/azure-storage-files-datalake/src/datalake_file_system_client.cpp +++ b/sdk/storage/azure-storage-files-datalake/src/datalake_file_system_client.cpp @@ -292,6 +292,7 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake { item.Group = std::move(path.Group); item.Permissions = std::move(path.Permissions); item.EncryptionScope = std::move(path.EncryptionScope); + item.EncryptionContext = std::move(path.EncryptionContext); item.ETag = std::move(path.ETag); if (path.CreatedOn.HasValue()) { diff --git a/sdk/storage/azure-storage-files-datalake/src/datalake_path_client.cpp b/sdk/storage/azure-storage-files-datalake/src/datalake_path_client.cpp index 4dbc55e089..7c1f390f9f 100644 --- a/sdk/storage/azure-storage-files-datalake/src/datalake_path_client.cpp +++ b/sdk/storage/azure-storage-files-datalake/src/datalake_path_client.cpp @@ -208,6 +208,7 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake { protocolLayerOptions.Owner = options.Owner; protocolLayerOptions.Group = options.Group; protocolLayerOptions.ProposedLeaseId = options.LeaseId; + protocolLayerOptions.EncryptionContext = options.EncryptionContext; if (options.Acls.HasValue()) { protocolLayerOptions.Acl = Models::Acl::SerializeAcls(options.Acls.Value()); @@ -341,6 +342,8 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake { ret.VersionId = std::move(response.Value.VersionId); ret.IsCurrentVersion = std::move(response.Value.IsCurrentVersion); ret.IsDirectory = _detail::MetadataIncidatesIsDirectory(ret.Metadata); + ret.EncryptionContext = Azure::Core::Http::_internal::HttpShared::GetHeaderOrEmptyString( + response.RawResponse->GetHeaders(), _detail::EncryptionContextHeaderName); return Azure::Response(std::move(ret), std::move(response.RawResponse)); } diff --git a/sdk/storage/azure-storage-files-datalake/src/private/datalake_constants.hpp b/sdk/storage/azure-storage-files-datalake/src/private/datalake_constants.hpp index 19102b1003..682f9733e6 100644 --- a/sdk/storage/azure-storage-files-datalake/src/private/datalake_constants.hpp +++ b/sdk/storage/azure-storage-files-datalake/src/private/datalake_constants.hpp @@ -11,5 +11,6 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake { nam constexpr static const char* DataLakePathNotFound = "PathNotFound"; constexpr static const char* DataLakePathAlreadyExists = "PathAlreadyExists"; constexpr static const char* DataLakeIsDirectoryKey = "hdi_isFolder"; + constexpr static const char* EncryptionContextHeaderName = "x-ms-encryption-context"; }}}}} // namespace Azure::Storage::Files::DataLake::_detail diff --git a/sdk/storage/azure-storage-files-datalake/test/ut/datalake_file_client_test.cpp b/sdk/storage/azure-storage-files-datalake/test/ut/datalake_file_client_test.cpp index d7e1f28780..002866c6a8 100644 --- a/sdk/storage/azure-storage-files-datalake/test/ut/datalake_file_client_test.cpp +++ b/sdk/storage/azure-storage-files-datalake/test/ut/datalake_file_client_test.cpp @@ -469,6 +469,34 @@ namespace Azure { namespace Storage { namespace Test { } } + TEST_F(DataLakeFileClientTest, DISABLED_CreateWithEncryptionContext) + { + std::string encryptionContext = "encryptionContext"; + const std::string fileName = RandomString(); + auto fileClient = m_fileSystemClient->GetFileClient(fileName); + Files::DataLake::CreateFileOptions options; + options.EncryptionContext = encryptionContext; + // Assert Create + EXPECT_NO_THROW(fileClient.Create(options)); + // Assert GetProperties + auto properties = fileClient.GetProperties(); + EXPECT_TRUE(properties.Value.EncryptionContext.HasValue()); + EXPECT_EQ(encryptionContext, properties.Value.EncryptionContext.Value()); + // Assert Download + auto downloadResult = fileClient.Download(); + EXPECT_TRUE(downloadResult.Value.Details.EncryptionContext.HasValue()); + EXPECT_EQ(encryptionContext, downloadResult.Value.Details.EncryptionContext.Value()); + // Assert ListPaths + auto paths = m_fileSystemClient->ListPaths(false).Paths; + auto iter = std::find_if( + paths.begin(), paths.end(), [&fileName](const Files::DataLake::Models::PathItem& path) { + return path.Name == fileName; + }); + EXPECT_NE(paths.end(), iter); + EXPECT_TRUE(iter->EncryptionContext.HasValue()); + EXPECT_EQ(encryptionContext, iter->EncryptionContext.Value()); + } + TEST_F(DataLakeFileClientTest, FileReadReturns) { const int32_t bufferSize = 4 * 1024; // 4KB data size