From 94ac9cab84a181f1fa5d67041edf6e95d2ab2d8e Mon Sep 17 00:00:00 2001 From: microzchang <110015819+microzchang@users.noreply.github.com> Date: Tue, 9 Dec 2025 22:48:40 +0800 Subject: [PATCH] Storage/Stg101/Source Customer Provided Key and AccessTierConditions for Delete Blob API (#6869) * Source CMK and Delete condition for AccessTier * Rename * Fix Cspell --- .../inc/azure/storage/blobs/blob_options.hpp | 55 ++++++++++++++++++- .../inc/azure/storage/blobs/rest_client.hpp | 20 +++---- .../src/append_blob_client.cpp | 8 +++ .../azure-storage-blobs/src/blob_client.cpp | 4 ++ .../src/block_blob_client.cpp | 16 ++++++ .../src/page_blob_client.cpp | 9 +++ .../azure-storage-blobs/src/rest_client.cpp | 40 +++++++------- .../azure-storage-blobs/swagger/README.md | 13 +++++ .../test/ut/blob_sas_test.cpp | 2 + .../test/ut/datalake_sas_test.cpp | 2 + 10 files changed, 138 insertions(+), 31 deletions(-) diff --git a/sdk/storage/azure-storage-blobs/inc/azure/storage/blobs/blob_options.hpp b/sdk/storage/azure-storage-blobs/inc/azure/storage/blobs/blob_options.hpp index 0adefbc4a..1bcbb9346 100644 --- a/sdk/storage/azure-storage-blobs/inc/azure/storage/blobs/blob_options.hpp +++ b/sdk/storage/azure-storage-blobs/inc/azure/storage/blobs/blob_options.hpp @@ -85,13 +85,42 @@ namespace Azure { namespace Storage { namespace Blobs { Azure::Nullable TagConditions; }; + /** + * @brief Specifies HTTP options for conditional requests based on AccessTier. + */ + struct AccessTierAccessConditions + { + /** + * @brief Destructor. + * + */ + virtual ~AccessTierAccessConditions() = default; + + /** + * @brief Specify this header value to operate only on a blob if the access-tier has been + * modified since the specified date/time. Note: If this is specified, + * AccessTierIfUnmodifiedSince cannot be specified. + * Only valid for Delete Blob API. + */ + Azure::Nullable AccessTierIfModifiedSince; + + /** + * @brief Specify this header value to operate only on a blob if the access-tier has not been + * modified since the specified date/time. Note: If this is specified, AccessTierIfModifiedSince + * cannot be specified. + * Only valid for Delete Blob API. + */ + Azure::Nullable AccessTierIfUnmodifiedSince; + }; + /** * @brief Specifies access conditions for a blob. */ struct BlobAccessConditions : public Azure::ModifiedConditions, public Azure::MatchConditions, public LeaseAccessConditions, - public TagAccessConditions + public TagAccessConditions, + public AccessTierAccessConditions { }; @@ -1086,6 +1115,12 @@ namespace Azure { namespace Storage { namespace Blobs { * token authentication. Used to indicate the intent of the request. */ Azure::Nullable FileRequestIntent; + + /** + * Optional. Specifies the source customer provided key to use to encrypt the source blob. + * Applicable only for service version 2026-02-06 or later. + */ + Azure::Nullable SourceCustomerProvidedKey; }; /** @@ -1153,6 +1188,12 @@ namespace Azure { namespace Storage { namespace Blobs { * token authentication. Used to indicate the intent of the request. */ Azure::Nullable FileRequestIntent; + + /** + * Optional. Specifies the source customer provided key to use to encrypt the source blob. + * Applicable only for service version 2026-02-06 or later. + */ + Azure::Nullable SourceCustomerProvidedKey; }; /** @@ -1457,6 +1498,12 @@ namespace Azure { namespace Storage { namespace Blobs { * token authentication. Used to indicate the intent of the request. */ Azure::Nullable FileRequestIntent; + + /** + * Optional. Specifies the source customer provided key to use to encrypt the source blob. + * Applicable only for service version 2026-02-06 or later. + */ + Azure::Nullable SourceCustomerProvidedKey; }; /** @@ -1579,6 +1626,12 @@ namespace Azure { namespace Storage { namespace Blobs { * token authentication. Used to indicate the intent of the request. */ Azure::Nullable FileRequestIntent; + + /** + * Optional. Specifies the source customer provided key to use to encrypt the source blob. + * Applicable only for service version 2026-02-06 or later. + */ + Azure::Nullable SourceCustomerProvidedKey; }; /** diff --git a/sdk/storage/azure-storage-blobs/inc/azure/storage/blobs/rest_client.hpp b/sdk/storage/azure-storage-blobs/inc/azure/storage/blobs/rest_client.hpp index 043c31556..720ff1a32 100644 --- a/sdk/storage/azure-storage-blobs/inc/azure/storage/blobs/rest_client.hpp +++ b/sdk/storage/azure-storage-blobs/inc/azure/storage/blobs/rest_client.hpp @@ -36,8 +36,8 @@ namespace Azure { namespace Storage { namespace Blobs { } // namespace _detail namespace Models { /** - * @brief The algorithm used to produce the source encryption key hash. Currently, the only - * accepted value is "AES256". Must be provided if the x-ms-source-encryption-key is provided. + * @brief The algorithm used to produce the encryption key hash. Currently, the only accepted + * value is "AES256". Must be provided if the x-ms-encryption-key header is provided. */ class EncryptionAlgorithmType final : public Core::_internal::ExtendableEnumeration { @@ -4041,8 +4041,8 @@ namespace Azure { namespace Storage { namespace Blobs { Nullable CopySourceAuthorization; Nullable FileRequestIntent; Nullable SourceEncryptionKey; - Nullable SourceEncryptionKeySha256; - Nullable SourceEncryptionAlgorithm; + Nullable> SourceEncryptionKeySha256; + Nullable SourceEncryptionAlgorithm; }; static Response UploadPagesFromUri( Core::Http::_internal::HttpPipeline& pipeline, @@ -4219,8 +4219,8 @@ namespace Azure { namespace Storage { namespace Blobs { Nullable CopySourceAuthorization; Nullable FileRequestIntent; Nullable SourceEncryptionKey; - Nullable SourceEncryptionKeySha256; - Nullable SourceEncryptionAlgorithm; + Nullable> SourceEncryptionKeySha256; + Nullable SourceEncryptionAlgorithm; }; static Response AppendBlockFromUri( Core::Http::_internal::HttpPipeline& pipeline, @@ -4312,8 +4312,8 @@ namespace Azure { namespace Storage { namespace Blobs { Nullable CopySourceTags; Nullable FileRequestIntent; Nullable SourceEncryptionKey; - Nullable SourceEncryptionKeySha256; - Nullable SourceEncryptionAlgorithm; + Nullable> SourceEncryptionKeySha256; + Nullable SourceEncryptionAlgorithm; Nullable> SourceContentcrc64; }; static Response UploadFromUri( @@ -4359,8 +4359,8 @@ namespace Azure { namespace Storage { namespace Blobs { Nullable CopySourceAuthorization; Nullable FileRequestIntent; Nullable SourceEncryptionKey; - Nullable SourceEncryptionKeySha256; - Nullable SourceEncryptionAlgorithm; + Nullable> SourceEncryptionKeySha256; + Nullable SourceEncryptionAlgorithm; }; static Response StageBlockFromUri( Core::Http::_internal::HttpPipeline& pipeline, diff --git a/sdk/storage/azure-storage-blobs/src/append_blob_client.cpp b/sdk/storage/azure-storage-blobs/src/append_blob_client.cpp index 758a9beb8..bbe4c7756 100644 --- a/sdk/storage/azure-storage-blobs/src/append_blob_client.cpp +++ b/sdk/storage/azure-storage-blobs/src/append_blob_client.cpp @@ -240,6 +240,14 @@ namespace Azure { namespace Storage { namespace Blobs { protocolLayerOptions.CopySourceAuthorization = options.SourceAuthorization; } protocolLayerOptions.FileRequestIntent = options.FileRequestIntent; + if (options.SourceCustomerProvidedKey.HasValue()) + { + protocolLayerOptions.SourceEncryptionKey = options.SourceCustomerProvidedKey.Value().Key; + protocolLayerOptions.SourceEncryptionKeySha256 + = options.SourceCustomerProvidedKey.Value().KeyHash; + protocolLayerOptions.SourceEncryptionAlgorithm + = options.SourceCustomerProvidedKey.Value().Algorithm.ToString(); + } return _detail::AppendBlobClient::AppendBlockFromUri( *m_pipeline, m_blobUrl, protocolLayerOptions, context); diff --git a/sdk/storage/azure-storage-blobs/src/blob_client.cpp b/sdk/storage/azure-storage-blobs/src/blob_client.cpp index 893c896ab..aabea0a3e 100644 --- a/sdk/storage/azure-storage-blobs/src/blob_client.cpp +++ b/sdk/storage/azure-storage-blobs/src/blob_client.cpp @@ -854,6 +854,10 @@ namespace Azure { namespace Storage { namespace Blobs { protocolLayerOptions.IfMatch = options.AccessConditions.IfMatch; protocolLayerOptions.IfNoneMatch = options.AccessConditions.IfNoneMatch; protocolLayerOptions.IfTags = options.AccessConditions.TagConditions; + protocolLayerOptions.AccessTierIfModifiedSince + = options.AccessConditions.AccessTierIfModifiedSince; + protocolLayerOptions.AccessTierIfUnmodifiedSince + = options.AccessConditions.AccessTierIfUnmodifiedSince; return _detail::BlobClient::Delete(*m_pipeline, m_blobUrl, protocolLayerOptions, context); } diff --git a/sdk/storage/azure-storage-blobs/src/block_blob_client.cpp b/sdk/storage/azure-storage-blobs/src/block_blob_client.cpp index 8d317549c..ea7a36279 100644 --- a/sdk/storage/azure-storage-blobs/src/block_blob_client.cpp +++ b/sdk/storage/azure-storage-blobs/src/block_blob_client.cpp @@ -408,6 +408,14 @@ namespace Azure { namespace Storage { namespace Blobs { protocolLayerOptions.CopySourceAuthorization = options.SourceAuthorization; } protocolLayerOptions.FileRequestIntent = options.FileRequestIntent; + if (options.SourceCustomerProvidedKey.HasValue()) + { + protocolLayerOptions.SourceEncryptionKey = options.SourceCustomerProvidedKey.Value().Key; + protocolLayerOptions.SourceEncryptionKeySha256 + = options.SourceCustomerProvidedKey.Value().KeyHash; + protocolLayerOptions.SourceEncryptionAlgorithm + = options.SourceCustomerProvidedKey.Value().Algorithm.ToString(); + } return _detail::BlockBlobClient::UploadFromUri( *m_pipeline, m_blobUrl, protocolLayerOptions, context); @@ -512,6 +520,14 @@ namespace Azure { namespace Storage { namespace Blobs { protocolLayerOptions.CopySourceAuthorization = options.SourceAuthorization; } protocolLayerOptions.FileRequestIntent = options.FileRequestIntent; + if (options.SourceCustomerProvidedKey.HasValue()) + { + protocolLayerOptions.SourceEncryptionKey = options.SourceCustomerProvidedKey.Value().Key; + protocolLayerOptions.SourceEncryptionKeySha256 + = options.SourceCustomerProvidedKey.Value().KeyHash; + protocolLayerOptions.SourceEncryptionAlgorithm + = options.SourceCustomerProvidedKey.Value().Algorithm.ToString(); + } return _detail::BlockBlobClient::StageBlockFromUri( *m_pipeline, m_blobUrl, protocolLayerOptions, context); diff --git a/sdk/storage/azure-storage-blobs/src/page_blob_client.cpp b/sdk/storage/azure-storage-blobs/src/page_blob_client.cpp index cab82aa80..a05757a4e 100644 --- a/sdk/storage/azure-storage-blobs/src/page_blob_client.cpp +++ b/sdk/storage/azure-storage-blobs/src/page_blob_client.cpp @@ -258,6 +258,15 @@ namespace Azure { namespace Storage { namespace Blobs { protocolLayerOptions.CopySourceAuthorization = options.SourceAuthorization; } protocolLayerOptions.FileRequestIntent = options.FileRequestIntent; + if (options.SourceCustomerProvidedKey.HasValue()) + { + protocolLayerOptions.SourceEncryptionKey + = options.SourceCustomerProvidedKey.Value().Key; + protocolLayerOptions.SourceEncryptionKeySha256 + = options.SourceCustomerProvidedKey.Value().KeyHash; + protocolLayerOptions.SourceEncryptionAlgorithm + = options.SourceCustomerProvidedKey.Value().Algorithm.ToString(); + } return _detail::PageBlobClient::UploadPagesFromUri( *m_pipeline, m_blobUrl, protocolLayerOptions, context); diff --git a/sdk/storage/azure-storage-blobs/src/rest_client.cpp b/sdk/storage/azure-storage-blobs/src/rest_client.cpp index 2ca0dd7a3..bf9006ee7 100644 --- a/sdk/storage/azure-storage-blobs/src/rest_client.cpp +++ b/sdk/storage/azure-storage-blobs/src/rest_client.cpp @@ -6478,17 +6478,17 @@ namespace Azure { namespace Storage { namespace Blobs { request.SetHeader("x-ms-source-encryption-key", options.SourceEncryptionKey.Value()); } if (options.SourceEncryptionKeySha256.HasValue() - && !options.SourceEncryptionKeySha256.Value().empty()) + && !Core::Convert::Base64Encode(options.SourceEncryptionKeySha256.Value()).empty()) { request.SetHeader( - "x-ms-source-encryption-key-sha256", options.SourceEncryptionKeySha256.Value()); + "x-ms-source-encryption-key-sha256", + Core::Convert::Base64Encode(options.SourceEncryptionKeySha256.Value())); } if (options.SourceEncryptionAlgorithm.HasValue() - && !options.SourceEncryptionAlgorithm.Value().ToString().empty()) + && !options.SourceEncryptionAlgorithm.Value().empty()) { request.SetHeader( - "x-ms-source-encryption-algorithm", - options.SourceEncryptionAlgorithm.Value().ToString()); + "x-ms-source-encryption-algorithm", options.SourceEncryptionAlgorithm.Value()); } auto pRawResponse = pipeline.Send(request, context); auto httpStatusCode = pRawResponse->GetStatusCode(); @@ -7490,17 +7490,17 @@ namespace Azure { namespace Storage { namespace Blobs { request.SetHeader("x-ms-source-encryption-key", options.SourceEncryptionKey.Value()); } if (options.SourceEncryptionKeySha256.HasValue() - && !options.SourceEncryptionKeySha256.Value().empty()) + && !Core::Convert::Base64Encode(options.SourceEncryptionKeySha256.Value()).empty()) { request.SetHeader( - "x-ms-source-encryption-key-sha256", options.SourceEncryptionKeySha256.Value()); + "x-ms-source-encryption-key-sha256", + Core::Convert::Base64Encode(options.SourceEncryptionKeySha256.Value())); } if (options.SourceEncryptionAlgorithm.HasValue() - && !options.SourceEncryptionAlgorithm.Value().ToString().empty()) + && !options.SourceEncryptionAlgorithm.Value().empty()) { request.SetHeader( - "x-ms-source-encryption-algorithm", - options.SourceEncryptionAlgorithm.Value().ToString()); + "x-ms-source-encryption-algorithm", options.SourceEncryptionAlgorithm.Value()); } auto pRawResponse = pipeline.Send(request, context); auto httpStatusCode = pRawResponse->GetStatusCode(); @@ -7929,17 +7929,17 @@ namespace Azure { namespace Storage { namespace Blobs { request.SetHeader("x-ms-source-encryption-key", options.SourceEncryptionKey.Value()); } if (options.SourceEncryptionKeySha256.HasValue() - && !options.SourceEncryptionKeySha256.Value().empty()) + && !Core::Convert::Base64Encode(options.SourceEncryptionKeySha256.Value()).empty()) { request.SetHeader( - "x-ms-source-encryption-key-sha256", options.SourceEncryptionKeySha256.Value()); + "x-ms-source-encryption-key-sha256", + Core::Convert::Base64Encode(options.SourceEncryptionKeySha256.Value())); } if (options.SourceEncryptionAlgorithm.HasValue() - && !options.SourceEncryptionAlgorithm.Value().ToString().empty()) + && !options.SourceEncryptionAlgorithm.Value().empty()) { request.SetHeader( - "x-ms-source-encryption-algorithm", - options.SourceEncryptionAlgorithm.Value().ToString()); + "x-ms-source-encryption-algorithm", options.SourceEncryptionAlgorithm.Value()); } if (options.SourceContentcrc64.HasValue() && !Core::Convert::Base64Encode(options.SourceContentcrc64.Value()).empty()) @@ -8186,17 +8186,17 @@ namespace Azure { namespace Storage { namespace Blobs { request.SetHeader("x-ms-source-encryption-key", options.SourceEncryptionKey.Value()); } if (options.SourceEncryptionKeySha256.HasValue() - && !options.SourceEncryptionKeySha256.Value().empty()) + && !Core::Convert::Base64Encode(options.SourceEncryptionKeySha256.Value()).empty()) { request.SetHeader( - "x-ms-source-encryption-key-sha256", options.SourceEncryptionKeySha256.Value()); + "x-ms-source-encryption-key-sha256", + Core::Convert::Base64Encode(options.SourceEncryptionKeySha256.Value())); } if (options.SourceEncryptionAlgorithm.HasValue() - && !options.SourceEncryptionAlgorithm.Value().ToString().empty()) + && !options.SourceEncryptionAlgorithm.Value().empty()) { request.SetHeader( - "x-ms-source-encryption-algorithm", - options.SourceEncryptionAlgorithm.Value().ToString()); + "x-ms-source-encryption-algorithm", options.SourceEncryptionAlgorithm.Value()); } auto pRawResponse = pipeline.Send(request, context); auto httpStatusCode = pRawResponse->GetStatusCode(); diff --git a/sdk/storage/azure-storage-blobs/swagger/README.md b/sdk/storage/azure-storage-blobs/swagger/README.md index 1f5ff4044..dfac310b3 100644 --- a/sdk/storage/azure-storage-blobs/swagger/README.md +++ b/sdk/storage/azure-storage-blobs/swagger/README.md @@ -290,6 +290,8 @@ directive: $.SequenceNumberAction["x-ms-enum"]["name"] = "SequenceNumberAction"; delete $.EncryptionAlgorithm["enum"]; delete $.EncryptionAlgorithm["x-ms-enum"]; + delete $.SourceEncryptionAlgorithm["enum"]; + delete $.SourceEncryptionAlgorithm["x-ms-enum"]; $.ImmutabilityPolicyMode.enum = $.ImmutabilityPolicyMode.enum.map(e => e.toLowerCase()); $.CopySourceTags["x-ms-enum"]["name"] = "BlobCopySourceTagsMode"; delete $.FilterBlobsInclude; @@ -333,6 +335,16 @@ directive: }, "x-ms-export": true }; + $.SourceEncryptionAlgorithm = { + "type": "string", + "enum": ["AES256"], + "x-ms-enum": { + "name": "EncryptionAlgorithmType", + "modelAsString": false, + "values": [{"value": "__placeHolder", "name": "__placeHolder"}, {"value": "AES256", "name": "Aes256"}] + }, + "x-ms-export": true + }; $.BlockType = { "type": "string", "enum": ["Committed", "Uncommitted", "Latest"], @@ -391,6 +403,7 @@ directive: where: $.parameters transform: > $.EncryptionKeySha256["format"] = "byte"; + $.SourceEncryptionKeySha256["format"] = "byte"; $.BlobContentType["required"] = true; $.BlobContentEncoding["required"] = true; $.BlobContentLanguage["required"] = true; 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 81bd68907..2cb771a7a 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 @@ -1026,6 +1026,7 @@ namespace Azure { namespace Storage { namespace Test { blobSasBuilder.SetPermissions(Sas::BlobSasPermissions::All); + // cSpell:disable std::map requestHeaders; requestHeaders["x-ms-range"] = "bytes=0-1023"; requestHeaders["x-ms-range-get-content-md5"] = "true"; @@ -1057,6 +1058,7 @@ namespace Azure { namespace Storage { namespace Test { requestQueryParameters["hello$"] = "world!"; requestQueryParameters["abra"] = "cadabra"; requestQueryParameters["firstName"] = "john,Tim"; + // cSpell:enable blobSasBuilder.RequestHeaders = requestHeaders; blobSasBuilder.RequestQueryParameters = requestQueryParameters; 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 6f81961ae..48ca88e92 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 @@ -1022,6 +1022,7 @@ namespace Azure { namespace Storage { namespace Test { fileSasBuilder.SetPermissions(Sas::DataLakeSasPermissions::All); + // cSpell:disable std::map requestHeaders; requestHeaders["x-ms-range"] = "bytes=0-1023"; requestHeaders["x-ms-upn"] = "true"; @@ -1053,6 +1054,7 @@ namespace Azure { namespace Storage { namespace Test { requestQueryParameters["hello$"] = "world!"; requestQueryParameters["abra"] = "cadabra"; requestQueryParameters["firstName"] = "john,Tim"; + // cSpell:enable fileSasBuilder.RequestHeaders = requestHeaders; fileSasBuilder.RequestQueryParameters = requestQueryParameters;