diff --git a/sdk/storage/assets.json b/sdk/storage/assets.json index 03e24a0bf..4558eac5d 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_fc81ec148a" + "Tag": "cpp/storage_604485c00a" } diff --git a/sdk/storage/azure-storage-blobs/CHANGELOG.md b/sdk/storage/azure-storage-blobs/CHANGELOG.md index e8126f934..c25e5968c 100644 --- a/sdk/storage/azure-storage-blobs/CHANGELOG.md +++ b/sdk/storage/azure-storage-blobs/CHANGELOG.md @@ -4,11 +4,9 @@ ### Features Added -### Breaking Changes - -### Bugs Fixed - -### Other Changes +- Bumped up API version to `2024-08-04`. +- Added `BlobContainerClient::GetAccountInfo()` and `BlobClient::GetAccountInfo` APIs. +- Added more detailed messaging for authorization failure cases. ## 12.11.0 (2024-05-07) diff --git a/sdk/storage/azure-storage-blobs/inc/azure/storage/blobs/blob_client.hpp b/sdk/storage/azure-storage-blobs/inc/azure/storage/blobs/blob_client.hpp index ec13fc921..960d4564f 100644 --- a/sdk/storage/azure-storage-blobs/inc/azure/storage/blobs/blob_client.hpp +++ b/sdk/storage/azure-storage-blobs/inc/azure/storage/blobs/blob_client.hpp @@ -414,6 +414,17 @@ namespace Azure { namespace Storage { namespace Blobs { const SetBlobLegalHoldOptions& options = SetBlobLegalHoldOptions(), const Azure::Core::Context& context = Azure::Core::Context()) const; + /** + * @brief Returns the sku name and account kind for the specified account. + * + * @param options Optional parameters to execute this function. + * @param context Context for cancelling long running operations. + * @return AccountInfo describing the account. + */ + Azure::Response GetAccountInfo( + const GetAccountInfoOptions& options = GetAccountInfoOptions(), + const Azure::Core::Context& context = Azure::Core::Context()) const; + protected: /** @brief Blob Url */ Azure::Core::Url m_blobUrl; diff --git a/sdk/storage/azure-storage-blobs/inc/azure/storage/blobs/blob_container_client.hpp b/sdk/storage/azure-storage-blobs/inc/azure/storage/blobs/blob_container_client.hpp index fae4b017c..e5044fae6 100644 --- a/sdk/storage/azure-storage-blobs/inc/azure/storage/blobs/blob_container_client.hpp +++ b/sdk/storage/azure-storage-blobs/inc/azure/storage/blobs/blob_container_client.hpp @@ -326,6 +326,17 @@ namespace Azure { namespace Storage { namespace Blobs { const SubmitBlobBatchOptions& options = SubmitBlobBatchOptions(), const Core::Context& context = Core::Context()) const; + /** + * @brief Returns the sku name and account kind for the specified account. + * + * @param options Optional parameters to execute this function. + * @param context Context for cancelling long running operations. + * @return AccountInfo describing the account. + */ + Azure::Response GetAccountInfo( + const GetAccountInfoOptions& options = GetAccountInfoOptions(), + const Azure::Core::Context& context = Azure::Core::Context()) const; + private: Azure::Core::Url m_blobContainerUrl; std::shared_ptr m_pipeline; 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 a05421ee0..0278ca167 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 @@ -31,7 +31,7 @@ namespace Azure { namespace Storage { namespace Blobs { /** * The version used for the operations to Azure storage services. */ - constexpr static const char* ApiVersion = "2023-11-03"; + constexpr static const char* ApiVersion = "2024-08-04"; } // namespace _detail namespace Models { /** @@ -655,25 +655,6 @@ namespace Azure { namespace Storage { namespace Blobs { private: std::string m_value; }; - /** - * @brief Response type for #Azure::Storage::Blobs::BlobServiceClient::GetAccountInfo. - */ - struct AccountInfo final - { - /** - * Identifies the sku name of the account. - */ - Models::SkuName SkuName; - /** - * Identifies the account kind. - */ - Models::AccountKind AccountKind; - /** - * Version 2019-07-07 and newer. Indicates if the account has a hierarchical namespace - * enabled. - */ - bool IsHierarchicalNamespaceEnabled = bool(); - }; /** * @brief Blob info from a Filter Blobs API call. */ @@ -2400,6 +2381,25 @@ namespace Azure { namespace Storage { namespace Blobs { struct SetBlobAccessTierResult final { }; + /** + * @brief Response type for #Azure::Storage::Blobs::BlobServiceClient::GetAccountInfo. + */ + struct AccountInfo final + { + /** + * Identifies the sku name of the account. + */ + Models::SkuName SkuName; + /** + * Identifies the account kind. + */ + Models::AccountKind AccountKind; + /** + * Version 2019-07-07 and newer. Indicates if the account has a hierarchical namespace + * enabled. + */ + bool IsHierarchicalNamespaceEnabled = bool(); + }; namespace _detail { /** * @brief Required. The type of the provided query expression. @@ -3664,6 +3664,14 @@ namespace Azure { namespace Storage { namespace Blobs { const Core::Url& url, const ListBlobContainerBlobsByHierarchyOptions& options, const Core::Context& context); + struct GetBlobContainerAccountInfoOptions final + { + }; + static Response GetAccountInfo( + Core::Http::_internal::HttpPipeline& pipeline, + const Core::Url& url, + const GetBlobContainerAccountInfoOptions& options, + const Core::Context& context); }; class BlobClient final { public: @@ -3986,6 +3994,14 @@ namespace Azure { namespace Storage { namespace Blobs { const Core::Url& url, const SetBlobTierOptions& options, const Core::Context& context); + struct GetBlobAccountInfoOptions final + { + }; + static Response GetAccountInfo( + Core::Http::_internal::HttpPipeline& pipeline, + const Core::Url& url, + const GetBlobAccountInfoOptions& options, + const Core::Context& context); struct QueryBlobOptions final { Models::_detail::QueryRequest QueryRequest; diff --git a/sdk/storage/azure-storage-blobs/src/blob_client.cpp b/sdk/storage/azure-storage-blobs/src/blob_client.cpp index e7309d1f5..a96f2c98a 100644 --- a/sdk/storage/azure-storage-blobs/src/blob_client.cpp +++ b/sdk/storage/azure-storage-blobs/src/blob_client.cpp @@ -896,4 +896,14 @@ namespace Azure { namespace Storage { namespace Blobs { return _detail::BlobClient::SetLegalHold(*m_pipeline, m_blobUrl, protocolLayerOptions, context); } + Azure::Response BlobClient::GetAccountInfo( + const GetAccountInfoOptions& options, + const Azure::Core::Context& context) const + { + (void)options; + _detail::BlobClient::GetBlobAccountInfoOptions protocolLayerOptions; + return _detail::BlobClient::GetAccountInfo( + *m_pipeline, m_blobUrl, protocolLayerOptions, _internal::WithReplicaStatus(context)); + } + }}} // namespace Azure::Storage::Blobs diff --git a/sdk/storage/azure-storage-blobs/src/blob_container_client.cpp b/sdk/storage/azure-storage-blobs/src/blob_container_client.cpp index 9425f36e9..bd2f95916 100644 --- a/sdk/storage/azure-storage-blobs/src/blob_container_client.cpp +++ b/sdk/storage/azure-storage-blobs/src/blob_container_client.cpp @@ -516,4 +516,17 @@ namespace Azure { namespace Storage { namespace Blobs { return Azure::Response( Models::SubmitBlobBatchResult(), std::move(response.RawResponse)); } + + Azure::Response BlobContainerClient::GetAccountInfo( + const GetAccountInfoOptions& options, + const Azure::Core::Context& context) const + { + (void)options; + _detail::BlobContainerClient::GetBlobContainerAccountInfoOptions protocolLayerOptions; + return _detail::BlobContainerClient::GetAccountInfo( + *m_pipeline, + m_blobContainerUrl, + protocolLayerOptions, + _internal::WithReplicaStatus(context)); + } }}} // namespace Azure::Storage::Blobs diff --git a/sdk/storage/azure-storage-blobs/src/rest_client.cpp b/sdk/storage/azure-storage-blobs/src/rest_client.cpp index 4fe06009b..f42493bd9 100644 --- a/sdk/storage/azure-storage-blobs/src/rest_client.cpp +++ b/sdk/storage/azure-storage-blobs/src/rest_client.cpp @@ -377,7 +377,7 @@ namespace Azure { namespace Storage { namespace Blobs { request.SetHeader("Content-Length", std::to_string(requestBody.Length())); request.GetUrl().AppendQueryParameter("restype", "service"); request.GetUrl().AppendQueryParameter("comp", "properties"); - request.SetHeader("x-ms-version", "2023-11-03"); + request.SetHeader("x-ms-version", "2024-08-04"); auto pRawResponse = pipeline.Send(request, context); auto httpStatusCode = pRawResponse->GetStatusCode(); if (httpStatusCode != Core::Http::HttpStatusCode::Accepted) @@ -397,7 +397,7 @@ namespace Azure { namespace Storage { namespace Blobs { auto request = Core::Http::Request(Core::Http::HttpMethod::Get, url); request.GetUrl().AppendQueryParameter("restype", "service"); request.GetUrl().AppendQueryParameter("comp", "properties"); - request.SetHeader("x-ms-version", "2023-11-03"); + request.SetHeader("x-ms-version", "2024-08-04"); (void)options; auto pRawResponse = pipeline.Send(request, context); auto httpStatusCode = pRawResponse->GetStatusCode(); @@ -693,7 +693,7 @@ namespace Azure { namespace Storage { namespace Blobs { auto request = Core::Http::Request(Core::Http::HttpMethod::Get, url); request.GetUrl().AppendQueryParameter("restype", "service"); request.GetUrl().AppendQueryParameter("comp", "stats"); - request.SetHeader("x-ms-version", "2023-11-03"); + request.SetHeader("x-ms-version", "2024-08-04"); (void)options; auto pRawResponse = pipeline.Send(request, context); auto httpStatusCode = pRawResponse->GetStatusCode(); @@ -793,7 +793,7 @@ namespace Azure { namespace Storage { namespace Blobs { _internal::UrlEncodeQueryParameter( ListBlobContainersIncludeFlagsToString(options.Include.Value()))); } - request.SetHeader("x-ms-version", "2023-11-03"); + request.SetHeader("x-ms-version", "2024-08-04"); auto pRawResponse = pipeline.Send(request, context); auto httpStatusCode = pRawResponse->GetStatusCode(); if (httpStatusCode != Core::Http::HttpStatusCode::Ok) @@ -1078,7 +1078,7 @@ namespace Azure { namespace Storage { namespace Blobs { request.SetHeader("Content-Length", std::to_string(requestBody.Length())); request.GetUrl().AppendQueryParameter("restype", "service"); request.GetUrl().AppendQueryParameter("comp", "userdelegationkey"); - request.SetHeader("x-ms-version", "2023-11-03"); + request.SetHeader("x-ms-version", "2024-08-04"); auto pRawResponse = pipeline.Send(request, context); auto httpStatusCode = pRawResponse->GetStatusCode(); if (httpStatusCode != Core::Http::HttpStatusCode::Ok) @@ -1193,7 +1193,7 @@ namespace Azure { namespace Storage { namespace Blobs { auto request = Core::Http::Request(Core::Http::HttpMethod::Get, url); request.GetUrl().AppendQueryParameter("restype", "account"); request.GetUrl().AppendQueryParameter("comp", "properties"); - request.SetHeader("x-ms-version", "2023-11-03"); + request.SetHeader("x-ms-version", "2024-08-04"); (void)options; auto pRawResponse = pipeline.Send(request, context); auto httpStatusCode = pRawResponse->GetStatusCode(); @@ -1223,7 +1223,7 @@ namespace Azure { namespace Storage { namespace Blobs { { request.SetHeader("Content-Type", options.MultipartContentType); } - request.SetHeader("x-ms-version", "2023-11-03"); + request.SetHeader("x-ms-version", "2024-08-04"); auto pRawResponse = pipeline.Send(request, context); auto httpStatusCode = pRawResponse->GetStatusCode(); if (httpStatusCode != Core::Http::HttpStatusCode::Accepted) @@ -1244,7 +1244,7 @@ namespace Azure { namespace Storage { namespace Blobs { { auto request = Core::Http::Request(Core::Http::HttpMethod::Get, url); request.GetUrl().AppendQueryParameter("comp", "blobs"); - request.SetHeader("x-ms-version", "2023-11-03"); + request.SetHeader("x-ms-version", "2024-08-04"); if (options.Where.HasValue() && !options.Where.Value().empty()) { request.GetUrl().AppendQueryParameter( @@ -1400,7 +1400,7 @@ namespace Azure { namespace Storage { namespace Blobs { { request.SetHeader("x-ms-blob-public-access", options.Access.ToString()); } - request.SetHeader("x-ms-version", "2023-11-03"); + request.SetHeader("x-ms-version", "2024-08-04"); if (options.DefaultEncryptionScope.HasValue() && !options.DefaultEncryptionScope.Value().empty()) { @@ -1437,7 +1437,7 @@ namespace Azure { namespace Storage { namespace Blobs { { request.SetHeader("x-ms-lease-id", options.LeaseId.Value()); } - request.SetHeader("x-ms-version", "2023-11-03"); + request.SetHeader("x-ms-version", "2024-08-04"); auto pRawResponse = pipeline.Send(request, context); auto httpStatusCode = pRawResponse->GetStatusCode(); if (httpStatusCode != Core::Http::HttpStatusCode::Ok) @@ -1515,7 +1515,7 @@ namespace Azure { namespace Storage { namespace Blobs { "If-Unmodified-Since", options.IfUnmodifiedSince.Value().ToString(Azure::DateTime::DateFormat::Rfc1123)); } - request.SetHeader("x-ms-version", "2023-11-03"); + request.SetHeader("x-ms-version", "2024-08-04"); auto pRawResponse = pipeline.Send(request, context); auto httpStatusCode = pRawResponse->GetStatusCode(); if (httpStatusCode != Core::Http::HttpStatusCode::Accepted) @@ -1549,7 +1549,7 @@ namespace Azure { namespace Storage { namespace Blobs { "If-Modified-Since", options.IfModifiedSince.Value().ToString(Azure::DateTime::DateFormat::Rfc1123)); } - request.SetHeader("x-ms-version", "2023-11-03"); + request.SetHeader("x-ms-version", "2024-08-04"); auto pRawResponse = pipeline.Send(request, context); auto httpStatusCode = pRawResponse->GetStatusCode(); if (httpStatusCode != Core::Http::HttpStatusCode::Ok) @@ -1576,7 +1576,7 @@ namespace Azure { namespace Storage { namespace Blobs { { request.SetHeader("x-ms-lease-id", options.LeaseId.Value()); } - request.SetHeader("x-ms-version", "2023-11-03"); + request.SetHeader("x-ms-version", "2024-08-04"); auto pRawResponse = pipeline.Send(request, context); auto httpStatusCode = pRawResponse->GetStatusCode(); if (httpStatusCode != Core::Http::HttpStatusCode::Ok) @@ -1745,7 +1745,7 @@ namespace Azure { namespace Storage { namespace Blobs { "If-Unmodified-Since", options.IfUnmodifiedSince.Value().ToString(Azure::DateTime::DateFormat::Rfc1123)); } - request.SetHeader("x-ms-version", "2023-11-03"); + request.SetHeader("x-ms-version", "2024-08-04"); auto pRawResponse = pipeline.Send(request, context); auto httpStatusCode = pRawResponse->GetStatusCode(); if (httpStatusCode != Core::Http::HttpStatusCode::Ok) @@ -1768,7 +1768,7 @@ namespace Azure { namespace Storage { namespace Blobs { auto request = Core::Http::Request(Core::Http::HttpMethod::Put, url); request.GetUrl().AppendQueryParameter("restype", "container"); request.GetUrl().AppendQueryParameter("comp", "undelete"); - request.SetHeader("x-ms-version", "2023-11-03"); + request.SetHeader("x-ms-version", "2024-08-04"); if (options.DeletedContainerName.HasValue() && !options.DeletedContainerName.Value().empty()) { request.SetHeader("x-ms-deleted-container-name", options.DeletedContainerName.Value()); @@ -1798,7 +1798,7 @@ namespace Azure { namespace Storage { namespace Blobs { auto request = Core::Http::Request(Core::Http::HttpMethod::Put, url); request.GetUrl().AppendQueryParameter("restype", "container"); request.GetUrl().AppendQueryParameter("comp", "rename"); - request.SetHeader("x-ms-version", "2023-11-03"); + request.SetHeader("x-ms-version", "2024-08-04"); if (!options.SourceContainerName.empty()) { request.SetHeader("x-ms-source-container-name", options.SourceContainerName); @@ -1832,7 +1832,7 @@ namespace Azure { namespace Storage { namespace Blobs { { request.SetHeader("Content-Type", options.MultipartContentType); } - request.SetHeader("x-ms-version", "2023-11-03"); + request.SetHeader("x-ms-version", "2024-08-04"); auto pRawResponse = pipeline.Send(request, context); auto httpStatusCode = pRawResponse->GetStatusCode(); if (httpStatusCode != Core::Http::HttpStatusCode::Accepted) @@ -1854,7 +1854,7 @@ namespace Azure { namespace Storage { namespace Blobs { auto request = Core::Http::Request(Core::Http::HttpMethod::Get, url); request.GetUrl().AppendQueryParameter("restype", "container"); request.GetUrl().AppendQueryParameter("comp", "blobs"); - request.SetHeader("x-ms-version", "2023-11-03"); + request.SetHeader("x-ms-version", "2024-08-04"); if (options.Where.HasValue() && !options.Where.Value().empty()) { request.GetUrl().AppendQueryParameter( @@ -2024,7 +2024,7 @@ namespace Azure { namespace Storage { namespace Blobs { "If-Unmodified-Since", options.IfUnmodifiedSince.Value().ToString(Azure::DateTime::DateFormat::Rfc1123)); } - request.SetHeader("x-ms-version", "2023-11-03"); + request.SetHeader("x-ms-version", "2024-08-04"); auto pRawResponse = pipeline.Send(request, context); auto httpStatusCode = pRawResponse->GetStatusCode(); if (httpStatusCode != Core::Http::HttpStatusCode::Created) @@ -2065,7 +2065,7 @@ namespace Azure { namespace Storage { namespace Blobs { "If-Unmodified-Since", options.IfUnmodifiedSince.Value().ToString(Azure::DateTime::DateFormat::Rfc1123)); } - request.SetHeader("x-ms-version", "2023-11-03"); + request.SetHeader("x-ms-version", "2024-08-04"); auto pRawResponse = pipeline.Send(request, context); auto httpStatusCode = pRawResponse->GetStatusCode(); if (httpStatusCode != Core::Http::HttpStatusCode::Ok) @@ -2105,7 +2105,7 @@ namespace Azure { namespace Storage { namespace Blobs { "If-Unmodified-Since", options.IfUnmodifiedSince.Value().ToString(Azure::DateTime::DateFormat::Rfc1123)); } - request.SetHeader("x-ms-version", "2023-11-03"); + request.SetHeader("x-ms-version", "2024-08-04"); auto pRawResponse = pipeline.Send(request, context); auto httpStatusCode = pRawResponse->GetStatusCode(); if (httpStatusCode != Core::Http::HttpStatusCode::Ok) @@ -2146,7 +2146,7 @@ namespace Azure { namespace Storage { namespace Blobs { "If-Unmodified-Since", options.IfUnmodifiedSince.Value().ToString(Azure::DateTime::DateFormat::Rfc1123)); } - request.SetHeader("x-ms-version", "2023-11-03"); + request.SetHeader("x-ms-version", "2024-08-04"); auto pRawResponse = pipeline.Send(request, context); auto httpStatusCode = pRawResponse->GetStatusCode(); if (httpStatusCode != Core::Http::HttpStatusCode::Accepted) @@ -2191,7 +2191,7 @@ namespace Azure { namespace Storage { namespace Blobs { "If-Unmodified-Since", options.IfUnmodifiedSince.Value().ToString(Azure::DateTime::DateFormat::Rfc1123)); } - request.SetHeader("x-ms-version", "2023-11-03"); + request.SetHeader("x-ms-version", "2024-08-04"); auto pRawResponse = pipeline.Send(request, context); auto httpStatusCode = pRawResponse->GetStatusCode(); if (httpStatusCode != Core::Http::HttpStatusCode::Ok) @@ -2238,7 +2238,7 @@ namespace Azure { namespace Storage { namespace Blobs { _internal::UrlEncodeQueryParameter( ListBlobsIncludeFlagsToString(options.Include.Value()))); } - request.SetHeader("x-ms-version", "2023-11-03"); + request.SetHeader("x-ms-version", "2024-08-04"); auto pRawResponse = pipeline.Send(request, context); auto httpStatusCode = pRawResponse->GetStatusCode(); if (httpStatusCode != Core::Http::HttpStatusCode::Ok) @@ -2917,7 +2917,7 @@ namespace Azure { namespace Storage { namespace Blobs { _internal::UrlEncodeQueryParameter( ListBlobsIncludeFlagsToString(options.Include.Value()))); } - request.SetHeader("x-ms-version", "2023-11-03"); + request.SetHeader("x-ms-version", "2024-08-04"); if (options.ShowOnly.HasValue() && !options.ShowOnly.Value().empty()) { request.GetUrl().AppendQueryParameter( @@ -3597,6 +3597,31 @@ namespace Azure { namespace Storage { namespace Blobs { return Response( std::move(response), std::move(pRawResponse)); } + Response BlobContainerClient::GetAccountInfo( + Core::Http::_internal::HttpPipeline& pipeline, + const Core::Url& url, + const GetBlobContainerAccountInfoOptions& options, + const Core::Context& context) + { + auto request = Core::Http::Request(Core::Http::HttpMethod::Get, url); + request.GetUrl().AppendQueryParameter("restype", "account"); + request.GetUrl().AppendQueryParameter("comp", "properties"); + request.SetHeader("x-ms-version", "2024-08-04"); + (void)options; + auto pRawResponse = pipeline.Send(request, context); + auto httpStatusCode = pRawResponse->GetStatusCode(); + if (httpStatusCode != Core::Http::HttpStatusCode::Ok) + { + throw StorageException::CreateFromResponse(std::move(pRawResponse)); + } + Models::AccountInfo response; + response.SkuName = Models::SkuName(pRawResponse->GetHeaders().at("x-ms-sku-name")); + response.AccountKind + = Models::AccountKind(pRawResponse->GetHeaders().at("x-ms-account-kind")); + response.IsHierarchicalNamespaceEnabled + = pRawResponse->GetHeaders().at("x-ms-is-hns-enabled") == std::string("true"); + return Response(std::move(response), std::move(pRawResponse)); + } Response BlobClient::Download( Core::Http::_internal::HttpPipeline& pipeline, const Core::Url& url, @@ -3672,7 +3697,7 @@ namespace Azure { namespace Storage { namespace Blobs { { request.SetHeader("x-ms-if-tags", options.IfTags.Value()); } - request.SetHeader("x-ms-version", "2023-11-03"); + request.SetHeader("x-ms-version", "2024-08-04"); if (options.UserPrincipalName.HasValue()) { request.SetHeader("x-ms-upn", options.UserPrincipalName.Value() ? "true" : "false"); @@ -3952,7 +3977,7 @@ namespace Azure { namespace Storage { namespace Blobs { { request.SetHeader("x-ms-if-tags", options.IfTags.Value()); } - request.SetHeader("x-ms-version", "2023-11-03"); + request.SetHeader("x-ms-version", "2024-08-04"); if (options.UserPrincipalName.HasValue()) { request.SetHeader("x-ms-upn", options.UserPrincipalName.Value() ? "true" : "false"); @@ -4218,7 +4243,7 @@ namespace Azure { namespace Storage { namespace Blobs { { request.SetHeader("x-ms-if-tags", options.IfTags.Value()); } - request.SetHeader("x-ms-version", "2023-11-03"); + request.SetHeader("x-ms-version", "2024-08-04"); auto pRawResponse = pipeline.Send(request, context); auto httpStatusCode = pRawResponse->GetStatusCode(); if (httpStatusCode != Core::Http::HttpStatusCode::Accepted) @@ -4236,7 +4261,7 @@ namespace Azure { namespace Storage { namespace Blobs { { auto request = Core::Http::Request(Core::Http::HttpMethod::Put, url); request.GetUrl().AppendQueryParameter("comp", "undelete"); - request.SetHeader("x-ms-version", "2023-11-03"); + request.SetHeader("x-ms-version", "2024-08-04"); (void)options; auto pRawResponse = pipeline.Send(request, context); auto httpStatusCode = pRawResponse->GetStatusCode(); @@ -4255,7 +4280,7 @@ namespace Azure { namespace Storage { namespace Blobs { { auto request = Core::Http::Request(Core::Http::HttpMethod::Put, url); request.GetUrl().AppendQueryParameter("comp", "expiry"); - request.SetHeader("x-ms-version", "2023-11-03"); + request.SetHeader("x-ms-version", "2024-08-04"); if (!options.ExpiryOptions.ToString().empty()) { request.SetHeader("x-ms-expiry-option", options.ExpiryOptions.ToString()); @@ -4343,7 +4368,7 @@ namespace Azure { namespace Storage { namespace Blobs { { request.SetHeader("x-ms-blob-content-disposition", options.BlobContentDisposition); } - request.SetHeader("x-ms-version", "2023-11-03"); + request.SetHeader("x-ms-version", "2024-08-04"); auto pRawResponse = pipeline.Send(request, context); auto httpStatusCode = pRawResponse->GetStatusCode(); if (httpStatusCode != Core::Http::HttpStatusCode::Ok) @@ -4376,7 +4401,7 @@ namespace Azure { namespace Storage { namespace Blobs { { auto request = Core::Http::Request(Core::Http::HttpMethod::Put, url); request.GetUrl().AppendQueryParameter("comp", "immutabilityPolicies"); - request.SetHeader("x-ms-version", "2023-11-03"); + request.SetHeader("x-ms-version", "2024-08-04"); if (options.IfUnmodifiedSince.HasValue()) { request.SetHeader( @@ -4419,7 +4444,7 @@ namespace Azure { namespace Storage { namespace Blobs { { auto request = Core::Http::Request(Core::Http::HttpMethod::Delete, url); request.GetUrl().AppendQueryParameter("comp", "immutabilityPolicies"); - request.SetHeader("x-ms-version", "2023-11-03"); + request.SetHeader("x-ms-version", "2024-08-04"); (void)options; auto pRawResponse = pipeline.Send(request, context); auto httpStatusCode = pRawResponse->GetStatusCode(); @@ -4439,7 +4464,7 @@ namespace Azure { namespace Storage { namespace Blobs { { auto request = Core::Http::Request(Core::Http::HttpMethod::Put, url); request.GetUrl().AppendQueryParameter("comp", "legalhold"); - request.SetHeader("x-ms-version", "2023-11-03"); + request.SetHeader("x-ms-version", "2024-08-04"); request.SetHeader("x-ms-legal-hold", options.LegalHold ? "true" : "false"); auto pRawResponse = pipeline.Send(request, context); auto httpStatusCode = pRawResponse->GetStatusCode(); @@ -4511,7 +4536,7 @@ namespace Azure { namespace Storage { namespace Blobs { { request.SetHeader("x-ms-if-tags", options.IfTags.Value()); } - request.SetHeader("x-ms-version", "2023-11-03"); + request.SetHeader("x-ms-version", "2024-08-04"); auto pRawResponse = pipeline.Send(request, context); auto httpStatusCode = pRawResponse->GetStatusCode(); if (httpStatusCode != Core::Http::HttpStatusCode::Ok) @@ -4586,7 +4611,7 @@ namespace Azure { namespace Storage { namespace Blobs { { request.SetHeader("x-ms-if-tags", options.IfTags.Value()); } - request.SetHeader("x-ms-version", "2023-11-03"); + request.SetHeader("x-ms-version", "2024-08-04"); auto pRawResponse = pipeline.Send(request, context); auto httpStatusCode = pRawResponse->GetStatusCode(); if (httpStatusCode != Core::Http::HttpStatusCode::Created) @@ -4644,7 +4669,7 @@ namespace Azure { namespace Storage { namespace Blobs { { request.SetHeader("x-ms-if-tags", options.IfTags.Value()); } - request.SetHeader("x-ms-version", "2023-11-03"); + request.SetHeader("x-ms-version", "2024-08-04"); auto pRawResponse = pipeline.Send(request, context); auto httpStatusCode = pRawResponse->GetStatusCode(); if (httpStatusCode != Core::Http::HttpStatusCode::Ok) @@ -4701,7 +4726,7 @@ namespace Azure { namespace Storage { namespace Blobs { { request.SetHeader("x-ms-if-tags", options.IfTags.Value()); } - request.SetHeader("x-ms-version", "2023-11-03"); + request.SetHeader("x-ms-version", "2024-08-04"); auto pRawResponse = pipeline.Send(request, context); auto httpStatusCode = pRawResponse->GetStatusCode(); if (httpStatusCode != Core::Http::HttpStatusCode::Ok) @@ -4763,7 +4788,7 @@ namespace Azure { namespace Storage { namespace Blobs { { request.SetHeader("x-ms-if-tags", options.IfTags.Value()); } - request.SetHeader("x-ms-version", "2023-11-03"); + request.SetHeader("x-ms-version", "2024-08-04"); auto pRawResponse = pipeline.Send(request, context); auto httpStatusCode = pRawResponse->GetStatusCode(); if (httpStatusCode != Core::Http::HttpStatusCode::Ok) @@ -4821,7 +4846,7 @@ namespace Azure { namespace Storage { namespace Blobs { { request.SetHeader("x-ms-if-tags", options.IfTags.Value()); } - request.SetHeader("x-ms-version", "2023-11-03"); + request.SetHeader("x-ms-version", "2024-08-04"); auto pRawResponse = pipeline.Send(request, context); auto httpStatusCode = pRawResponse->GetStatusCode(); if (httpStatusCode != Core::Http::HttpStatusCode::Accepted) @@ -4901,7 +4926,7 @@ namespace Azure { namespace Storage { namespace Blobs { { request.SetHeader("x-ms-lease-id", options.LeaseId.Value()); } - request.SetHeader("x-ms-version", "2023-11-03"); + request.SetHeader("x-ms-version", "2024-08-04"); auto pRawResponse = pipeline.Send(request, context); auto httpStatusCode = pRawResponse->GetStatusCode(); if (httpStatusCode != Core::Http::HttpStatusCode::Created) @@ -5008,7 +5033,7 @@ namespace Azure { namespace Storage { namespace Blobs { { request.SetHeader("x-ms-lease-id", options.LeaseId.Value()); } - request.SetHeader("x-ms-version", "2023-11-03"); + request.SetHeader("x-ms-version", "2024-08-04"); if (options.BlobTagsString.HasValue() && !options.BlobTagsString.Value().empty()) { request.SetHeader("x-ms-tags", options.BlobTagsString.Value()); @@ -5127,7 +5152,7 @@ namespace Azure { namespace Storage { namespace Blobs { { request.SetHeader("x-ms-lease-id", options.LeaseId.Value()); } - request.SetHeader("x-ms-version", "2023-11-03"); + request.SetHeader("x-ms-version", "2024-08-04"); if (options.SourceContentMD5.HasValue() && !Core::Convert::Base64Encode(options.SourceContentMD5.Value()).empty()) { @@ -5237,7 +5262,7 @@ namespace Azure { namespace Storage { namespace Blobs { { request.SetHeader("x-ms-lease-id", options.LeaseId.Value()); } - request.SetHeader("x-ms-version", "2023-11-03"); + request.SetHeader("x-ms-version", "2024-08-04"); auto pRawResponse = pipeline.Send(request, context); auto httpStatusCode = pRawResponse->GetStatusCode(); if (httpStatusCode != Core::Http::HttpStatusCode::NoContent) @@ -5275,7 +5300,7 @@ namespace Azure { namespace Storage { namespace Blobs { { request.SetHeader("x-ms-rehydrate-priority", options.RehydratePriority.Value().ToString()); } - request.SetHeader("x-ms-version", "2023-11-03"); + request.SetHeader("x-ms-version", "2024-08-04"); if (options.LeaseId.HasValue() && !options.LeaseId.Value().empty()) { request.SetHeader("x-ms-lease-id", options.LeaseId.Value()); @@ -5295,6 +5320,31 @@ namespace Azure { namespace Storage { namespace Blobs { return Response( std::move(response), std::move(pRawResponse)); } + Response BlobClient::GetAccountInfo( + Core::Http::_internal::HttpPipeline& pipeline, + const Core::Url& url, + const GetBlobAccountInfoOptions& options, + const Core::Context& context) + { + auto request = Core::Http::Request(Core::Http::HttpMethod::Get, url); + request.GetUrl().AppendQueryParameter("restype", "account"); + request.GetUrl().AppendQueryParameter("comp", "properties"); + request.SetHeader("x-ms-version", "2024-08-04"); + (void)options; + auto pRawResponse = pipeline.Send(request, context); + auto httpStatusCode = pRawResponse->GetStatusCode(); + if (httpStatusCode != Core::Http::HttpStatusCode::Ok) + { + throw StorageException::CreateFromResponse(std::move(pRawResponse)); + } + Models::AccountInfo response; + response.SkuName = Models::SkuName(pRawResponse->GetHeaders().at("x-ms-sku-name")); + response.AccountKind + = Models::AccountKind(pRawResponse->GetHeaders().at("x-ms-account-kind")); + response.IsHierarchicalNamespaceEnabled + = pRawResponse->GetHeaders().at("x-ms-is-hns-enabled") == std::string("true"); + return Response(std::move(response), std::move(pRawResponse)); + } Response BlobClient::Query( Core::Http::_internal::HttpPipeline& pipeline, const Core::Url& url, @@ -5577,7 +5627,7 @@ namespace Azure { namespace Storage { namespace Blobs { { request.SetHeader("x-ms-if-tags", options.IfTags.Value()); } - request.SetHeader("x-ms-version", "2023-11-03"); + request.SetHeader("x-ms-version", "2024-08-04"); if (options.EncryptionScope.HasValue() && !options.EncryptionScope.Value().empty()) { request.SetHeader("x-ms-encryption-scope", options.EncryptionScope.Value()); @@ -5626,7 +5676,7 @@ namespace Azure { namespace Storage { namespace Blobs { { auto request = Core::Http::Request(Core::Http::HttpMethod::Get, url); request.GetUrl().AppendQueryParameter("comp", "tags"); - request.SetHeader("x-ms-version", "2023-11-03"); + request.SetHeader("x-ms-version", "2024-08-04"); if (options.Snapshot.HasValue() && !options.Snapshot.Value().empty()) { request.GetUrl().AppendQueryParameter( @@ -5753,7 +5803,7 @@ namespace Azure { namespace Storage { namespace Blobs { request.SetHeader("Content-Type", "application/xml; charset=UTF-8"); request.SetHeader("Content-Length", std::to_string(requestBody.Length())); request.GetUrl().AppendQueryParameter("comp", "tags"); - request.SetHeader("x-ms-version", "2023-11-03"); + request.SetHeader("x-ms-version", "2024-08-04"); if (options.VersionId.HasValue() && !options.VersionId.Value().empty()) { request.GetUrl().AppendQueryParameter( @@ -5884,7 +5934,7 @@ namespace Azure { namespace Storage { namespace Blobs { request.SetHeader( "x-ms-blob-sequence-number", std::to_string(options.BlobSequenceNumber.Value())); } - request.SetHeader("x-ms-version", "2023-11-03"); + request.SetHeader("x-ms-version", "2024-08-04"); if (options.BlobTagsString.HasValue() && !options.BlobTagsString.Value().empty()) { request.SetHeader("x-ms-tags", options.BlobTagsString.Value()); @@ -6030,7 +6080,7 @@ namespace Azure { namespace Storage { namespace Blobs { { request.SetHeader("x-ms-if-tags", options.IfTags.Value()); } - request.SetHeader("x-ms-version", "2023-11-03"); + request.SetHeader("x-ms-version", "2024-08-04"); auto pRawResponse = pipeline.Send(request, context); auto httpStatusCode = pRawResponse->GetStatusCode(); if (httpStatusCode != Core::Http::HttpStatusCode::Created) @@ -6156,7 +6206,7 @@ namespace Azure { namespace Storage { namespace Blobs { { request.SetHeader("x-ms-if-tags", options.IfTags.Value()); } - request.SetHeader("x-ms-version", "2023-11-03"); + request.SetHeader("x-ms-version", "2024-08-04"); auto pRawResponse = pipeline.Send(request, context); auto httpStatusCode = pRawResponse->GetStatusCode(); if (httpStatusCode != Core::Http::HttpStatusCode::Created) @@ -6299,7 +6349,7 @@ namespace Azure { namespace Storage { namespace Blobs { { request.SetHeader("x-ms-source-if-none-match", options.SourceIfNoneMatch.ToString()); } - request.SetHeader("x-ms-version", "2023-11-03"); + request.SetHeader("x-ms-version", "2024-08-04"); if (options.CopySourceAuthorization.HasValue() && !options.CopySourceAuthorization.Value().empty()) { @@ -6400,7 +6450,7 @@ namespace Azure { namespace Storage { namespace Blobs { { request.SetHeader("x-ms-if-tags", options.IfTags.Value()); } - request.SetHeader("x-ms-version", "2023-11-03"); + request.SetHeader("x-ms-version", "2024-08-04"); if (options.Marker.HasValue() && !options.Marker.Value().empty()) { request.GetUrl().AppendQueryParameter( @@ -6578,7 +6628,7 @@ namespace Azure { namespace Storage { namespace Blobs { { request.SetHeader("x-ms-if-tags", options.IfTags.Value()); } - request.SetHeader("x-ms-version", "2023-11-03"); + request.SetHeader("x-ms-version", "2024-08-04"); if (options.Marker.HasValue() && !options.Marker.Value().empty()) { request.GetUrl().AppendQueryParameter( @@ -6758,7 +6808,7 @@ namespace Azure { namespace Storage { namespace Blobs { request.SetHeader("x-ms-if-tags", options.IfTags.Value()); } request.SetHeader("x-ms-blob-content-length", std::to_string(options.BlobContentLength)); - request.SetHeader("x-ms-version", "2023-11-03"); + request.SetHeader("x-ms-version", "2024-08-04"); auto pRawResponse = pipeline.Send(request, context); auto httpStatusCode = pRawResponse->GetStatusCode(); if (httpStatusCode != Core::Http::HttpStatusCode::Ok) @@ -6824,7 +6874,7 @@ namespace Azure { namespace Storage { namespace Blobs { request.SetHeader( "x-ms-blob-sequence-number", std::to_string(options.BlobSequenceNumber.Value())); } - request.SetHeader("x-ms-version", "2023-11-03"); + request.SetHeader("x-ms-version", "2024-08-04"); auto pRawResponse = pipeline.Send(request, context); auto httpStatusCode = pRawResponse->GetStatusCode(); if (httpStatusCode != Core::Http::HttpStatusCode::Ok) @@ -6882,7 +6932,7 @@ namespace Azure { namespace Storage { namespace Blobs { { request.SetHeader("x-ms-copy-source", options.CopySource); } - request.SetHeader("x-ms-version", "2023-11-03"); + request.SetHeader("x-ms-version", "2024-08-04"); auto pRawResponse = pipeline.Send(request, context); auto httpStatusCode = pRawResponse->GetStatusCode(); if (httpStatusCode != Core::Http::HttpStatusCode::Accepted) @@ -6993,7 +7043,7 @@ namespace Azure { namespace Storage { namespace Blobs { { request.SetHeader("x-ms-if-tags", options.IfTags.Value()); } - request.SetHeader("x-ms-version", "2023-11-03"); + request.SetHeader("x-ms-version", "2024-08-04"); if (options.BlobTagsString.HasValue() && !options.BlobTagsString.Value().empty()) { request.SetHeader("x-ms-tags", options.BlobTagsString.Value()); @@ -7121,7 +7171,7 @@ namespace Azure { namespace Storage { namespace Blobs { { request.SetHeader("x-ms-if-tags", options.IfTags.Value()); } - request.SetHeader("x-ms-version", "2023-11-03"); + request.SetHeader("x-ms-version", "2024-08-04"); auto pRawResponse = pipeline.Send(request, context); auto httpStatusCode = pRawResponse->GetStatusCode(); if (httpStatusCode != Core::Http::HttpStatusCode::Created) @@ -7275,7 +7325,7 @@ namespace Azure { namespace Storage { namespace Blobs { { request.SetHeader("x-ms-source-if-none-match", options.SourceIfNoneMatch.ToString()); } - request.SetHeader("x-ms-version", "2023-11-03"); + request.SetHeader("x-ms-version", "2024-08-04"); if (options.CopySourceAuthorization.HasValue() && !options.CopySourceAuthorization.Value().empty()) { @@ -7331,7 +7381,7 @@ namespace Azure { namespace Storage { namespace Blobs { { auto request = Core::Http::Request(Core::Http::HttpMethod::Put, url); request.GetUrl().AppendQueryParameter("comp", "seal"); - request.SetHeader("x-ms-version", "2023-11-03"); + request.SetHeader("x-ms-version", "2024-08-04"); if (options.LeaseId.HasValue() && !options.LeaseId.Value().empty()) { request.SetHeader("x-ms-lease-id", options.LeaseId.Value()); @@ -7470,7 +7520,7 @@ namespace Azure { namespace Storage { namespace Blobs { { request.SetHeader("x-ms-if-tags", options.IfTags.Value()); } - request.SetHeader("x-ms-version", "2023-11-03"); + request.SetHeader("x-ms-version", "2024-08-04"); if (options.BlobTagsString.HasValue() && !options.BlobTagsString.Value().empty()) { request.SetHeader("x-ms-tags", options.BlobTagsString.Value()); @@ -7653,7 +7703,7 @@ namespace Azure { namespace Storage { namespace Blobs { { request.SetHeader("x-ms-source-if-tags", options.SourceIfTags.Value()); } - request.SetHeader("x-ms-version", "2023-11-03"); + request.SetHeader("x-ms-version", "2024-08-04"); if (options.SourceContentMD5.HasValue() && !Core::Convert::Base64Encode(options.SourceContentMD5.Value()).empty()) { @@ -7785,7 +7835,7 @@ namespace Azure { namespace Storage { namespace Blobs { { request.SetHeader("x-ms-encryption-scope", options.EncryptionScope.Value()); } - request.SetHeader("x-ms-version", "2023-11-03"); + request.SetHeader("x-ms-version", "2024-08-04"); auto pRawResponse = pipeline.Send(request, context); auto httpStatusCode = pRawResponse->GetStatusCode(); if (httpStatusCode != Core::Http::HttpStatusCode::Created) @@ -7899,7 +7949,7 @@ namespace Azure { namespace Storage { namespace Blobs { { request.SetHeader("x-ms-source-if-none-match", options.SourceIfNoneMatch.ToString()); } - request.SetHeader("x-ms-version", "2023-11-03"); + request.SetHeader("x-ms-version", "2024-08-04"); if (options.CopySourceAuthorization.HasValue() && !options.CopySourceAuthorization.Value().empty()) { @@ -8066,7 +8116,7 @@ namespace Azure { namespace Storage { namespace Blobs { { request.SetHeader("x-ms-if-tags", options.IfTags.Value()); } - request.SetHeader("x-ms-version", "2023-11-03"); + request.SetHeader("x-ms-version", "2024-08-04"); if (options.BlobTagsString.HasValue() && !options.BlobTagsString.Value().empty()) { request.SetHeader("x-ms-tags", options.BlobTagsString.Value()); @@ -8155,7 +8205,7 @@ namespace Azure { namespace Storage { namespace Blobs { { request.SetHeader("x-ms-if-tags", options.IfTags.Value()); } - request.SetHeader("x-ms-version", "2023-11-03"); + request.SetHeader("x-ms-version", "2024-08-04"); auto pRawResponse = pipeline.Send(request, context); auto httpStatusCode = pRawResponse->GetStatusCode(); if (httpStatusCode != Core::Http::HttpStatusCode::Ok) diff --git a/sdk/storage/azure-storage-blobs/swagger/README.md b/sdk/storage/azure-storage-blobs/swagger/README.md index b60766023..662835fa8 100644 --- a/sdk/storage/azure-storage-blobs/swagger/README.md +++ b/sdk/storage/azure-storage-blobs/swagger/README.md @@ -9,7 +9,7 @@ package-name: azure-storage-blobs namespace: Azure::Storage::Blobs output-folder: generated clear-output-folder: true -input-file: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/storage/data-plane/Microsoft.BlobStorage/preview/2021-12-02/blob.json +input-file: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/storage/data-plane/Microsoft.BlobStorage/stable/2021-12-02/blob.json ``` ## ModelFour Options @@ -70,8 +70,6 @@ directive: - from: swagger-document where: $["x-ms-paths"] transform: > - delete $["/{containerName}?restype=account&comp=properties"]; - delete $["/{containerName}/{blob}?restype=account&comp=properties"]; delete $["/{filesystem}/{path}?action=setAccessControl&blob"]; delete $["/{filesystem}/{path}?action=getAccessControl&blob"]; delete $["/{filesystem}/{path}?FileRename"]; @@ -102,12 +100,12 @@ directive: "name": "ApiVersion", "modelAsString": false }, - "enum": ["2023-11-03"] + "enum": ["2024-08-04"] }; - from: swagger-document where: $.parameters transform: > - $.ApiVersionParameter.enum[0] = "2023-11-03"; + $.ApiVersionParameter.enum[0] = "2024-08-04"; ``` ### Rename Operations @@ -673,22 +671,30 @@ directive: ```yaml directive: - from: swagger-document - where: $["x-ms-paths"]["/?restype=account&comp=properties"].get.responses["200"] + where: $ transform: > - $.schema = {"$ref": "#/definitions/AccountInfo"}; - - from: swagger-document - where: $["x-ms-paths"]["/?restype=account&comp=properties"].get.responses["200"].headers["x-ms-sku-name"] - transform: > - $["x-ms-enum"]["values"] = [ - {"value": "Standard_LRS", "name":"Standard_Lrs"}, - {"value": "Standard_GRS", "name":"StandardGrs"}, - {"value": "Standard_RAGRS", "name":"StandardRagrs"}, - {"value": "Standard_ZRS", "name":"StandardZrs"}, - {"value": "Premium_LRS", "name":"PremiumLrs"}, - {"value": "Premium_ZRS", "name":"PremiumZrs"}, - {"value": "Standard_GZRS", "name":"StandardGzrs"}, - {"value": "Standard_RAGZRS", "name":"StandardRagzrs"} + const operations = [ + "Service_GetAccountInfo", + "BlobContainer_GetAccountInfo", + "Blob_GetAccountInfo", ]; + for (const url in $["x-ms-paths"]) { + for (const verb in $["x-ms-paths"][url]) { + if (!operations.includes($["x-ms-paths"][url][verb].operationId)) continue; + const operation = $["x-ms-paths"][url][verb]; + operation.responses["200"].schema = {"$ref": "#/definitions/AccountInfo"}; + operation.responses["200"].headers["x-ms-sku-name"]["x-ms-enum"]["values"] = [ + {"value": "Standard_LRS", "name":"Standard_Lrs"}, + {"value": "Standard_GRS", "name":"StandardGrs"}, + {"value": "Standard_RAGRS", "name":"StandardRagrs"}, + {"value": "Standard_ZRS", "name":"StandardZrs"}, + {"value": "Premium_LRS", "name":"PremiumLrs"}, + {"value": "Premium_ZRS", "name":"PremiumZrs"}, + {"value": "Standard_GZRS", "name":"StandardGzrs"}, + {"value": "Standard_RAGZRS", "name":"StandardRagzrs"} + ]; + } + } ``` ### FindBlobsByTags diff --git a/sdk/storage/azure-storage-blobs/test/ut/blob_container_client_test.cpp b/sdk/storage/azure-storage-blobs/test/ut/blob_container_client_test.cpp index 0e6533730..ce53635ef 100644 --- a/sdk/storage/azure-storage-blobs/test/ut/blob_container_client_test.cpp +++ b/sdk/storage/azure-storage-blobs/test/ut/blob_container_client_test.cpp @@ -1493,4 +1493,14 @@ namespace Azure { namespace Storage { namespace Test { EXPECT_EQ(e.ErrorCode, "BlobNotFound"); } } + + TEST_F(BlobContainerClientTest, AccountInfo) + { + auto containerClient = *m_blobContainerClient; + + auto accountInfo = containerClient.GetAccountInfo().Value; + EXPECT_FALSE(accountInfo.SkuName.ToString().empty()); + EXPECT_FALSE(accountInfo.AccountKind.ToString().empty()); + EXPECT_FALSE(accountInfo.IsHierarchicalNamespaceEnabled); + } }}} // namespace Azure::Storage::Test 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 263372484..bb269127c 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 @@ -741,4 +741,36 @@ namespace Azure { namespace Storage { namespace Test { EXPECT_NO_THROW(blobClient1.Delete()); } + TEST_F(BlobSasTest, AccountSasAuthorizationErrorDetail_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; + + auto blobContainerClient = *m_blobContainerClient; + auto blobClient = *m_blockBlobClient; + const std::string blobName = m_blobName; + + Sas::AccountSasBuilder accountSasBuilder; + accountSasBuilder.Protocol = Sas::SasProtocol::HttpsAndHttp; + accountSasBuilder.StartsOn = sasStartsOn; + accountSasBuilder.ExpiresOn = sasExpiresOn; + accountSasBuilder.Services = Sas::AccountSasServices::Blobs; + accountSasBuilder.ResourceTypes = Sas::AccountSasResource::Service; + accountSasBuilder.SetPermissions(Sas::AccountSasPermissions::All); + auto sasToken = accountSasBuilder.GenerateSasToken(*keyCredential); + auto unauthorizedBlobClient = GetSasAuthenticatedClient(blobClient, sasToken); + try + { + unauthorizedBlobClient.Download(); + } + catch (StorageException& e) + { + EXPECT_EQ("AuthorizationResourceTypeMismatch", e.ErrorCode); + EXPECT_TRUE(e.AdditionalInformation.count("ExtendedErrorDetail") != 0); + } + } }}} // namespace Azure::Storage::Test diff --git a/sdk/storage/azure-storage-blobs/test/ut/block_blob_client_test.cpp b/sdk/storage/azure-storage-blobs/test/ut/block_blob_client_test.cpp index adbea6b43..bdbcaabbc 100644 --- a/sdk/storage/azure-storage-blobs/test/ut/block_blob_client_test.cpp +++ b/sdk/storage/azure-storage-blobs/test/ut/block_blob_client_test.cpp @@ -2074,4 +2074,85 @@ namespace Azure { namespace Storage { namespace Test { = Blobs::BlockBlobClient(m_blockBlobClient->GetUrl(), credential, clientOptions); EXPECT_THROW(blockBlobClient.GetProperties(), StorageException); } + + TEST_F(BlockBlobClientTest, AccountInfo) + { + auto blobClient = *m_blockBlobClient; + + auto accountInfo = blobClient.GetAccountInfo().Value; + EXPECT_FALSE(accountInfo.SkuName.ToString().empty()); + EXPECT_FALSE(accountInfo.AccountKind.ToString().empty()); + EXPECT_FALSE(accountInfo.IsHierarchicalNamespaceEnabled); + } + + TEST_F(BlockBlobClientTest, SharedKeySigningHeaderWithSymbols) + { + class AdditionalHeaderPolicy final : public Azure::Core::Http::Policies::HttpPolicy { + public: + ~AdditionalHeaderPolicy() override {} + + std::unique_ptr Clone() const override + { + return std::make_unique(*this); + } + + std::unique_ptr Send( + Azure::Core::Http::Request& request, + Azure::Core::Http::Policies::NextHttpPolicy nextPolicy, + Azure::Core::Context const& context) const override + { + // cSpell:disable + request.SetHeader("x-ms-test", "val"); + request.SetHeader("x-ms-test-", "val"); + request.SetHeader("x-ms-test-a", "val"); + request.SetHeader("x-ms-test-g", "val"); + request.SetHeader("x-ms-test-Z", "val"); + request.SetHeader("x-ms-testa", "val"); + request.SetHeader("x-ms-testd", "val"); + request.SetHeader("x-ms-testx", "val"); + request.SetHeader("x-ms-test--", "val"); + request.SetHeader("x-ms-test-_", "val"); + request.SetHeader("x-ms-test_-", "val"); + request.SetHeader("x-ms-test__", "val"); + request.SetHeader("x-ms-test-a", "val"); + request.SetHeader("x-ms-test-A", "val"); + request.SetHeader("x-ms-test-_A", "val"); + request.SetHeader("x-ms-test_a", "val"); + request.SetHeader("x-ms-test_Z", "val"); + request.SetHeader("x-ms-test_a_", "val"); + request.SetHeader("x-ms-test_a-", "val"); + request.SetHeader("x-ms-test_a-_", "val"); + request.SetHeader("x-ms-testa--", "val"); + request.SetHeader("x-ms-test-a-", "val"); + request.SetHeader("x-ms-test--a", "val"); + request.SetHeader("x-ms-testaa-", "val"); + request.SetHeader("x-ms-testa-a", "val"); + request.SetHeader("x-ms-test-aa", "val"); + + request.SetHeader("x-ms-test-!", "val"); + request.SetHeader("x-ms-test-#", "val"); + request.SetHeader("x-ms-test-$", "val"); + request.SetHeader("x-ms-test-%", "val"); + request.SetHeader("x-ms-test-&", "val"); + request.SetHeader("x-ms-test-*", "val"); + request.SetHeader("x-ms-test-+", "val"); + request.SetHeader("x-ms-test-.", "val"); + request.SetHeader("x-ms-test-^", "val"); + request.SetHeader("x-ms-test-_", "val"); + request.SetHeader("x-ms-test-`", "val"); + request.SetHeader("x-ms-test-|", "val"); + request.SetHeader("x-ms-test-~", "val"); + // cSpell:enable + return nextPolicy.Send(request, context); + } + }; + + auto clientOptions = InitStorageClientOptions(); + clientOptions.PerOperationPolicies.push_back(std::make_unique()); + auto keyCredential + = _internal::ParseConnectionString(StandardStorageConnectionString()).KeyCredential; + auto blockBlobClient + = Blobs::BlockBlobClient(m_blockBlobClient->GetUrl(), keyCredential, clientOptions); + EXPECT_NO_THROW(blockBlobClient.GetProperties()); + } }}} // namespace Azure::Storage::Test diff --git a/sdk/storage/azure-storage-blobs/test/ut/page_blob_client_test.cpp b/sdk/storage/azure-storage-blobs/test/ut/page_blob_client_test.cpp index 6af6c9dcb..3eb86a519 100644 --- a/sdk/storage/azure-storage-blobs/test/ut/page_blob_client_test.cpp +++ b/sdk/storage/azure-storage-blobs/test/ut/page_blob_client_test.cpp @@ -714,5 +714,75 @@ namespace Azure { namespace Storage { namespace Test { } } } + TEST_F(PageBlobClientTest, SharedKeySigningHeaderWithSymbols) + { + class AdditionalHeaderPolicy final : public Azure::Core::Http::Policies::HttpPolicy { + public: + ~AdditionalHeaderPolicy() override {} + + std::unique_ptr Clone() const override + { + return std::make_unique(*this); + } + + std::unique_ptr Send( + Azure::Core::Http::Request& request, + Azure::Core::Http::Policies::NextHttpPolicy nextPolicy, + Azure::Core::Context const& context) const override + { + // cSpell:disable + request.SetHeader("x-ms-test", "val"); + request.SetHeader("x-ms-test-", "val"); + request.SetHeader("x-ms-test-a", "val"); + request.SetHeader("x-ms-test-g", "val"); + request.SetHeader("x-ms-test-Z", "val"); + request.SetHeader("x-ms-testa", "val"); + request.SetHeader("x-ms-testd", "val"); + request.SetHeader("x-ms-testx", "val"); + request.SetHeader("x-ms-test--", "val"); + request.SetHeader("x-ms-test-_", "val"); + request.SetHeader("x-ms-test_-", "val"); + request.SetHeader("x-ms-test__", "val"); + request.SetHeader("x-ms-test-a", "val"); + request.SetHeader("x-ms-test-A", "val"); + request.SetHeader("x-ms-test-_A", "val"); + request.SetHeader("x-ms-test_a", "val"); + request.SetHeader("x-ms-test_Z", "val"); + request.SetHeader("x-ms-test_a_", "val"); + request.SetHeader("x-ms-test_a-", "val"); + request.SetHeader("x-ms-test_a-_", "val"); + request.SetHeader("x-ms-testa--", "val"); + request.SetHeader("x-ms-test-a-", "val"); + request.SetHeader("x-ms-test--a", "val"); + request.SetHeader("x-ms-testaa-", "val"); + request.SetHeader("x-ms-testa-a", "val"); + request.SetHeader("x-ms-test-aa", "val"); + + request.SetHeader("x-ms-test-!", "val"); + request.SetHeader("x-ms-test-#", "val"); + request.SetHeader("x-ms-test-$", "val"); + request.SetHeader("x-ms-test-%", "val"); + request.SetHeader("x-ms-test-&", "val"); + request.SetHeader("x-ms-test-*", "val"); + request.SetHeader("x-ms-test-+", "val"); + request.SetHeader("x-ms-test-.", "val"); + request.SetHeader("x-ms-test-^", "val"); + request.SetHeader("x-ms-test-_", "val"); + request.SetHeader("x-ms-test-`", "val"); + request.SetHeader("x-ms-test-|", "val"); + request.SetHeader("x-ms-test-~", "val"); + // cSpell:enable + return nextPolicy.Send(request, context); + } + }; + + auto clientOptions = InitStorageClientOptions(); + clientOptions.PerOperationPolicies.push_back(std::make_unique()); + auto keyCredential + = _internal::ParseConnectionString(StandardStorageConnectionString()).KeyCredential; + auto blockBlobClient + = Blobs::BlockBlobClient(m_pageBlobClient->GetUrl(), keyCredential, clientOptions); + EXPECT_NO_THROW(blockBlobClient.GetProperties()); + } }}} // namespace Azure::Storage::Test diff --git a/sdk/storage/azure-storage-common/CHANGELOG.md b/sdk/storage/azure-storage-common/CHANGELOG.md index 23648662a..d980760e2 100644 --- a/sdk/storage/azure-storage-common/CHANGELOG.md +++ b/sdk/storage/azure-storage-common/CHANGELOG.md @@ -4,11 +4,7 @@ ### Features Added -### Breaking Changes - -### Bugs Fixed - -### Other Changes +- Bumped up Account SAS version to `2024-08-04`. ## 12.6.0 (2024-05-07) diff --git a/sdk/storage/azure-storage-common/src/account_sas_builder.cpp b/sdk/storage/azure-storage-common/src/account_sas_builder.cpp index c4466a7e7..dd8af8b5f 100644 --- a/sdk/storage/azure-storage-common/src/account_sas_builder.cpp +++ b/sdk/storage/azure-storage-common/src/account_sas_builder.cpp @@ -9,7 +9,7 @@ namespace Azure { namespace Storage { namespace Sas { namespace { - constexpr static const char* SasVersion = "2024-05-04"; + constexpr static const char* SasVersion = "2024-08-04"; } void AccountSasBuilder::SetPermissions(AccountSasPermissions permissions) diff --git a/sdk/storage/azure-storage-common/src/shared_key_policy.cpp b/sdk/storage/azure-storage-common/src/shared_key_policy.cpp index 80dc602f7..3a8d7e0b3 100644 --- a/sdk/storage/azure-storage-common/src/shared_key_policy.cpp +++ b/sdk/storage/azure-storage-common/src/shared_key_policy.cpp @@ -10,6 +10,86 @@ #include +namespace { +/* + * We need to imitate .Net culture-aware sorting, which is used in storage service. + * Below tables contain sort-keys for en-US culture. + */ +const static int table_lv0[128] = { + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x71c, 0x0, 0x71f, 0x721, 0x723, 0x725, + 0x0, 0x0, 0x0, 0x72d, 0x803, 0x0, 0x0, 0x733, 0x0, 0xd03, 0xd1a, 0xd1c, 0xd1e, + 0xd20, 0xd22, 0xd24, 0xd26, 0xd28, 0xd2a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0xe02, 0xe09, 0xe0a, 0xe1a, 0xe21, 0xe23, 0xe25, 0xe2c, 0xe32, 0xe35, 0xe36, 0xe48, 0xe51, + 0xe70, 0xe7c, 0xe7e, 0xe89, 0xe8a, 0xe91, 0xe99, 0xe9f, 0xea2, 0xea4, 0xea6, 0xea7, 0xea9, + 0x0, 0x0, 0x0, 0x743, 0x744, 0x748, 0xe02, 0xe09, 0xe0a, 0xe1a, 0xe21, 0xe23, 0xe25, + 0xe2c, 0xe32, 0xe35, 0xe36, 0xe48, 0xe51, 0xe70, 0xe7c, 0xe7e, 0xe89, 0xe8a, 0xe91, 0xe99, + 0xe9f, 0xea2, 0xea4, 0xea6, 0xea7, 0xea9, 0x0, 0x74c, 0x0, 0x750, 0x0, +}; +const static int table_lv2[128] = { + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, + 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, +}; +const static int table_lv4[128] = { + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8012, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8212, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, +}; + +bool comparator(const std::string& lhs, const std::string& rhs) +{ + const static std::array tables{table_lv0, table_lv2, table_lv4}; + size_t curr_level = 0; + size_t i = 0; + size_t j = 0; + while (curr_level < tables.size()) + { + if (curr_level == tables.size() - 1 && i != j) + { + return i > j; + } + const int weight1 = i < lhs.size() ? tables[curr_level][static_cast(lhs[i])] : 0x1; + const int weight2 = j < rhs.size() ? tables[curr_level][static_cast(rhs[j])] : 0x1; + if (weight1 == 0x1 && weight2 == 0x1) + { + i = 0; + j = 0; + ++curr_level; + } + else if (weight1 == weight2) + { + ++i; + ++j; + } + else if (weight1 == 0) + { + ++i; + } + else if (weight2 == 0) + { + ++j; + } + else + { + return weight1 < weight2; + } + } + return false; +} +} // namespace + namespace Azure { namespace Storage { namespace _internal { std::string SharedKeyPolicy::GetSignature(const Core::Http::Request& request) const @@ -56,7 +136,9 @@ namespace Azure { namespace Storage { namespace _internal { std::string key = Azure::Core::_internal::StringExtensions::ToLower(ite->first); ordered_kv.emplace_back(std::make_pair(std::move(key), ite->second)); } - std::sort(ordered_kv.begin(), ordered_kv.end()); + std::sort(ordered_kv.begin(), ordered_kv.end(), [](const auto& lhs, const auto& rhs) { + return comparator(lhs.first, rhs.first); + }); for (const auto& p : ordered_kv) { string_to_sign += p.first + ":" + p.second + "\n"; diff --git a/sdk/storage/azure-storage-files-datalake/CHANGELOG.md b/sdk/storage/azure-storage-files-datalake/CHANGELOG.md index dc56d491e..103b9cb7f 100644 --- a/sdk/storage/azure-storage-files-datalake/CHANGELOG.md +++ b/sdk/storage/azure-storage-files-datalake/CHANGELOG.md @@ -4,11 +4,8 @@ ### Features Added -### Breaking Changes - -### Bugs Fixed - -### Other Changes +- Bumped up API version to `2024-08-04`. +- Added more detailed messaging for authorization failure cases. ## 12.10.0 (2024-05-07) diff --git a/sdk/storage/azure-storage-files-datalake/inc/azure/storage/files/datalake/rest_client.hpp b/sdk/storage/azure-storage-files-datalake/inc/azure/storage/files/datalake/rest_client.hpp index c9f04f7c8..27b56be0d 100644 --- a/sdk/storage/azure-storage-files-datalake/inc/azure/storage/files/datalake/rest_client.hpp +++ b/sdk/storage/azure-storage-files-datalake/inc/azure/storage/files/datalake/rest_client.hpp @@ -26,7 +26,7 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake { /** * The version used for the operations to Azure storage services. */ - constexpr static const char* ApiVersion = "2023-11-03"; + constexpr static const char* ApiVersion = "2024-08-04"; } // namespace _detail namespace Models { namespace _detail { diff --git a/sdk/storage/azure-storage-files-datalake/src/rest_client.cpp b/sdk/storage/azure-storage-files-datalake/src/rest_client.cpp index a467c5887..24ebdde41 100644 --- a/sdk/storage/azure-storage-files-datalake/src/rest_client.cpp +++ b/sdk/storage/azure-storage-files-datalake/src/rest_client.cpp @@ -61,7 +61,7 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake { { request.GetUrl().AppendQueryParameter("timeout", std::to_string(options.Timeout.Value())); } - request.SetHeader("x-ms-version", "2023-11-03"); + request.SetHeader("x-ms-version", "2024-08-04"); if (options.ContinuationToken.HasValue() && !options.ContinuationToken.Value().empty()) { request.GetUrl().AppendQueryParameter( @@ -157,7 +157,7 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake { { request.GetUrl().AppendQueryParameter("timeout", std::to_string(options.Timeout.Value())); } - request.SetHeader("x-ms-version", "2023-11-03"); + request.SetHeader("x-ms-version", "2024-08-04"); if (options.Resource.HasValue() && !options.Resource.Value().ToString().empty()) { request.GetUrl().AppendQueryParameter( @@ -345,7 +345,7 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake { { request.GetUrl().AppendQueryParameter("timeout", std::to_string(options.Timeout.Value())); } - request.SetHeader("x-ms-version", "2023-11-03"); + request.SetHeader("x-ms-version", "2024-08-04"); if (options.Recursive.HasValue()) { request.GetUrl().AppendQueryParameter( @@ -443,7 +443,7 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake { "If-Unmodified-Since", options.IfUnmodifiedSince.Value().ToString(Azure::DateTime::DateFormat::Rfc1123)); } - request.SetHeader("x-ms-version", "2023-11-03"); + request.SetHeader("x-ms-version", "2024-08-04"); auto pRawResponse = pipeline.Send(request, context); auto httpStatusCode = pRawResponse->GetStatusCode(); if (httpStatusCode != Core::Http::HttpStatusCode::Ok) @@ -490,7 +490,7 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake { { request.SetHeader("x-ms-acl", options.Acl.Value()); } - request.SetHeader("x-ms-version", "2023-11-03"); + request.SetHeader("x-ms-version", "2024-08-04"); auto pRawResponse = pipeline.Send(request, context); auto httpStatusCode = pRawResponse->GetStatusCode(); if (httpStatusCode != Core::Http::HttpStatusCode::Ok) @@ -543,7 +543,7 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake { { request.SetHeader("x-ms-undelete-source", options.UndeleteSource.Value()); } - request.SetHeader("x-ms-version", "2023-11-03"); + request.SetHeader("x-ms-version", "2024-08-04"); auto pRawResponse = pipeline.Send(request, context); auto httpStatusCode = pRawResponse->GetStatusCode(); if (httpStatusCode != Core::Http::HttpStatusCode::Ok) @@ -594,7 +594,7 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake { "If-Unmodified-Since", options.IfUnmodifiedSince.Value().ToString(Azure::DateTime::DateFormat::Rfc1123)); } - request.SetHeader("x-ms-version", "2023-11-03"); + request.SetHeader("x-ms-version", "2024-08-04"); auto pRawResponse = pipeline.Send(request, context); auto httpStatusCode = pRawResponse->GetStatusCode(); if (httpStatusCode != Core::Http::HttpStatusCode::Ok) @@ -693,7 +693,7 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake { "If-Unmodified-Since", options.IfUnmodifiedSince.Value().ToString(Azure::DateTime::DateFormat::Rfc1123)); } - request.SetHeader("x-ms-version", "2023-11-03"); + request.SetHeader("x-ms-version", "2024-08-04"); if (options.EncryptionKey.HasValue() && !options.EncryptionKey.Value().empty()) { request.SetHeader("x-ms-encryption-key", options.EncryptionKey.Value()); @@ -777,7 +777,7 @@ namespace Azure { namespace Storage { namespace Files { namespace DataLake { { request.SetHeader("x-ms-proposed-lease-id", options.ProposedLeaseId.Value()); } - request.SetHeader("x-ms-version", "2023-11-03"); + request.SetHeader("x-ms-version", "2024-08-04"); if (options.EncryptionKey.HasValue() && !options.EncryptionKey.Value().empty()) { request.SetHeader("x-ms-encryption-key", options.EncryptionKey.Value()); diff --git a/sdk/storage/azure-storage-files-datalake/swagger/README.md b/sdk/storage/azure-storage-files-datalake/swagger/README.md index 4f7fbd580..f2e2c144f 100644 --- a/sdk/storage/azure-storage-files-datalake/swagger/README.md +++ b/sdk/storage/azure-storage-files-datalake/swagger/README.md @@ -9,7 +9,7 @@ package-name: azure-storage-files-datalake namespace: Azure::Storage::Files::DataLake output-folder: generated clear-output-folder: true -input-file: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/storage/data-plane/Azure.Storage.Files.DataLake/preview/2023-05-03/DataLakeStorage.json +input-file: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/storage/data-plane/Azure.Storage.Files.DataLake/stable/2023-05-03/DataLakeStorage.json ``` ## ModelFour Options @@ -88,12 +88,12 @@ directive: "name": "ApiVersion", "modelAsString": false }, - "enum": ["2023-11-03"] + "enum": ["2024-08-04"] }; - from: swagger-document where: $.parameters transform: > - $.ApiVersionParameter.enum[0] = "2023-11-03"; + $.ApiVersionParameter.enum[0] = "2024-08-04"; ``` ### Rename Operations 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 d9408ad27..96d932464 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 @@ -786,4 +786,41 @@ namespace Azure { namespace Storage { namespace Test { ASSERT_TRUE(properties.EncryptionScope.HasValue()); EXPECT_EQ(properties.EncryptionScope.Value(), encryptionScope); } + + TEST_F(DataLakeSasTest, AccountSasAuthorizationErrorDetail_LIVEONLY_) + { + auto sasStartsOn = std::chrono::system_clock::now() - std::chrono::minutes(5); + auto sasExpiresOn = std::chrono::system_clock::now() + std::chrono::minutes(60); + + Sas::AccountSasBuilder accountSasBuilder; + accountSasBuilder.Protocol = Sas::SasProtocol::HttpsAndHttp; + accountSasBuilder.StartsOn = sasStartsOn; + accountSasBuilder.ExpiresOn = sasExpiresOn; + accountSasBuilder.Services = Sas::AccountSasServices::Blobs; + accountSasBuilder.ResourceTypes = Sas::AccountSasResource::Service; + accountSasBuilder.SetPermissions(Sas::AccountSasPermissions::All); + + auto keyCredential = _internal::ParseConnectionString(AdlsGen2ConnectionString()).KeyCredential; + + std::string directoryName = RandomString(); + std::string fileName = RandomString(); + + auto dataLakeFileSystemClient = *m_fileSystemClient; + auto dataLakeDirectoryClient = dataLakeFileSystemClient.GetDirectoryClient(directoryName); + dataLakeDirectoryClient.Create(); + auto dataLakeFileClient = dataLakeFileSystemClient.GetFileClient(fileName); + dataLakeFileClient.Create(); + + auto sasToken = accountSasBuilder.GenerateSasToken(*keyCredential); + auto unauthorizedFileClient = GetSasAuthenticatedClient(dataLakeFileClient, sasToken); + try + { + unauthorizedFileClient.Download(); + } + catch (StorageException& e) + { + EXPECT_EQ("AuthorizationResourceTypeMismatch", e.ErrorCode); + EXPECT_TRUE(e.AdditionalInformation.count("ExtendedErrorDetail") != 0); + } + } }}} // namespace Azure::Storage::Test diff --git a/sdk/storage/azure-storage-files-shares/CHANGELOG.md b/sdk/storage/azure-storage-files-shares/CHANGELOG.md index 7b1d275a0..d2bdf39a5 100644 --- a/sdk/storage/azure-storage-files-shares/CHANGELOG.md +++ b/sdk/storage/azure-storage-files-shares/CHANGELOG.md @@ -4,11 +4,9 @@ ### Features Added -### Breaking Changes - -### Bugs Fixed - -### Other Changes +- Bumped up API version to `2024-08-04`. +- Added more detailed messaging for authorization failure cases. +- Added support for snapshot management on NFS shares. ## 12.9.0 (2024-05-07) diff --git a/sdk/storage/azure-storage-files-shares/inc/azure/storage/files/shares/rest_client.hpp b/sdk/storage/azure-storage-files-shares/inc/azure/storage/files/shares/rest_client.hpp index f0c9fb483..4ae1343f6 100644 --- a/sdk/storage/azure-storage-files-shares/inc/azure/storage/files/shares/rest_client.hpp +++ b/sdk/storage/azure-storage-files-shares/inc/azure/storage/files/shares/rest_client.hpp @@ -31,7 +31,7 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares { /** * The version used for the operations to Azure storage services. */ - constexpr static const char* ApiVersion = "2024-05-04"; + constexpr static const char* ApiVersion = "2024-08-04"; } // namespace _detail namespace Models { /** @@ -393,6 +393,12 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares { * Root squash to set on the share. Only valid for NFS shares. */ Nullable RootSquash; + /** + * Version 2023-08-03 and newer. Specifies whether the snapshot virtual directory should be + * accessible at the root of share mount point when NFS is enabled. This header is only + * returned for shares, not for snapshots. + */ + Nullable EnableSnapshotVirtualDirectoryAccess; }; /** * @brief A listed Azure Storage share item. @@ -570,6 +576,12 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares { * Valid for NFS shares only. */ Nullable RootSquash; + /** + * Version 2023-08-03 and newer. Specifies whether the snapshot virtual directory should be + * accessible at the root of share mount point when NFS is enabled. This header is only + * returned for shares, not for snapshots. + */ + Nullable EnableSnapshotVirtualDirectoryAccess; }; /** * @brief Specifies the option include to delete the base share and all of its snapshots. @@ -2085,6 +2097,7 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares { Nullable AccessTier; Nullable EnabledProtocols; Nullable RootSquash; + Nullable EnableSnapshotVirtualDirectoryAccess; }; static Response Create( Core::Http::_internal::HttpPipeline& pipeline, @@ -2200,6 +2213,7 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares { Nullable AccessTier; Nullable LeaseId; Nullable RootSquash; + Nullable EnableSnapshotVirtualDirectoryAccess; }; static Response SetProperties( Core::Http::_internal::HttpPipeline& pipeline, diff --git a/sdk/storage/azure-storage-files-shares/inc/azure/storage/files/shares/share_options.hpp b/sdk/storage/azure-storage-files-shares/inc/azure/storage/files/shares/share_options.hpp index 6df49abb1..4e81ead93 100644 --- a/sdk/storage/azure-storage-files-shares/inc/azure/storage/files/shares/share_options.hpp +++ b/sdk/storage/azure-storage-files-shares/inc/azure/storage/files/shares/share_options.hpp @@ -174,6 +174,13 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares { * specified, the default is NoRootSquash. */ Azure::Nullable RootSquash; + + /** + * Version 2023-08-03 and newer. Specifies whether the snapshot virtual directory should be + * accessible at the root of share mount point when NFS is enabled. This header is only + * returned for shares, not for snapshots. + */ + Nullable EnableSnapshotVirtualDirectoryAccess; }; /** @@ -226,6 +233,13 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares { * specified, the default is NoRootSquash. */ Azure::Nullable RootSquash; + + /** + * Version 2023-08-03 and newer. Specifies whether the snapshot virtual directory should be + * accessible at the root of share mount point when NFS is enabled. This header is only + * returned for shares, not for snapshots. + */ + Nullable EnableSnapshotVirtualDirectoryAccess; }; /** diff --git a/sdk/storage/azure-storage-files-shares/src/rest_client.cpp b/sdk/storage/azure-storage-files-shares/src/rest_client.cpp index 0efdce276..d13da0188 100644 --- a/sdk/storage/azure-storage-files-shares/src/rest_client.cpp +++ b/sdk/storage/azure-storage-files-shares/src/rest_client.cpp @@ -317,7 +317,7 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares { request.SetHeader("Content-Length", std::to_string(requestBody.Length())); request.GetUrl().AppendQueryParameter("restype", "service"); request.GetUrl().AppendQueryParameter("comp", "properties"); - request.SetHeader("x-ms-version", "2024-05-04"); + request.SetHeader("x-ms-version", "2024-08-04"); auto pRawResponse = pipeline.Send(request, context); auto httpStatusCode = pRawResponse->GetStatusCode(); if (httpStatusCode != Core::Http::HttpStatusCode::Accepted) @@ -337,7 +337,7 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares { auto request = Core::Http::Request(Core::Http::HttpMethod::Get, url); request.GetUrl().AppendQueryParameter("restype", "service"); request.GetUrl().AppendQueryParameter("comp", "properties"); - request.SetHeader("x-ms-version", "2024-05-04"); + request.SetHeader("x-ms-version", "2024-08-04"); (void)options; auto pRawResponse = pipeline.Send(request, context); auto httpStatusCode = pRawResponse->GetStatusCode(); @@ -569,7 +569,7 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares { _internal::UrlEncodeQueryParameter( ListSharesIncludeFlagsToString(options.Include.Value()))); } - request.SetHeader("x-ms-version", "2024-05-04"); + request.SetHeader("x-ms-version", "2024-08-04"); auto pRawResponse = pipeline.Send(request, context); auto httpStatusCode = pRawResponse->GetStatusCode(); if (httpStatusCode != Core::Http::HttpStatusCode::Ok) @@ -614,6 +614,7 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares { kLeaseDuration, kEnabledProtocols, kRootSquash, + kEnableSnapshotVirtualDirectoryAccess, kNextMarker, }; const std::unordered_map XmlTagEnumMap{ @@ -647,6 +648,8 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares { {"LeaseDuration", XmlTagEnum::kLeaseDuration}, {"EnabledProtocols", XmlTagEnum::kEnabledProtocols}, {"RootSquash", XmlTagEnum::kRootSquash}, + {"EnableSnapshotVirtualDirectoryAccess", + XmlTagEnum::kEnableSnapshotVirtualDirectoryAccess}, {"NextMarker", XmlTagEnum::kNextMarker}, }; std::vector xmlPath; @@ -865,6 +868,15 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares { { vectorElement1.Details.RootSquash = Models::ShareRootSquash(node.Value); } + else if ( + xmlPath.size() == 5 && xmlPath[0] == XmlTagEnum::kEnumerationResults + && xmlPath[1] == XmlTagEnum::kShares && xmlPath[2] == XmlTagEnum::kShare + && xmlPath[3] == XmlTagEnum::kProperties + && xmlPath[4] == XmlTagEnum::kEnableSnapshotVirtualDirectoryAccess) + { + vectorElement1.Details.EnableSnapshotVirtualDirectoryAccess + = node.Value == std::string("true"); + } else if ( xmlPath.size() == 2 && xmlPath[0] == XmlTagEnum::kEnumerationResults && xmlPath[1] == XmlTagEnum::kNextMarker) @@ -922,7 +934,7 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares { { request.SetHeader("x-ms-access-tier", options.AccessTier.Value().ToString()); } - request.SetHeader("x-ms-version", "2024-05-04"); + request.SetHeader("x-ms-version", "2024-08-04"); if (options.EnabledProtocols.HasValue() && !options.EnabledProtocols.Value().ToString().empty()) { @@ -932,6 +944,12 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares { { request.SetHeader("x-ms-root-squash", options.RootSquash.Value().ToString()); } + if (options.EnableSnapshotVirtualDirectoryAccess.HasValue()) + { + request.SetHeader( + "x-ms-enable-snapshot-virtual-directory-access", + options.EnableSnapshotVirtualDirectoryAccess.Value() ? "true" : "false"); + } auto pRawResponse = pipeline.Send(request, context); auto httpStatusCode = pRawResponse->GetStatusCode(); if (httpStatusCode != Core::Http::HttpStatusCode::Created) @@ -957,7 +975,7 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares { request.GetUrl().AppendQueryParameter( "sharesnapshot", _internal::UrlEncodeQueryParameter(options.Sharesnapshot.Value())); } - request.SetHeader("x-ms-version", "2024-05-04"); + request.SetHeader("x-ms-version", "2024-08-04"); if (options.LeaseId.HasValue() && !options.LeaseId.Value().empty()) { request.SetHeader("x-ms-lease-id", options.LeaseId.Value()); @@ -1044,6 +1062,12 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares { response.RootSquash = Models::ShareRootSquash(pRawResponse->GetHeaders().at("x-ms-root-squash")); } + if (pRawResponse->GetHeaders().count("x-ms-enable-snapshot-virtual-directory-access") != 0) + { + response.EnableSnapshotVirtualDirectoryAccess + = pRawResponse->GetHeaders().at("x-ms-enable-snapshot-virtual-directory-access") + == std::string("true"); + } return Response(std::move(response), std::move(pRawResponse)); } Response ShareClient::Delete( @@ -1059,7 +1083,7 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares { request.GetUrl().AppendQueryParameter( "sharesnapshot", _internal::UrlEncodeQueryParameter(options.Sharesnapshot.Value())); } - request.SetHeader("x-ms-version", "2024-05-04"); + request.SetHeader("x-ms-version", "2024-08-04"); if (options.DeleteSnapshots.HasValue() && !options.DeleteSnapshots.Value().ToString().empty()) { request.SetHeader("x-ms-delete-snapshots", options.DeleteSnapshots.Value().ToString()); @@ -1095,7 +1119,7 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares { { request.SetHeader("x-ms-proposed-lease-id", options.ProposedLeaseId.Value()); } - request.SetHeader("x-ms-version", "2024-05-04"); + request.SetHeader("x-ms-version", "2024-08-04"); if (options.Sharesnapshot.HasValue() && !options.Sharesnapshot.Value().empty()) { request.GetUrl().AppendQueryParameter( @@ -1129,7 +1153,7 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares { { request.SetHeader("x-ms-lease-id", options.LeaseId); } - request.SetHeader("x-ms-version", "2024-05-04"); + request.SetHeader("x-ms-version", "2024-08-04"); if (options.Sharesnapshot.HasValue() && !options.Sharesnapshot.Value().empty()) { request.GetUrl().AppendQueryParameter( @@ -1166,7 +1190,7 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares { { request.SetHeader("x-ms-proposed-lease-id", options.ProposedLeaseId.Value()); } - request.SetHeader("x-ms-version", "2024-05-04"); + request.SetHeader("x-ms-version", "2024-08-04"); if (options.Sharesnapshot.HasValue() && !options.Sharesnapshot.Value().empty()) { request.GetUrl().AppendQueryParameter( @@ -1200,7 +1224,7 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares { { request.SetHeader("x-ms-lease-id", options.LeaseId); } - request.SetHeader("x-ms-version", "2024-05-04"); + request.SetHeader("x-ms-version", "2024-08-04"); if (options.Sharesnapshot.HasValue() && !options.Sharesnapshot.Value().empty()) { request.GetUrl().AppendQueryParameter( @@ -1238,7 +1262,7 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares { { request.SetHeader("x-ms-lease-id", options.LeaseId.Value()); } - request.SetHeader("x-ms-version", "2024-05-04"); + request.SetHeader("x-ms-version", "2024-08-04"); if (options.Sharesnapshot.HasValue() && !options.Sharesnapshot.Value().empty()) { request.GetUrl().AppendQueryParameter( @@ -1271,7 +1295,7 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares { { request.SetHeader("x-ms-meta-" + p.first, p.second); } - request.SetHeader("x-ms-version", "2024-05-04"); + request.SetHeader("x-ms-version", "2024-08-04"); auto pRawResponse = pipeline.Send(request, context); auto httpStatusCode = pRawResponse->GetStatusCode(); if (httpStatusCode != Core::Http::HttpStatusCode::Created) @@ -1305,7 +1329,7 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares { request.SetHeader("Content-Length", std::to_string(requestBody.Length())); request.GetUrl().AppendQueryParameter("restype", "share"); request.GetUrl().AppendQueryParameter("comp", "filepermission"); - request.SetHeader("x-ms-version", "2024-05-04"); + request.SetHeader("x-ms-version", "2024-08-04"); if (options.FileRequestIntent.HasValue() && !options.FileRequestIntent.Value().ToString().empty()) { @@ -1335,7 +1359,7 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares { { request.SetHeader("x-ms-file-permission-key", options.FilePermissionKey); } - request.SetHeader("x-ms-version", "2024-05-04"); + request.SetHeader("x-ms-version", "2024-08-04"); if (options.FileRequestIntent.HasValue() && !options.FileRequestIntent.Value().ToString().empty()) { @@ -1366,7 +1390,7 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares { auto request = Core::Http::Request(Core::Http::HttpMethod::Put, url); request.GetUrl().AppendQueryParameter("restype", "share"); request.GetUrl().AppendQueryParameter("comp", "properties"); - request.SetHeader("x-ms-version", "2024-05-04"); + request.SetHeader("x-ms-version", "2024-08-04"); if (options.Quota.HasValue()) { request.SetHeader("x-ms-share-quota", std::to_string(options.Quota.Value())); @@ -1383,6 +1407,12 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares { { request.SetHeader("x-ms-root-squash", options.RootSquash.Value().ToString()); } + if (options.EnableSnapshotVirtualDirectoryAccess.HasValue()) + { + request.SetHeader( + "x-ms-enable-snapshot-virtual-directory-access", + options.EnableSnapshotVirtualDirectoryAccess.Value() ? "true" : "false"); + } auto pRawResponse = pipeline.Send(request, context); auto httpStatusCode = pRawResponse->GetStatusCode(); if (httpStatusCode != Core::Http::HttpStatusCode::Ok) @@ -1409,7 +1439,7 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares { { request.SetHeader("x-ms-meta-" + p.first, p.second); } - request.SetHeader("x-ms-version", "2024-05-04"); + request.SetHeader("x-ms-version", "2024-08-04"); if (options.LeaseId.HasValue() && !options.LeaseId.Value().empty()) { request.SetHeader("x-ms-lease-id", options.LeaseId.Value()); @@ -1435,7 +1465,7 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares { auto request = Core::Http::Request(Core::Http::HttpMethod::Get, url); request.GetUrl().AppendQueryParameter("restype", "share"); request.GetUrl().AppendQueryParameter("comp", "acl"); - request.SetHeader("x-ms-version", "2024-05-04"); + request.SetHeader("x-ms-version", "2024-08-04"); if (options.LeaseId.HasValue() && !options.LeaseId.Value().empty()) { request.SetHeader("x-ms-lease-id", options.LeaseId.Value()); @@ -1582,7 +1612,7 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares { request.SetHeader("Content-Length", std::to_string(requestBody.Length())); request.GetUrl().AppendQueryParameter("restype", "share"); request.GetUrl().AppendQueryParameter("comp", "acl"); - request.SetHeader("x-ms-version", "2024-05-04"); + request.SetHeader("x-ms-version", "2024-08-04"); if (options.LeaseId.HasValue() && !options.LeaseId.Value().empty()) { request.SetHeader("x-ms-lease-id", options.LeaseId.Value()); @@ -1609,7 +1639,7 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares { auto request = Core::Http::Request(Core::Http::HttpMethod::Get, url); request.GetUrl().AppendQueryParameter("restype", "share"); request.GetUrl().AppendQueryParameter("comp", "stats"); - request.SetHeader("x-ms-version", "2024-05-04"); + request.SetHeader("x-ms-version", "2024-08-04"); if (options.LeaseId.HasValue() && !options.LeaseId.Value().empty()) { request.SetHeader("x-ms-lease-id", options.LeaseId.Value()); @@ -1695,7 +1725,7 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares { { request.SetHeader("x-ms-meta-" + p.first, p.second); } - request.SetHeader("x-ms-version", "2024-05-04"); + request.SetHeader("x-ms-version", "2024-08-04"); if (options.FilePermission.HasValue() && !options.FilePermission.Value().empty()) { request.SetHeader("x-ms-file-permission", options.FilePermission.Value()); @@ -1784,7 +1814,7 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares { request.GetUrl().AppendQueryParameter( "sharesnapshot", _internal::UrlEncodeQueryParameter(options.Sharesnapshot.Value())); } - request.SetHeader("x-ms-version", "2024-05-04"); + request.SetHeader("x-ms-version", "2024-08-04"); if (options.FileRequestIntent.HasValue() && !options.FileRequestIntent.Value().ToString().empty()) { @@ -1850,7 +1880,7 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares { request.SetHeader( "x-ms-allow-trailing-dot", options.AllowTrailingDot.Value() ? "true" : "false"); } - request.SetHeader("x-ms-version", "2024-05-04"); + request.SetHeader("x-ms-version", "2024-08-04"); if (options.FileRequestIntent.HasValue() && !options.FileRequestIntent.Value().ToString().empty()) { @@ -1874,7 +1904,7 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares { auto request = Core::Http::Request(Core::Http::HttpMethod::Put, url); request.GetUrl().AppendQueryParameter("restype", "directory"); request.GetUrl().AppendQueryParameter("comp", "properties"); - request.SetHeader("x-ms-version", "2024-05-04"); + request.SetHeader("x-ms-version", "2024-08-04"); if (options.FilePermission.HasValue() && !options.FilePermission.Value().empty()) { request.SetHeader("x-ms-file-permission", options.FilePermission.Value()); @@ -1964,7 +1994,7 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares { { request.SetHeader("x-ms-meta-" + p.first, p.second); } - request.SetHeader("x-ms-version", "2024-05-04"); + request.SetHeader("x-ms-version", "2024-08-04"); if (options.AllowTrailingDot.HasValue()) { request.SetHeader( @@ -2018,7 +2048,7 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares { request.GetUrl().AppendQueryParameter( "maxresults", std::to_string(options.MaxResults.Value())); } - request.SetHeader("x-ms-version", "2024-05-04"); + request.SetHeader("x-ms-version", "2024-08-04"); if (options.Include.HasValue() && !ListFilesIncludeFlagsToString(options.Include.Value()).empty()) { @@ -2411,7 +2441,7 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares { { request.SetHeader("x-ms-recursive", options.Recursive.Value() ? "true" : "false"); } - request.SetHeader("x-ms-version", "2024-05-04"); + request.SetHeader("x-ms-version", "2024-08-04"); if (options.AllowTrailingDot.HasValue()) { request.SetHeader( @@ -2624,7 +2654,7 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares { { request.SetHeader("x-ms-recursive", options.Recursive.Value() ? "true" : "false"); } - request.SetHeader("x-ms-version", "2024-05-04"); + request.SetHeader("x-ms-version", "2024-08-04"); if (options.AllowTrailingDot.HasValue()) { request.SetHeader( @@ -2662,7 +2692,7 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares { auto request = Core::Http::Request(Core::Http::HttpMethod::Put, url); request.GetUrl().AppendQueryParameter("restype", "directory"); request.GetUrl().AppendQueryParameter("comp", "rename"); - request.SetHeader("x-ms-version", "2024-05-04"); + request.SetHeader("x-ms-version", "2024-08-04"); if (!options.RenameSource.empty()) { request.SetHeader("x-ms-file-rename-source", options.RenameSource); @@ -2770,7 +2800,7 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares { request.SetHeader( "x-ms-allow-trailing-dot", options.AllowTrailingDot.Value() ? "true" : "false"); } - request.SetHeader("x-ms-version", "2024-05-04"); + request.SetHeader("x-ms-version", "2024-08-04"); request.SetHeader("x-ms-content-length", std::to_string(options.FileContentLength)); request.SetHeader("x-ms-type", "file"); if (options.FileContentType.HasValue() && !options.FileContentType.Value().empty()) @@ -2890,7 +2920,7 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares { request.SetHeader( "x-ms-allow-trailing-dot", options.AllowTrailingDot.Value() ? "true" : "false"); } - request.SetHeader("x-ms-version", "2024-05-04"); + request.SetHeader("x-ms-version", "2024-08-04"); if (options.Range.HasValue() && !options.Range.Value().empty()) { request.SetHeader("x-ms-range", options.Range.Value()); @@ -3075,7 +3105,7 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares { request.GetUrl().AppendQueryParameter( "sharesnapshot", _internal::UrlEncodeQueryParameter(options.Sharesnapshot.Value())); } - request.SetHeader("x-ms-version", "2024-05-04"); + request.SetHeader("x-ms-version", "2024-08-04"); if (options.LeaseId.HasValue() && !options.LeaseId.Value().empty()) { request.SetHeader("x-ms-lease-id", options.LeaseId.Value()); @@ -3213,7 +3243,7 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares { request.SetHeader( "x-ms-allow-trailing-dot", options.AllowTrailingDot.Value() ? "true" : "false"); } - request.SetHeader("x-ms-version", "2024-05-04"); + request.SetHeader("x-ms-version", "2024-08-04"); if (options.LeaseId.HasValue() && !options.LeaseId.Value().empty()) { request.SetHeader("x-ms-lease-id", options.LeaseId.Value()); @@ -3240,7 +3270,7 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares { { auto request = Core::Http::Request(Core::Http::HttpMethod::Put, url); request.GetUrl().AppendQueryParameter("comp", "properties"); - request.SetHeader("x-ms-version", "2024-05-04"); + request.SetHeader("x-ms-version", "2024-08-04"); if (options.FileContentLength.HasValue()) { request.SetHeader("x-ms-content-length", std::to_string(options.FileContentLength.Value())); @@ -3364,7 +3394,7 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares { { request.SetHeader("x-ms-meta-" + p.first, p.second); } - request.SetHeader("x-ms-version", "2024-05-04"); + request.SetHeader("x-ms-version", "2024-08-04"); if (options.LeaseId.HasValue() && !options.LeaseId.Value().empty()) { request.SetHeader("x-ms-lease-id", options.LeaseId.Value()); @@ -3408,7 +3438,7 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares { { request.SetHeader("x-ms-proposed-lease-id", options.ProposedLeaseId.Value()); } - request.SetHeader("x-ms-version", "2024-05-04"); + request.SetHeader("x-ms-version", "2024-08-04"); if (options.AllowTrailingDot.HasValue()) { request.SetHeader( @@ -3446,7 +3476,7 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares { { request.SetHeader("x-ms-lease-id", options.LeaseId); } - request.SetHeader("x-ms-version", "2024-05-04"); + request.SetHeader("x-ms-version", "2024-08-04"); if (options.AllowTrailingDot.HasValue()) { request.SetHeader( @@ -3487,7 +3517,7 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares { { request.SetHeader("x-ms-proposed-lease-id", options.ProposedLeaseId.Value()); } - request.SetHeader("x-ms-version", "2024-05-04"); + request.SetHeader("x-ms-version", "2024-08-04"); if (options.AllowTrailingDot.HasValue()) { request.SetHeader( @@ -3525,7 +3555,7 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares { { request.SetHeader("x-ms-lease-id", options.LeaseId.Value()); } - request.SetHeader("x-ms-version", "2024-05-04"); + request.SetHeader("x-ms-version", "2024-08-04"); if (options.AllowTrailingDot.HasValue()) { request.SetHeader( @@ -3572,7 +3602,7 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares { { request.SetHeader("Content-MD5", Core::Convert::Base64Encode(options.ContentMD5.Value())); } - request.SetHeader("x-ms-version", "2024-05-04"); + request.SetHeader("x-ms-version", "2024-08-04"); if (options.LeaseId.HasValue() && !options.LeaseId.Value().empty()) { request.SetHeader("x-ms-lease-id", options.LeaseId.Value()); @@ -3659,7 +3689,7 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares { "x-ms-source-if-none-match-crc64", Core::Convert::Base64Encode(options.SourceIfNoneMatchCrc64.Value())); } - request.SetHeader("x-ms-version", "2024-05-04"); + request.SetHeader("x-ms-version", "2024-08-04"); if (options.LeaseId.HasValue() && !options.LeaseId.Value().empty()) { request.SetHeader("x-ms-lease-id", options.LeaseId.Value()); @@ -3735,7 +3765,7 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares { "prevsharesnapshot", _internal::UrlEncodeQueryParameter(options.Prevsharesnapshot.Value())); } - request.SetHeader("x-ms-version", "2024-05-04"); + request.SetHeader("x-ms-version", "2024-08-04"); if (options.Range.HasValue() && !options.Range.Value().empty()) { request.SetHeader("x-ms-range", options.Range.Value()); @@ -3864,7 +3894,7 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares { const Core::Context& context) { auto request = Core::Http::Request(Core::Http::HttpMethod::Put, url); - request.SetHeader("x-ms-version", "2024-05-04"); + request.SetHeader("x-ms-version", "2024-08-04"); for (const auto& p : options.Metadata) { request.SetHeader("x-ms-meta-" + p.first, p.second); @@ -3962,7 +3992,7 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares { "copyid", _internal::UrlEncodeQueryParameter(options.CopyId)); } request.SetHeader("x-ms-copy-action", "abort"); - request.SetHeader("x-ms-version", "2024-05-04"); + request.SetHeader("x-ms-version", "2024-08-04"); if (options.LeaseId.HasValue() && !options.LeaseId.Value().empty()) { request.SetHeader("x-ms-lease-id", options.LeaseId.Value()); @@ -4009,7 +4039,7 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares { request.GetUrl().AppendQueryParameter( "sharesnapshot", _internal::UrlEncodeQueryParameter(options.Sharesnapshot.Value())); } - request.SetHeader("x-ms-version", "2024-05-04"); + request.SetHeader("x-ms-version", "2024-08-04"); if (options.AllowTrailingDot.HasValue()) { request.SetHeader( @@ -4218,7 +4248,7 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares { { request.SetHeader("x-ms-handle-id", options.HandleId); } - request.SetHeader("x-ms-version", "2024-05-04"); + request.SetHeader("x-ms-version", "2024-08-04"); if (options.AllowTrailingDot.HasValue()) { request.SetHeader( @@ -4255,7 +4285,7 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares { { auto request = Core::Http::Request(Core::Http::HttpMethod::Put, url); request.GetUrl().AppendQueryParameter("comp", "rename"); - request.SetHeader("x-ms-version", "2024-05-04"); + request.SetHeader("x-ms-version", "2024-08-04"); if (!options.RenameSource.empty()) { request.SetHeader("x-ms-file-rename-source", options.RenameSource); diff --git a/sdk/storage/azure-storage-files-shares/src/share_client.cpp b/sdk/storage/azure-storage-files-shares/src/share_client.cpp index ee9e27034..5aa5d7a07 100644 --- a/sdk/storage/azure-storage-files-shares/src/share_client.cpp +++ b/sdk/storage/azure-storage-files-shares/src/share_client.cpp @@ -149,6 +149,8 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares { protocolLayerOptions.AccessTier = options.AccessTier; protocolLayerOptions.EnabledProtocols = options.EnabledProtocols; protocolLayerOptions.RootSquash = options.RootSquash; + protocolLayerOptions.EnableSnapshotVirtualDirectoryAccess + = options.EnableSnapshotVirtualDirectoryAccess; auto result = _detail::ShareClient::Create(*m_pipeline, m_shareUrl, protocolLayerOptions, context); Models::CreateShareResult ret; @@ -245,6 +247,8 @@ namespace Azure { namespace Storage { namespace Files { namespace Shares { protocolLayerOptions.Quota = options.ShareQuotaInGiB; protocolLayerOptions.AccessTier = options.AccessTier; protocolLayerOptions.RootSquash = options.RootSquash; + protocolLayerOptions.EnableSnapshotVirtualDirectoryAccess + = options.EnableSnapshotVirtualDirectoryAccess; return _detail::ShareClient::SetProperties( *m_pipeline, m_shareUrl, protocolLayerOptions, context); } diff --git a/sdk/storage/azure-storage-files-shares/swagger/README.md b/sdk/storage/azure-storage-files-shares/swagger/README.md index d07ec47b9..345aa9350 100644 --- a/sdk/storage/azure-storage-files-shares/swagger/README.md +++ b/sdk/storage/azure-storage-files-shares/swagger/README.md @@ -9,7 +9,7 @@ package-name: azure-storage-files-shares namespace: Azure::Storage::Files::Shares output-folder: generated clear-output-folder: true -input-file: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/storage/data-plane/Microsoft.FileStorage/preview/2024-05-04/file.json +input-file: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/storage/data-plane/Microsoft.FileStorage/stable/2024-08-04/file.json ``` ## ModelFour Options @@ -79,12 +79,12 @@ directive: "name": "ApiVersion", "modelAsString": false }, - "enum": ["2024-05-04"] + "enum": ["2024-08-04"] }; - from: swagger-document where: $.parameters transform: > - $.ApiVersionParameter.enum[0] = "2024-05-04"; + $.ApiVersionParameter.enum[0] = "2024-08-04"; ``` ### Rename Operations @@ -469,6 +469,7 @@ directive: $["x-ms-enabled-protocols"]["enum"] = ["Smb", "Nfs"]; $["x-ms-enabled-protocols"]["x-ms-enum"] = {"name": "ShareProtocols", "modelAsString": false}; $["x-ms-enabled-protocols"]["x-ms-enum"]["values"] = [{"value": "SMB", "name": "Smb"},{"value": "NFS", "name": "Nfs"}]; + $["x-ms-enable-snapshot-virtual-directory-access"]["x-nullable"] = true; - from: swagger-document where: $["x-ms-paths"]["/{shareName}?restype=share"].get.responses["200"] transform: > @@ -1067,6 +1068,7 @@ directive: $.ShareItemDetails.properties["EnabledProtocols"].description = "The protocols which have been enabled on the share."; $.ShareItemDetails.properties["RootSquash"].description = "Root squash to set on the share. Only valid for NFS shares."; $.ShareItemDetails.properties["Last-Modified"].description = "The date and time the share was last modified."; + $.ShareItemDetails.properties["EnableSnapshotVirtualDirectoryAccess"].description = "Version 2023-08-03 and newer. Specifies whether the snapshot virtual directory should be accessible at the root of share mount point when NFS is enabled. This header is only returned for shares, not for snapshots."; $.ShareItemInternal.properties["Name"].description = "The name of the share."; $.ShareItemInternal.properties["Snapshot"].description = "The snapshot of the share."; $.ShareItemInternal.properties["Deleted"].description = "True if the share is deleted."; diff --git a/sdk/storage/azure-storage-files-shares/test/ut/share_client_test.cpp b/sdk/storage/azure-storage-files-shares/test/ut/share_client_test.cpp index 3a847103a..3838402d8 100644 --- a/sdk/storage/azure-storage-files-shares/test/ut/share_client_test.cpp +++ b/sdk/storage/azure-storage-files-shares/test/ut/share_client_test.cpp @@ -79,18 +79,6 @@ namespace Azure { namespace Storage { namespace Test { return shareClient; } - Files::Shares::ShareClient FileShareClientTest::GetPremiumShareClientForTest( - const std::string& shareName, - Files::Shares::ShareClientOptions clientOptions) - { - InitStorageClientOptions(clientOptions); - auto shareClient = Files::Shares::ShareClient::CreateFromConnectionString( - PremiumFileConnectionString(), shareName, clientOptions); - m_resourceCleanupFunctions.push_back([shareClient]() { shareClient.DeleteIfExists(); }); - - return shareClient; - } - TEST_F(FileShareClientTest, CreateDeleteShares) { { @@ -729,4 +717,30 @@ namespace Azure { namespace Storage { namespace Test { shareClient = Files::Shares::ShareClient(m_shareClient->GetUrl(), credential, clientOptions); EXPECT_THROW(shareClient.GetPermission(created.FilePermissionKey), StorageException); } + + TEST_F(FileShareClientTest, EnableSnapshotVirtualDirectoryAccess_PLAYBACKONLY_) + { + std::string shareName = LowercaseRandomString(); + auto shareClient = GetPremiumShareClientForTest(shareName); + Files::Shares::CreateShareOptions createOptions; + createOptions.EnabledProtocols = Files::Shares::Models::ShareProtocols::Nfs; + shareClient.Create(createOptions); + + Files::Shares::SetSharePropertiesOptions setPropertiesOptions; + // EnableSnapshotVirtualDirectoryAccess = true + setPropertiesOptions.EnableSnapshotVirtualDirectoryAccess = true; + shareClient.SetProperties(setPropertiesOptions); + auto properties = shareClient.GetProperties().Value; + EXPECT_TRUE( + properties.EnableSnapshotVirtualDirectoryAccess.HasValue() + && properties.EnableSnapshotVirtualDirectoryAccess.Value()); + + // EnableSnapshotVirtualDirectoryAccess = false + setPropertiesOptions.EnableSnapshotVirtualDirectoryAccess = false; + shareClient.SetProperties(setPropertiesOptions); + properties = shareClient.GetProperties().Value; + EXPECT_TRUE( + properties.EnableSnapshotVirtualDirectoryAccess.HasValue() + && !properties.EnableSnapshotVirtualDirectoryAccess.Value()); + } }}} // namespace Azure::Storage::Test diff --git a/sdk/storage/azure-storage-files-shares/test/ut/share_client_test.hpp b/sdk/storage/azure-storage-files-shares/test/ut/share_client_test.hpp index 70ad3832c..6b2fa5102 100644 --- a/sdk/storage/azure-storage-files-shares/test/ut/share_client_test.hpp +++ b/sdk/storage/azure-storage-files-shares/test/ut/share_client_test.hpp @@ -14,9 +14,6 @@ namespace Azure { namespace Storage { namespace Test { Files::Shares::ShareClient GetShareClientForTest( const std::string& shareName, Files::Shares::ShareClientOptions clientOptions = Files::Shares::ShareClientOptions()); - Files::Shares::ShareClient GetPremiumShareClientForTest( - const std::string& shareName, - Files::Shares::ShareClientOptions clientOptions = Files::Shares::ShareClientOptions()); protected: std::shared_ptr m_shareClient; diff --git a/sdk/storage/azure-storage-files-shares/test/ut/share_sas_test.cpp b/sdk/storage/azure-storage-files-shares/test/ut/share_sas_test.cpp index 4008198c4..c7285f6d6 100644 --- a/sdk/storage/azure-storage-files-shares/test/ut/share_sas_test.cpp +++ b/sdk/storage/azure-storage-files-shares/test/ut/share_sas_test.cpp @@ -493,4 +493,39 @@ namespace Azure { namespace Storage { namespace Test { EXPECT_EQ(properties.Value.HttpHeaders.CacheControl, fileSasBuilder.CacheControl); EXPECT_EQ(properties.Value.HttpHeaders.ContentEncoding, fileSasBuilder.ContentEncoding); } + + TEST_F(ShareSasTest, AccountSasAuthorizationErrorDetail_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; + + std::string fileName = RandomString(); + + auto shareClient = *m_shareClient; + auto fileClient = shareClient.GetRootDirectoryClient().GetFileClient(fileName); + fileClient.Create(1); + + Sas::AccountSasBuilder accountSasBuilder; + accountSasBuilder.Protocol = Sas::SasProtocol::HttpsAndHttp; + accountSasBuilder.StartsOn = sasStartsOn; + accountSasBuilder.ExpiresOn = sasExpiresOn; + accountSasBuilder.Services = Sas::AccountSasServices::Files; + accountSasBuilder.ResourceTypes = Sas::AccountSasResource::Service; + accountSasBuilder.SetPermissions(Sas::AccountSasPermissions::All); + auto sasToken = accountSasBuilder.GenerateSasToken(*keyCredential); + auto unauthorizedFileClient = GetSasAuthenticatedClient(fileClient, sasToken); + try + { + unauthorizedFileClient.Download(); + } + catch (StorageException& e) + { + EXPECT_EQ("AuthorizationResourceTypeMismatch", e.ErrorCode); + EXPECT_TRUE(e.AdditionalInformation.count("ExtendedErrorDetail") != 0); + } + } }}} // namespace Azure::Storage::Test diff --git a/sdk/storage/azure-storage-files-shares/test/ut/share_service_client_test.cpp b/sdk/storage/azure-storage-files-shares/test/ut/share_service_client_test.cpp index 7655422a6..865e92644 100644 --- a/sdk/storage/azure-storage-files-shares/test/ut/share_service_client_test.cpp +++ b/sdk/storage/azure-storage-files-shares/test/ut/share_service_client_test.cpp @@ -26,6 +26,18 @@ namespace Azure { namespace Storage { namespace Test { StandardStorageConnectionString(), options)); } + Files::Shares::ShareClient FileShareServiceClientTest::GetPremiumShareClientForTest( + const std::string& shareName, + Files::Shares::ShareClientOptions clientOptions) + { + InitStorageClientOptions(clientOptions); + auto shareClient = Files::Shares::ShareClient::CreateFromConnectionString( + PremiumFileConnectionString(), shareName, clientOptions); + m_resourceCleanupFunctions.push_back([shareClient]() { shareClient.DeleteIfExists(); }); + + return shareClient; + } + TEST_F(FileShareServiceClientTest, Constructors) { auto clientOptions = InitStorageClientOptions(); @@ -132,6 +144,53 @@ namespace Azure { namespace Storage { namespace Test { } } + TEST_F(FileShareServiceClientTest, ListSharesEnableSnapshotVirtualDirectoryAccess_PLAYBACKONLY_) + { + auto premiumFileShareServiceClient + = Files::Shares::ShareServiceClient::CreateFromConnectionString( + PremiumFileConnectionString(), + InitStorageClientOptions()); + std::string shareName1 = LowercaseRandomString(); + std::string shareName2 = LowercaseRandomString(); + auto shareClient1 = GetPremiumShareClientForTest(shareName1); + auto shareClient2 = GetPremiumShareClientForTest(shareName2); + Files::Shares::CreateShareOptions createOptions; + createOptions.EnabledProtocols = Files::Shares::Models::ShareProtocols::Nfs; + shareClient1.Create(createOptions); + shareClient2.Create(createOptions); + + Files::Shares::SetSharePropertiesOptions setPropertiesOptions; + setPropertiesOptions.EnableSnapshotVirtualDirectoryAccess = true; + shareClient1.SetProperties(setPropertiesOptions); + setPropertiesOptions.EnableSnapshotVirtualDirectoryAccess = false; + shareClient2.SetProperties(setPropertiesOptions); + + Azure::Nullable share1; + Azure::Nullable share2; + for (auto page = premiumFileShareServiceClient.ListShares(); page.HasPage(); + page.MoveToNextPage()) + { + for (const auto& share : page.Shares) + { + if (share.Name == shareName1) + { + share1 = share; + } + else if (share.Name == shareName2) + { + share2 = share; + } + } + } + ASSERT_TRUE(share1.HasValue() && share2.HasValue()); + EXPECT_TRUE( + share1.Value().Details.EnableSnapshotVirtualDirectoryAccess.HasValue() + && share1.Value().Details.EnableSnapshotVirtualDirectoryAccess.Value()); + EXPECT_TRUE( + share2.Value().Details.EnableSnapshotVirtualDirectoryAccess.HasValue() + && !share2.Value().Details.EnableSnapshotVirtualDirectoryAccess.Value()); + } + TEST_F(FileShareServiceClientTest, GetProperties) { auto ret = m_shareServiceClient->GetProperties(); diff --git a/sdk/storage/azure-storage-files-shares/test/ut/share_service_client_test.hpp b/sdk/storage/azure-storage-files-shares/test/ut/share_service_client_test.hpp index e28072da2..0c09b2b09 100644 --- a/sdk/storage/azure-storage-files-shares/test/ut/share_service_client_test.hpp +++ b/sdk/storage/azure-storage-files-shares/test/ut/share_service_client_test.hpp @@ -11,6 +11,10 @@ namespace Azure { namespace Storage { namespace Test { protected: void SetUp() override; + Files::Shares::ShareClient GetPremiumShareClientForTest( + const std::string& shareName, + Files::Shares::ShareClientOptions clientOptions = Files::Shares::ShareClientOptions()); + protected: std::shared_ptr m_shareServiceClient; }; diff --git a/sdk/storage/azure-storage-queues/CHANGELOG.md b/sdk/storage/azure-storage-queues/CHANGELOG.md index a64144321..b565b3522 100644 --- a/sdk/storage/azure-storage-queues/CHANGELOG.md +++ b/sdk/storage/azure-storage-queues/CHANGELOG.md @@ -4,11 +4,8 @@ ### Features Added -### Breaking Changes - -### Bugs Fixed - -### Other Changes +- Bumped up API version to `2024-08-04`. +- Added more detailed messaging for authorization failure cases. ## 12.2.0 (2023-11-07) diff --git a/sdk/storage/azure-storage-queues/inc/azure/storage/queues/queue_options.hpp b/sdk/storage/azure-storage-queues/inc/azure/storage/queues/queue_options.hpp index b0ff0dfae..178f94a9f 100644 --- a/sdk/storage/azure-storage-queues/inc/azure/storage/queues/queue_options.hpp +++ b/sdk/storage/azure-storage-queues/inc/azure/storage/queues/queue_options.hpp @@ -101,6 +101,12 @@ namespace Azure { namespace Storage { namespace Queues { */ AZ_STORAGE_QUEUES_DLLEXPORT const static ServiceVersion V2019_12_12; + /** + * @brief API version 2024-08-04. + * + */ + AZ_STORAGE_QUEUES_DLLEXPORT const static ServiceVersion V2024_08_04; + private: std::string m_version; }; diff --git a/sdk/storage/azure-storage-queues/inc/azure/storage/queues/rest_client.hpp b/sdk/storage/azure-storage-queues/inc/azure/storage/queues/rest_client.hpp index 250a0f11c..e93b1e938 100644 --- a/sdk/storage/azure-storage-queues/inc/azure/storage/queues/rest_client.hpp +++ b/sdk/storage/azure-storage-queues/inc/azure/storage/queues/rest_client.hpp @@ -26,7 +26,7 @@ namespace Azure { namespace Storage { namespace Queues { /** * The version used for the operations to Azure storage services. */ - constexpr static const char* ApiVersion = "2019-12-12"; + constexpr static const char* ApiVersion = "2024-08-04"; } // namespace _detail namespace Models { /** diff --git a/sdk/storage/azure-storage-queues/src/queue_options.cpp b/sdk/storage/azure-storage-queues/src/queue_options.cpp index 91eca6e2a..353f80f63 100644 --- a/sdk/storage/azure-storage-queues/src/queue_options.cpp +++ b/sdk/storage/azure-storage-queues/src/queue_options.cpp @@ -9,6 +9,7 @@ namespace Azure { namespace Storage { namespace Queues { const ServiceVersion ServiceVersion::V2018_03_28(std::string("2018-03-28")); const ServiceVersion ServiceVersion::V2019_12_12(std::string("2019-12-12")); + const ServiceVersion ServiceVersion::V2024_08_04(std::string("2024-08-04")); const std::chrono::seconds EnqueueMessageOptions::MessageNeverExpires{-1}; }}} // namespace Azure::Storage::Queues diff --git a/sdk/storage/azure-storage-queues/src/rest_client.cpp b/sdk/storage/azure-storage-queues/src/rest_client.cpp index 4befa24c8..614a595ca 100644 --- a/sdk/storage/azure-storage-queues/src/rest_client.cpp +++ b/sdk/storage/azure-storage-queues/src/rest_client.cpp @@ -189,7 +189,7 @@ namespace Azure { namespace Storage { namespace Queues { request.SetHeader("Content-Length", std::to_string(requestBody.Length())); request.GetUrl().AppendQueryParameter("restype", "service"); request.GetUrl().AppendQueryParameter("comp", "properties"); - request.SetHeader("x-ms-version", "2019-12-12"); + request.SetHeader("x-ms-version", "2024-08-04"); auto pRawResponse = pipeline.Send(request, context); auto httpStatusCode = pRawResponse->GetStatusCode(); if (httpStatusCode != Core::Http::HttpStatusCode::Accepted) @@ -209,7 +209,7 @@ namespace Azure { namespace Storage { namespace Queues { auto request = Core::Http::Request(Core::Http::HttpMethod::Get, url); request.GetUrl().AppendQueryParameter("restype", "service"); request.GetUrl().AppendQueryParameter("comp", "properties"); - request.SetHeader("x-ms-version", "2019-12-12"); + request.SetHeader("x-ms-version", "2024-08-04"); (void)options; auto pRawResponse = pipeline.Send(request, context); auto httpStatusCode = pRawResponse->GetStatusCode(); @@ -446,7 +446,7 @@ namespace Azure { namespace Storage { namespace Queues { auto request = Core::Http::Request(Core::Http::HttpMethod::Get, url); request.GetUrl().AppendQueryParameter("restype", "service"); request.GetUrl().AppendQueryParameter("comp", "stats"); - request.SetHeader("x-ms-version", "2019-12-12"); + request.SetHeader("x-ms-version", "2024-08-04"); (void)options; auto pRawResponse = pipeline.Send(request, context); auto httpStatusCode = pRawResponse->GetStatusCode(); @@ -546,7 +546,7 @@ namespace Azure { namespace Storage { namespace Queues { _internal::UrlEncodeQueryParameter( ListQueuesIncludeFlagsToString(options.Include.Value()))); } - request.SetHeader("x-ms-version", "2019-12-12"); + request.SetHeader("x-ms-version", "2024-08-04"); auto pRawResponse = pipeline.Send(request, context); auto httpStatusCode = pRawResponse->GetStatusCode(); if (httpStatusCode != Core::Http::HttpStatusCode::Ok) @@ -669,7 +669,7 @@ namespace Azure { namespace Storage { namespace Queues { { request.SetHeader("x-ms-meta-" + p.first, p.second); } - request.SetHeader("x-ms-version", "2019-12-12"); + request.SetHeader("x-ms-version", "2024-08-04"); auto pRawResponse = pipeline.Send(request, context); auto httpStatusCode = pRawResponse->GetStatusCode(); if (!(httpStatusCode == Core::Http::HttpStatusCode::Created @@ -687,7 +687,7 @@ namespace Azure { namespace Storage { namespace Queues { const Core::Context& context) { auto request = Core::Http::Request(Core::Http::HttpMethod::Delete, url); - request.SetHeader("x-ms-version", "2019-12-12"); + request.SetHeader("x-ms-version", "2024-08-04"); (void)options; auto pRawResponse = pipeline.Send(request, context); auto httpStatusCode = pRawResponse->GetStatusCode(); @@ -706,7 +706,7 @@ namespace Azure { namespace Storage { namespace Queues { { auto request = Core::Http::Request(Core::Http::HttpMethod::Get, url); request.GetUrl().AppendQueryParameter("comp", "metadata"); - request.SetHeader("x-ms-version", "2019-12-12"); + request.SetHeader("x-ms-version", "2024-08-04"); (void)options; auto pRawResponse = pipeline.Send(request, context); auto httpStatusCode = pRawResponse->GetStatusCode(); @@ -737,7 +737,7 @@ namespace Azure { namespace Storage { namespace Queues { { request.SetHeader("x-ms-meta-" + p.first, p.second); } - request.SetHeader("x-ms-version", "2019-12-12"); + request.SetHeader("x-ms-version", "2024-08-04"); auto pRawResponse = pipeline.Send(request, context); auto httpStatusCode = pRawResponse->GetStatusCode(); if (httpStatusCode != Core::Http::HttpStatusCode::NoContent) @@ -755,7 +755,7 @@ namespace Azure { namespace Storage { namespace Queues { { auto request = Core::Http::Request(Core::Http::HttpMethod::Get, url); request.GetUrl().AppendQueryParameter("comp", "acl"); - request.SetHeader("x-ms-version", "2019-12-12"); + request.SetHeader("x-ms-version", "2024-08-04"); (void)options; auto pRawResponse = pipeline.Send(request, context); auto httpStatusCode = pRawResponse->GetStatusCode(); @@ -898,7 +898,7 @@ namespace Azure { namespace Storage { namespace Queues { request.SetHeader("Content-Type", "application/xml; charset=UTF-8"); request.SetHeader("Content-Length", std::to_string(requestBody.Length())); request.GetUrl().AppendQueryParameter("comp", "acl"); - request.SetHeader("x-ms-version", "2019-12-12"); + request.SetHeader("x-ms-version", "2024-08-04"); auto pRawResponse = pipeline.Send(request, context); auto httpStatusCode = pRawResponse->GetStatusCode(); if (httpStatusCode != Core::Http::HttpStatusCode::NoContent) @@ -926,7 +926,7 @@ namespace Azure { namespace Storage { namespace Queues { request.GetUrl().AppendQueryParameter( "visibilitytimeout", std::to_string(options.Visibilitytimeout.Value())); } - request.SetHeader("x-ms-version", "2019-12-12"); + request.SetHeader("x-ms-version", "2024-08-04"); auto pRawResponse = pipeline.Send(request, context); auto httpStatusCode = pRawResponse->GetStatusCode(); if (httpStatusCode != Core::Http::HttpStatusCode::Ok) @@ -1052,7 +1052,7 @@ namespace Azure { namespace Storage { namespace Queues { const Core::Context& context) { auto request = Core::Http::Request(Core::Http::HttpMethod::Delete, url); - request.SetHeader("x-ms-version", "2019-12-12"); + request.SetHeader("x-ms-version", "2024-08-04"); (void)options; auto pRawResponse = pipeline.Send(request, context); auto httpStatusCode = pRawResponse->GetStatusCode(); @@ -1094,7 +1094,7 @@ namespace Azure { namespace Storage { namespace Queues { request.GetUrl().AppendQueryParameter( "messagettl", std::to_string(options.MessageTimeToLive.Value())); } - request.SetHeader("x-ms-version", "2019-12-12"); + request.SetHeader("x-ms-version", "2024-08-04"); auto pRawResponse = pipeline.Send(request, context); auto httpStatusCode = pRawResponse->GetStatusCode(); if (httpStatusCode != Core::Http::HttpStatusCode::Created) @@ -1203,7 +1203,7 @@ namespace Azure { namespace Storage { namespace Queues { request.GetUrl().AppendQueryParameter( "numofmessages", std::to_string(options.NumberOfMessages.Value())); } - request.SetHeader("x-ms-version", "2019-12-12"); + request.SetHeader("x-ms-version", "2024-08-04"); auto pRawResponse = pipeline.Send(request, context); auto httpStatusCode = pRawResponse->GetStatusCode(); if (httpStatusCode != Core::Http::HttpStatusCode::Ok) @@ -1332,7 +1332,7 @@ namespace Azure { namespace Storage { namespace Queues { } request.GetUrl().AppendQueryParameter( "visibilitytimeout", std::to_string(options.Visibilitytimeout)); - request.SetHeader("x-ms-version", "2019-12-12"); + request.SetHeader("x-ms-version", "2024-08-04"); auto pRawResponse = pipeline.Send(request, context); auto httpStatusCode = pRawResponse->GetStatusCode(); if (httpStatusCode != Core::Http::HttpStatusCode::NoContent) @@ -1358,7 +1358,7 @@ namespace Azure { namespace Storage { namespace Queues { request.GetUrl().AppendQueryParameter( "popreceipt", _internal::UrlEncodeQueryParameter(options.PopReceipt)); } - request.SetHeader("x-ms-version", "2019-12-12"); + request.SetHeader("x-ms-version", "2024-08-04"); auto pRawResponse = pipeline.Send(request, context); auto httpStatusCode = pRawResponse->GetStatusCode(); if (httpStatusCode != Core::Http::HttpStatusCode::NoContent) @@ -1382,7 +1382,7 @@ namespace Azure { namespace Storage { namespace Queues { } request.GetUrl().AppendQueryParameter( "visibilitytimeout", std::to_string(options.Visibilitytimeout)); - request.SetHeader("x-ms-version", "2019-12-12"); + request.SetHeader("x-ms-version", "2024-08-04"); auto pRawResponse = pipeline.Send(request, context); auto httpStatusCode = pRawResponse->GetStatusCode(); if (httpStatusCode != Core::Http::HttpStatusCode::NoContent) diff --git a/sdk/storage/azure-storage-queues/swagger/README.md b/sdk/storage/azure-storage-queues/swagger/README.md index 6f3d22258..6fc784a06 100644 --- a/sdk/storage/azure-storage-queues/swagger/README.md +++ b/sdk/storage/azure-storage-queues/swagger/README.md @@ -9,7 +9,7 @@ package-name: azure-storage-queues namespace: Azure::Storage::Queues output-folder: generated clear-output-folder: true -input-file: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/storage/data-plane/Microsoft.QueueStorage/preview/2018-03-28/queue.json +input-file: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/storage/data-plane/Microsoft.QueueStorage/stable/2018-03-28/queue.json ``` ## ModelFour Options @@ -77,13 +77,13 @@ directive: "name": "ApiVersion", "modelAsString": false }, - "enum": ["2019-12-12"], + "enum": ["2024-08-04"], "description": "The version used for the operations to Azure storage services." }; - from: swagger-document where: $.parameters transform: > - $.ApiVersionParameter.enum[0] = "2019-12-12"; + $.ApiVersionParameter.enum[0] = "2024-08-04"; ``` ### Rename Operations 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 077a835d4..15e84ccea 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 @@ -317,4 +317,34 @@ namespace Azure { namespace Storage { namespace Test { VerifyQueueSasRead(queueClient, sasToken); } + TEST_F(QueueSasTest, AccountSasAuthorizationErrorDetail_LIVEONLY_) + { + auto sasStartsOn = std::chrono::system_clock::now() - std::chrono::minutes(5); + auto sasExpiresOn = std::chrono::system_clock::now() + std::chrono::minutes(60); + + Sas::AccountSasBuilder accountSasBuilder; + accountSasBuilder.Protocol = Sas::SasProtocol::HttpsAndHttp; + accountSasBuilder.StartsOn = sasStartsOn; + accountSasBuilder.ExpiresOn = sasExpiresOn; + accountSasBuilder.Services = Sas::AccountSasServices::Queue; + accountSasBuilder.ResourceTypes = Sas::AccountSasResource::Object; + accountSasBuilder.SetPermissions(Sas::AccountSasPermissions::All); + auto keyCredential + = _internal::ParseConnectionString(StandardStorageConnectionString()).KeyCredential; + + auto queueServiceClient = *m_queueServiceClient; + + auto sasToken = accountSasBuilder.GenerateSasToken(*keyCredential); + auto unauthorizedQueueServiceClient = GetSasAuthenticatedClient(queueServiceClient, sasToken); + try + { + unauthorizedQueueServiceClient.ListQueues(); + } + catch (StorageException& e) + { + EXPECT_EQ("AuthorizationResourceTypeMismatch", e.ErrorCode); + EXPECT_TRUE(e.AdditionalInformation.count("ExtendedErrorDetail") != 0); + } + } + }}} // namespace Azure::Storage::Test diff --git a/sdk/storage/azure-storage-queues/test/ut/queue_service_client_test.cpp b/sdk/storage/azure-storage-queues/test/ut/queue_service_client_test.cpp index cda4ce94c..ca5161aae 100644 --- a/sdk/storage/azure-storage-queues/test/ut/queue_service_client_test.cpp +++ b/sdk/storage/azure-storage-queues/test/ut/queue_service_client_test.cpp @@ -342,4 +342,73 @@ namespace Azure { namespace Storage { namespace Test { = Queues::QueueServiceClient(m_queueServiceClient->GetUrl(), credential, clientOptions); EXPECT_THROW(queueServiceClient.GetProperties(), StorageException); } + + TEST_F(QueueServiceClientTest, BearerChallengeWorks) + { + auto clientOptions = InitStorageClientOptions(); + auto options = InitStorageClientOptions(); + + // With tenantId + clientOptions.EnableTenantDiscovery = true; + options.AdditionallyAllowedTenants = {"*"}; + auto queueServiceClient = Queues::QueueServiceClient( + m_queueServiceClient->GetUrl(), + std::make_shared( + AadTenantId(), AadClientId(), AadClientSecret(), options), + clientOptions); + EXPECT_NO_THROW(queueServiceClient.GetProperties()); + + // Without tenantId + clientOptions.EnableTenantDiscovery = true; + options.AdditionallyAllowedTenants = {"*"}; + queueServiceClient = Queues::QueueServiceClient( + m_queueServiceClient->GetUrl(), + std::make_shared( + "", AadClientId(), AadClientSecret(), options), + clientOptions); + EXPECT_NO_THROW(queueServiceClient.GetProperties()); + + // With custom audience + auto queueUrl = Azure::Core::Url(m_queueServiceClient->GetUrl()); + clientOptions.Audience + = Queues::QueueAudience(queueUrl.GetScheme() + "://" + queueUrl.GetHost()); + queueServiceClient = Queues::QueueServiceClient( + m_queueServiceClient->GetUrl(), + std::make_shared( + "", AadClientId(), AadClientSecret(), options), + clientOptions); + EXPECT_NO_THROW(queueServiceClient.GetProperties()); + clientOptions.Audience.Reset(); + + // With error tenantId + clientOptions.EnableTenantDiscovery = true; + options.AdditionallyAllowedTenants = {"*"}; + queueServiceClient = Queues::QueueServiceClient( + m_queueServiceClient->GetUrl(), + std::make_shared( + "test", AadClientId(), AadClientSecret(), options), + clientOptions); + EXPECT_NO_THROW(queueServiceClient.GetProperties()); + + // Disable Tenant Discovery and without tenantId + clientOptions.EnableTenantDiscovery = false; + queueServiceClient = Queues::QueueServiceClient( + m_queueServiceClient->GetUrl(), + std::make_shared( + "", AadClientId(), AadClientSecret(), options), + clientOptions); + EXPECT_THROW( + queueServiceClient.GetProperties(), Azure::Core::Credentials::AuthenticationException); + + // Don't allow additional tenants + clientOptions.EnableTenantDiscovery = true; + options.AdditionallyAllowedTenants = {}; + queueServiceClient = Queues::QueueServiceClient( + m_queueServiceClient->GetUrl(), + std::make_shared( + "", AadClientId(), AadClientSecret(), options), + clientOptions); + EXPECT_THROW( + queueServiceClient.GetProperties(), Azure::Core::Credentials::AuthenticationException); + } }}} // namespace Azure::Storage::Test