diff --git a/sdk/storage/Azure.Storage.Blobs/src/Azure.Storage.Blobs.csproj b/sdk/storage/Azure.Storage.Blobs/src/Azure.Storage.Blobs.csproj
index a97864d06566..bef69e9d56b4 100644
--- a/sdk/storage/Azure.Storage.Blobs/src/Azure.Storage.Blobs.csproj
+++ b/sdk/storage/Azure.Storage.Blobs/src/Azure.Storage.Blobs.csproj
@@ -106,5 +106,6 @@
+
diff --git a/sdk/storage/Azure.Storage.Blobs/src/BlobServiceClient.cs b/sdk/storage/Azure.Storage.Blobs/src/BlobServiceClient.cs
index 8eb5c079a4b5..775051b352a0 100644
--- a/sdk/storage/Azure.Storage.Blobs/src/BlobServiceClient.cs
+++ b/sdk/storage/Azure.Storage.Blobs/src/BlobServiceClient.cs
@@ -1611,12 +1611,12 @@ private async Task> GetUserDelegationKeyInternal(
if (startsOn.HasValue && startsOn.Value.Offset != TimeSpan.Zero)
{
- throw BlobErrors.InvalidDateTimeUtc(nameof(startsOn));
+ throw Errors.InvalidDateTimeUtc(nameof(startsOn));
}
if (expiresOn.Offset != TimeSpan.Zero)
{
- throw BlobErrors.InvalidDateTimeUtc(nameof(expiresOn));
+ throw Errors.InvalidDateTimeUtc(nameof(expiresOn));
}
KeyInfo keyInfo = new KeyInfo(expiresOn.ToString(Constants.Iso8601Format, CultureInfo.InvariantCulture))
diff --git a/sdk/storage/Azure.Storage.Blobs/src/Sas/BlobSasBuilder.cs b/sdk/storage/Azure.Storage.Blobs/src/Sas/BlobSasBuilder.cs
index 10445deb8b22..9c36d485a7ed 100644
--- a/sdk/storage/Azure.Storage.Blobs/src/Sas/BlobSasBuilder.cs
+++ b/sdk/storage/Azure.Storage.Blobs/src/Sas/BlobSasBuilder.cs
@@ -463,7 +463,7 @@ public BlobSasQueryParameters ToSasQueryParameters(UserDelegationKey userDelegat
stringToSign = ToStringToSign(userDelegationKey, accountName);
- string signature = ComputeHMACSHA256(userDelegationKey.Value, stringToSign);
+ string signature = SasExtensions.ComputeHMACSHA256(userDelegationKey.Value, stringToSign);
BlobSasQueryParameters p = new BlobSasQueryParameters(
version: Version,
@@ -546,22 +546,6 @@ private static string GetCanonicalName(string account, string containerName, str
? $"/blob/{account}/{containerName}/{blobName.Replace("\\", "/")}"
: $"/blob/{account}/{containerName}";
- ///
- /// ComputeHMACSHA256 generates a base-64 hash signature string for an
- /// HTTP request or for a SAS.
- ///
- ///
- /// A used to sign with a key
- /// representing AD credentials.
- ///
- /// The message to sign.
- /// The signed message.
- private static string ComputeHMACSHA256(string userDelegationKeyValue, string message) =>
- Convert.ToBase64String(
- new HMACSHA256(
- Convert.FromBase64String(userDelegationKeyValue))
- .ComputeHash(Encoding.UTF8.GetBytes(message)));
-
///
/// Ensure the 's properties are in a
/// consistent state.
diff --git a/sdk/storage/Azure.Storage.Blobs/src/Shared/BlobErrors.cs b/sdk/storage/Azure.Storage.Blobs/src/Shared/BlobErrors.cs
index 873958b211bd..788602804d52 100644
--- a/sdk/storage/Azure.Storage.Blobs/src/Shared/BlobErrors.cs
+++ b/sdk/storage/Azure.Storage.Blobs/src/Shared/BlobErrors.cs
@@ -20,9 +20,6 @@ public static InvalidOperationException BlobOrContainerMissing(string leaseClien
string blobContainerClient) =>
new InvalidOperationException($"{leaseClient} requires either a {blobBaseClient} or {blobContainerClient}");
- public static ArgumentException InvalidDateTimeUtc(string dateTime) =>
- new ArgumentException($"{dateTime} must be UTC");
-
internal static void VerifyHttpsCustomerProvidedKey(Uri uri, CustomerProvidedKey? customerProvidedKey)
{
if (customerProvidedKey.HasValue && !string.Equals(uri.Scheme, Constants.Https, StringComparison.OrdinalIgnoreCase))
diff --git a/sdk/storage/Azure.Storage.Common/src/Shared/Constants.cs b/sdk/storage/Azure.Storage.Common/src/Shared/Constants.cs
index 57c15c5fbd8c..4360d5164585 100644
--- a/sdk/storage/Azure.Storage.Common/src/Shared/Constants.cs
+++ b/sdk/storage/Azure.Storage.Common/src/Shared/Constants.cs
@@ -25,6 +25,7 @@ internal static class Constants
/// Gets the default service version to use when building shared access
/// signatures.
///
+ // TODO fix this
public const string DefaultSasVersion = "2026-02-06";
///
@@ -435,6 +436,8 @@ internal static class Queue
public const string UriSubDomain = "queue";
public const string QueueTraitsMetadata = "metadata";
+
+ public const string Name = "Queue";
}
///
@@ -631,6 +634,7 @@ internal static class Resource
public const string File = "f";
public const string Share = "s";
public const string Directory = "d";
+ public const string Queue = "q";
}
internal static class AccountServices
diff --git a/sdk/storage/Azure.Storage.Common/src/Shared/Errors.Clients.cs b/sdk/storage/Azure.Storage.Common/src/Shared/Errors.Clients.cs
index a3e4330d9a58..d112219c9a5f 100644
--- a/sdk/storage/Azure.Storage.Common/src/Shared/Errors.Clients.cs
+++ b/sdk/storage/Azure.Storage.Common/src/Shared/Errors.Clients.cs
@@ -35,6 +35,9 @@ public static ArgumentOutOfRangeException MustBeGreaterThanValueOrEqualToOtherVa
long value1)
=> new ArgumentOutOfRangeException(paramName, $"Value must be greater than {value0} or equal to {value1}");
+ public static ArgumentException InvalidDateTimeUtc(string dateTime) =>
+ new ArgumentException($"{dateTime} must be UTC");
+
public static ArgumentException StreamMustBeReadable(string paramName)
=> new ArgumentException("Stream must be readable", paramName);
diff --git a/sdk/storage/Azure.Storage.Common/src/Shared/SasExtensions.cs b/sdk/storage/Azure.Storage.Common/src/Shared/SasExtensions.cs
index 417cc5ffb11c..4f883ec03970 100644
--- a/sdk/storage/Azure.Storage.Common/src/Shared/SasExtensions.cs
+++ b/sdk/storage/Azure.Storage.Common/src/Shared/SasExtensions.cs
@@ -6,6 +6,7 @@
using System.Globalization;
using System.Net;
using System.Runtime.CompilerServices;
+using System.Security.Cryptography;
using System.Text;
namespace Azure.Storage.Sas
@@ -238,5 +239,21 @@ internal static string ValidateAndSanitizeRawPermissions(string permissions,
return stringBuilder.ToString();
}
+
+ ///
+ /// ComputeHMACSHA256 generates a base-64 hash signature string for an
+ /// HTTP request or for a SAS.
+ ///
+ ///
+ /// A UserDelegationKey.Value used to sign with a key
+ /// representing AD credentials.
+ ///
+ /// The message to sign.
+ /// The signed message.
+ internal static string ComputeHMACSHA256(string userDelegationKeyValue, string message) =>
+ Convert.ToBase64String(
+ new HMACSHA256(
+ Convert.FromBase64String(userDelegationKeyValue))
+ .ComputeHash(Encoding.UTF8.GetBytes(message)));
}
}
diff --git a/sdk/storage/Azure.Storage.Blobs/src/Sas/SasQueryParametersExtensions.cs b/sdk/storage/Azure.Storage.Common/src/Shared/SasQueryParametersExtensions.cs
similarity index 98%
rename from sdk/storage/Azure.Storage.Blobs/src/Sas/SasQueryParametersExtensions.cs
rename to sdk/storage/Azure.Storage.Common/src/Shared/SasQueryParametersExtensions.cs
index abbbcd1260b1..0321d5c1ec6f 100644
--- a/sdk/storage/Azure.Storage.Blobs/src/Sas/SasQueryParametersExtensions.cs
+++ b/sdk/storage/Azure.Storage.Common/src/Shared/SasQueryParametersExtensions.cs
@@ -26,6 +26,8 @@ internal static void ParseKeyProperties(
BlobSasQueryParameters
#elif DataLakeSDK
DataLakeSasQueryParameters
+#elif QueueSDK
+ QueueSasQueryParameters
#endif
parameters,
IDictionary values)
diff --git a/sdk/storage/Azure.Storage.Files.DataLake/src/Azure.Storage.Files.DataLake.csproj b/sdk/storage/Azure.Storage.Files.DataLake/src/Azure.Storage.Files.DataLake.csproj
index dad53a252550..b0e4071e98af 100644
--- a/sdk/storage/Azure.Storage.Files.DataLake/src/Azure.Storage.Files.DataLake.csproj
+++ b/sdk/storage/Azure.Storage.Files.DataLake/src/Azure.Storage.Files.DataLake.csproj
@@ -36,7 +36,7 @@
-
+
diff --git a/sdk/storage/Azure.Storage.Queues/api/Azure.Storage.Queues.net8.0.cs b/sdk/storage/Azure.Storage.Queues/api/Azure.Storage.Queues.net8.0.cs
index 434ab6611cf1..7f820302788c 100644
--- a/sdk/storage/Azure.Storage.Queues/api/Azure.Storage.Queues.net8.0.cs
+++ b/sdk/storage/Azure.Storage.Queues/api/Azure.Storage.Queues.net8.0.cs
@@ -42,6 +42,12 @@ public QueueClient(System.Uri queueUri, Azure.Storage.StorageSharedKeyCredential
public virtual System.Uri GenerateSasUri(Azure.Storage.Sas.QueueSasPermissions permissions, System.DateTimeOffset expiresOn) { throw null; }
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)]
public virtual System.Uri GenerateSasUri(Azure.Storage.Sas.QueueSasPermissions permissions, System.DateTimeOffset expiresOn, out string stringToSign) { throw null; }
+ public virtual System.Uri GenerateUserDelegationSasUri(Azure.Storage.Sas.QueueSasBuilder builder, Azure.Storage.Queues.Models.UserDelegationKey userDelegationKey) { throw null; }
+ [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)]
+ public virtual System.Uri GenerateUserDelegationSasUri(Azure.Storage.Sas.QueueSasBuilder builder, Azure.Storage.Queues.Models.UserDelegationKey userDelegationKey, out string stringToSign) { throw null; }
+ public virtual System.Uri GenerateUserDelegationSasUri(Azure.Storage.Sas.QueueSasPermissions permissions, System.DateTimeOffset expiresOn, Azure.Storage.Queues.Models.UserDelegationKey userDelegationKey) { throw null; }
+ [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)]
+ public virtual System.Uri GenerateUserDelegationSasUri(Azure.Storage.Sas.QueueSasPermissions permissions, System.DateTimeOffset expiresOn, Azure.Storage.Queues.Models.UserDelegationKey userDelegationKey, out string stringToSign) { throw null; }
public virtual Azure.Response> GetAccessPolicy(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
public virtual System.Threading.Tasks.Task>> GetAccessPolicyAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
protected internal virtual Azure.Storage.Queues.QueueServiceClient GetParentQueueServiceClientCore() { throw null; }
@@ -161,6 +167,8 @@ public QueueServiceClient(System.Uri serviceUri, Azure.Storage.StorageSharedKeyC
public virtual Azure.AsyncPageable GetQueuesAsync(Azure.Storage.Queues.Models.QueueTraits traits = Azure.Storage.Queues.Models.QueueTraits.None, string prefix = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
public virtual Azure.Response GetStatistics(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
public virtual System.Threading.Tasks.Task> GetStatisticsAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
+ public virtual Azure.Response GetUserDelegationKey(System.DateTimeOffset? startsOn, System.DateTimeOffset expiresOn, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
+ public virtual System.Threading.Tasks.Task> GetUserDelegationKeyAsync(System.DateTimeOffset? startsOn, System.DateTimeOffset expiresOn, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
public virtual Azure.Response SetProperties(Azure.Storage.Queues.Models.QueueServiceProperties properties, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
public virtual System.Threading.Tasks.Task SetPropertiesAsync(Azure.Storage.Queues.Models.QueueServiceProperties properties, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
}
@@ -425,6 +433,17 @@ internal UpdateReceipt() { }
public System.DateTimeOffset NextVisibleOn { get { throw null; } }
public string PopReceipt { get { throw null; } }
}
+ public partial class UserDelegationKey
+ {
+ internal UserDelegationKey() { }
+ public System.DateTimeOffset SignedExpiresOn { get { throw null; } }
+ public string SignedObjectId { get { throw null; } }
+ public string SignedService { get { throw null; } }
+ public System.DateTimeOffset SignedStartsOn { get { throw null; } }
+ public string SignedTenantId { get { throw null; } }
+ public string SignedVersion { get { throw null; } }
+ public string Value { get { throw null; } }
+ }
}
namespace Azure.Storage.Queues.Specialized
{
@@ -469,6 +488,7 @@ public partial class QueueSasBuilder
public QueueSasBuilder() { }
public QueueSasBuilder(Azure.Storage.Sas.QueueAccountSasPermissions permissions, System.DateTimeOffset expiresOn) { }
public QueueSasBuilder(Azure.Storage.Sas.QueueSasPermissions permissions, System.DateTimeOffset expiresOn) { }
+ public string DelegatedUserObjectId { get { throw null; } set { } }
public System.DateTimeOffset ExpiresOn { get { throw null; } set { } }
public string Identifier { get { throw null; } set { } }
public Azure.Storage.Sas.SasIPRange IPRange { get { throw null; } set { } }
@@ -486,6 +506,8 @@ public void SetPermissions(Azure.Storage.Sas.QueueAccountSasPermissions permissi
public void SetPermissions(Azure.Storage.Sas.QueueSasPermissions permissions) { }
public void SetPermissions(string rawPermissions) { }
public void SetPermissions(string rawPermissions, bool normalize = false) { }
+ public Azure.Storage.Sas.QueueSasQueryParameters ToSasQueryParameters(Azure.Storage.Queues.Models.UserDelegationKey userDelegationKey, string accountName) { throw null; }
+ public Azure.Storage.Sas.QueueSasQueryParameters ToSasQueryParameters(Azure.Storage.Queues.Models.UserDelegationKey userDelegationKey, string accountName, out string stringToSign) { throw null; }
public Azure.Storage.Sas.SasQueryParameters ToSasQueryParameters(Azure.Storage.StorageSharedKeyCredential sharedKeyCredential) { throw null; }
public Azure.Storage.Sas.SasQueryParameters ToSasQueryParameters(Azure.Storage.StorageSharedKeyCredential sharedKeyCredential, out string stringToSign) { throw null; }
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)]
@@ -500,6 +522,18 @@ public enum QueueSasPermissions
Update = 4,
Process = 8,
}
+ public sealed partial class QueueSasQueryParameters : Azure.Storage.Sas.SasQueryParameters
+ {
+ internal QueueSasQueryParameters() { }
+ public static new Azure.Storage.Sas.QueueSasQueryParameters Empty { get { throw null; } }
+ public System.DateTimeOffset KeyExpiresOn { get { throw null; } }
+ public string KeyObjectId { get { throw null; } }
+ public string KeyService { get { throw null; } }
+ public System.DateTimeOffset KeyStartsOn { get { throw null; } }
+ public string KeyTenantId { get { throw null; } }
+ public string KeyVersion { get { throw null; } }
+ public override string ToString() { throw null; }
+ }
}
namespace Microsoft.Extensions.Azure
{
diff --git a/sdk/storage/Azure.Storage.Queues/api/Azure.Storage.Queues.netstandard2.0.cs b/sdk/storage/Azure.Storage.Queues/api/Azure.Storage.Queues.netstandard2.0.cs
index 434ab6611cf1..7f820302788c 100644
--- a/sdk/storage/Azure.Storage.Queues/api/Azure.Storage.Queues.netstandard2.0.cs
+++ b/sdk/storage/Azure.Storage.Queues/api/Azure.Storage.Queues.netstandard2.0.cs
@@ -42,6 +42,12 @@ public QueueClient(System.Uri queueUri, Azure.Storage.StorageSharedKeyCredential
public virtual System.Uri GenerateSasUri(Azure.Storage.Sas.QueueSasPermissions permissions, System.DateTimeOffset expiresOn) { throw null; }
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)]
public virtual System.Uri GenerateSasUri(Azure.Storage.Sas.QueueSasPermissions permissions, System.DateTimeOffset expiresOn, out string stringToSign) { throw null; }
+ public virtual System.Uri GenerateUserDelegationSasUri(Azure.Storage.Sas.QueueSasBuilder builder, Azure.Storage.Queues.Models.UserDelegationKey userDelegationKey) { throw null; }
+ [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)]
+ public virtual System.Uri GenerateUserDelegationSasUri(Azure.Storage.Sas.QueueSasBuilder builder, Azure.Storage.Queues.Models.UserDelegationKey userDelegationKey, out string stringToSign) { throw null; }
+ public virtual System.Uri GenerateUserDelegationSasUri(Azure.Storage.Sas.QueueSasPermissions permissions, System.DateTimeOffset expiresOn, Azure.Storage.Queues.Models.UserDelegationKey userDelegationKey) { throw null; }
+ [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)]
+ public virtual System.Uri GenerateUserDelegationSasUri(Azure.Storage.Sas.QueueSasPermissions permissions, System.DateTimeOffset expiresOn, Azure.Storage.Queues.Models.UserDelegationKey userDelegationKey, out string stringToSign) { throw null; }
public virtual Azure.Response> GetAccessPolicy(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
public virtual System.Threading.Tasks.Task>> GetAccessPolicyAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
protected internal virtual Azure.Storage.Queues.QueueServiceClient GetParentQueueServiceClientCore() { throw null; }
@@ -161,6 +167,8 @@ public QueueServiceClient(System.Uri serviceUri, Azure.Storage.StorageSharedKeyC
public virtual Azure.AsyncPageable GetQueuesAsync(Azure.Storage.Queues.Models.QueueTraits traits = Azure.Storage.Queues.Models.QueueTraits.None, string prefix = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
public virtual Azure.Response GetStatistics(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
public virtual System.Threading.Tasks.Task> GetStatisticsAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
+ public virtual Azure.Response GetUserDelegationKey(System.DateTimeOffset? startsOn, System.DateTimeOffset expiresOn, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
+ public virtual System.Threading.Tasks.Task> GetUserDelegationKeyAsync(System.DateTimeOffset? startsOn, System.DateTimeOffset expiresOn, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
public virtual Azure.Response SetProperties(Azure.Storage.Queues.Models.QueueServiceProperties properties, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
public virtual System.Threading.Tasks.Task SetPropertiesAsync(Azure.Storage.Queues.Models.QueueServiceProperties properties, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
}
@@ -425,6 +433,17 @@ internal UpdateReceipt() { }
public System.DateTimeOffset NextVisibleOn { get { throw null; } }
public string PopReceipt { get { throw null; } }
}
+ public partial class UserDelegationKey
+ {
+ internal UserDelegationKey() { }
+ public System.DateTimeOffset SignedExpiresOn { get { throw null; } }
+ public string SignedObjectId { get { throw null; } }
+ public string SignedService { get { throw null; } }
+ public System.DateTimeOffset SignedStartsOn { get { throw null; } }
+ public string SignedTenantId { get { throw null; } }
+ public string SignedVersion { get { throw null; } }
+ public string Value { get { throw null; } }
+ }
}
namespace Azure.Storage.Queues.Specialized
{
@@ -469,6 +488,7 @@ public partial class QueueSasBuilder
public QueueSasBuilder() { }
public QueueSasBuilder(Azure.Storage.Sas.QueueAccountSasPermissions permissions, System.DateTimeOffset expiresOn) { }
public QueueSasBuilder(Azure.Storage.Sas.QueueSasPermissions permissions, System.DateTimeOffset expiresOn) { }
+ public string DelegatedUserObjectId { get { throw null; } set { } }
public System.DateTimeOffset ExpiresOn { get { throw null; } set { } }
public string Identifier { get { throw null; } set { } }
public Azure.Storage.Sas.SasIPRange IPRange { get { throw null; } set { } }
@@ -486,6 +506,8 @@ public void SetPermissions(Azure.Storage.Sas.QueueAccountSasPermissions permissi
public void SetPermissions(Azure.Storage.Sas.QueueSasPermissions permissions) { }
public void SetPermissions(string rawPermissions) { }
public void SetPermissions(string rawPermissions, bool normalize = false) { }
+ public Azure.Storage.Sas.QueueSasQueryParameters ToSasQueryParameters(Azure.Storage.Queues.Models.UserDelegationKey userDelegationKey, string accountName) { throw null; }
+ public Azure.Storage.Sas.QueueSasQueryParameters ToSasQueryParameters(Azure.Storage.Queues.Models.UserDelegationKey userDelegationKey, string accountName, out string stringToSign) { throw null; }
public Azure.Storage.Sas.SasQueryParameters ToSasQueryParameters(Azure.Storage.StorageSharedKeyCredential sharedKeyCredential) { throw null; }
public Azure.Storage.Sas.SasQueryParameters ToSasQueryParameters(Azure.Storage.StorageSharedKeyCredential sharedKeyCredential, out string stringToSign) { throw null; }
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)]
@@ -500,6 +522,18 @@ public enum QueueSasPermissions
Update = 4,
Process = 8,
}
+ public sealed partial class QueueSasQueryParameters : Azure.Storage.Sas.SasQueryParameters
+ {
+ internal QueueSasQueryParameters() { }
+ public static new Azure.Storage.Sas.QueueSasQueryParameters Empty { get { throw null; } }
+ public System.DateTimeOffset KeyExpiresOn { get { throw null; } }
+ public string KeyObjectId { get { throw null; } }
+ public string KeyService { get { throw null; } }
+ public System.DateTimeOffset KeyStartsOn { get { throw null; } }
+ public string KeyTenantId { get { throw null; } }
+ public string KeyVersion { get { throw null; } }
+ public override string ToString() { throw null; }
+ }
}
namespace Microsoft.Extensions.Azure
{
diff --git a/sdk/storage/Azure.Storage.Queues/api/Azure.Storage.Queues.netstandard2.1.cs b/sdk/storage/Azure.Storage.Queues/api/Azure.Storage.Queues.netstandard2.1.cs
index 434ab6611cf1..7f820302788c 100644
--- a/sdk/storage/Azure.Storage.Queues/api/Azure.Storage.Queues.netstandard2.1.cs
+++ b/sdk/storage/Azure.Storage.Queues/api/Azure.Storage.Queues.netstandard2.1.cs
@@ -42,6 +42,12 @@ public QueueClient(System.Uri queueUri, Azure.Storage.StorageSharedKeyCredential
public virtual System.Uri GenerateSasUri(Azure.Storage.Sas.QueueSasPermissions permissions, System.DateTimeOffset expiresOn) { throw null; }
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)]
public virtual System.Uri GenerateSasUri(Azure.Storage.Sas.QueueSasPermissions permissions, System.DateTimeOffset expiresOn, out string stringToSign) { throw null; }
+ public virtual System.Uri GenerateUserDelegationSasUri(Azure.Storage.Sas.QueueSasBuilder builder, Azure.Storage.Queues.Models.UserDelegationKey userDelegationKey) { throw null; }
+ [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)]
+ public virtual System.Uri GenerateUserDelegationSasUri(Azure.Storage.Sas.QueueSasBuilder builder, Azure.Storage.Queues.Models.UserDelegationKey userDelegationKey, out string stringToSign) { throw null; }
+ public virtual System.Uri GenerateUserDelegationSasUri(Azure.Storage.Sas.QueueSasPermissions permissions, System.DateTimeOffset expiresOn, Azure.Storage.Queues.Models.UserDelegationKey userDelegationKey) { throw null; }
+ [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)]
+ public virtual System.Uri GenerateUserDelegationSasUri(Azure.Storage.Sas.QueueSasPermissions permissions, System.DateTimeOffset expiresOn, Azure.Storage.Queues.Models.UserDelegationKey userDelegationKey, out string stringToSign) { throw null; }
public virtual Azure.Response> GetAccessPolicy(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
public virtual System.Threading.Tasks.Task>> GetAccessPolicyAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
protected internal virtual Azure.Storage.Queues.QueueServiceClient GetParentQueueServiceClientCore() { throw null; }
@@ -161,6 +167,8 @@ public QueueServiceClient(System.Uri serviceUri, Azure.Storage.StorageSharedKeyC
public virtual Azure.AsyncPageable GetQueuesAsync(Azure.Storage.Queues.Models.QueueTraits traits = Azure.Storage.Queues.Models.QueueTraits.None, string prefix = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
public virtual Azure.Response GetStatistics(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
public virtual System.Threading.Tasks.Task> GetStatisticsAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
+ public virtual Azure.Response GetUserDelegationKey(System.DateTimeOffset? startsOn, System.DateTimeOffset expiresOn, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
+ public virtual System.Threading.Tasks.Task> GetUserDelegationKeyAsync(System.DateTimeOffset? startsOn, System.DateTimeOffset expiresOn, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
public virtual Azure.Response SetProperties(Azure.Storage.Queues.Models.QueueServiceProperties properties, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
public virtual System.Threading.Tasks.Task SetPropertiesAsync(Azure.Storage.Queues.Models.QueueServiceProperties properties, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
}
@@ -425,6 +433,17 @@ internal UpdateReceipt() { }
public System.DateTimeOffset NextVisibleOn { get { throw null; } }
public string PopReceipt { get { throw null; } }
}
+ public partial class UserDelegationKey
+ {
+ internal UserDelegationKey() { }
+ public System.DateTimeOffset SignedExpiresOn { get { throw null; } }
+ public string SignedObjectId { get { throw null; } }
+ public string SignedService { get { throw null; } }
+ public System.DateTimeOffset SignedStartsOn { get { throw null; } }
+ public string SignedTenantId { get { throw null; } }
+ public string SignedVersion { get { throw null; } }
+ public string Value { get { throw null; } }
+ }
}
namespace Azure.Storage.Queues.Specialized
{
@@ -469,6 +488,7 @@ public partial class QueueSasBuilder
public QueueSasBuilder() { }
public QueueSasBuilder(Azure.Storage.Sas.QueueAccountSasPermissions permissions, System.DateTimeOffset expiresOn) { }
public QueueSasBuilder(Azure.Storage.Sas.QueueSasPermissions permissions, System.DateTimeOffset expiresOn) { }
+ public string DelegatedUserObjectId { get { throw null; } set { } }
public System.DateTimeOffset ExpiresOn { get { throw null; } set { } }
public string Identifier { get { throw null; } set { } }
public Azure.Storage.Sas.SasIPRange IPRange { get { throw null; } set { } }
@@ -486,6 +506,8 @@ public void SetPermissions(Azure.Storage.Sas.QueueAccountSasPermissions permissi
public void SetPermissions(Azure.Storage.Sas.QueueSasPermissions permissions) { }
public void SetPermissions(string rawPermissions) { }
public void SetPermissions(string rawPermissions, bool normalize = false) { }
+ public Azure.Storage.Sas.QueueSasQueryParameters ToSasQueryParameters(Azure.Storage.Queues.Models.UserDelegationKey userDelegationKey, string accountName) { throw null; }
+ public Azure.Storage.Sas.QueueSasQueryParameters ToSasQueryParameters(Azure.Storage.Queues.Models.UserDelegationKey userDelegationKey, string accountName, out string stringToSign) { throw null; }
public Azure.Storage.Sas.SasQueryParameters ToSasQueryParameters(Azure.Storage.StorageSharedKeyCredential sharedKeyCredential) { throw null; }
public Azure.Storage.Sas.SasQueryParameters ToSasQueryParameters(Azure.Storage.StorageSharedKeyCredential sharedKeyCredential, out string stringToSign) { throw null; }
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)]
@@ -500,6 +522,18 @@ public enum QueueSasPermissions
Update = 4,
Process = 8,
}
+ public sealed partial class QueueSasQueryParameters : Azure.Storage.Sas.SasQueryParameters
+ {
+ internal QueueSasQueryParameters() { }
+ public static new Azure.Storage.Sas.QueueSasQueryParameters Empty { get { throw null; } }
+ public System.DateTimeOffset KeyExpiresOn { get { throw null; } }
+ public string KeyObjectId { get { throw null; } }
+ public string KeyService { get { throw null; } }
+ public System.DateTimeOffset KeyStartsOn { get { throw null; } }
+ public string KeyTenantId { get { throw null; } }
+ public string KeyVersion { get { throw null; } }
+ public override string ToString() { throw null; }
+ }
}
namespace Microsoft.Extensions.Azure
{
diff --git a/sdk/storage/Azure.Storage.Queues/assets.json b/sdk/storage/Azure.Storage.Queues/assets.json
index 83952061b930..2c2b66269895 100644
--- a/sdk/storage/Azure.Storage.Queues/assets.json
+++ b/sdk/storage/Azure.Storage.Queues/assets.json
@@ -2,5 +2,5 @@
"AssetsRepo": "Azure/azure-sdk-assets",
"AssetsRepoPrefixPath": "net",
"TagPrefix": "net/storage/Azure.Storage.Queues",
- "Tag": "net/storage/Azure.Storage.Queues_975e82f8f8"
+ "Tag": "net/storage/Azure.Storage.Queues_1b869deccf"
}
diff --git a/sdk/storage/Azure.Storage.Queues/src/Azure.Storage.Queues.csproj b/sdk/storage/Azure.Storage.Queues/src/Azure.Storage.Queues.csproj
index b5e934bc8b90..6851dc2eda87 100644
--- a/sdk/storage/Azure.Storage.Queues/src/Azure.Storage.Queues.csproj
+++ b/sdk/storage/Azure.Storage.Queues/src/Azure.Storage.Queues.csproj
@@ -73,5 +73,7 @@
+
+
diff --git a/sdk/storage/Azure.Storage.Queues/src/Generated/MessageIdRestClient.cs b/sdk/storage/Azure.Storage.Queues/src/Generated/MessageIdRestClient.cs
index 905db9baf0d3..2be78151d054 100644
--- a/sdk/storage/Azure.Storage.Queues/src/Generated/MessageIdRestClient.cs
+++ b/sdk/storage/Azure.Storage.Queues/src/Generated/MessageIdRestClient.cs
@@ -27,7 +27,7 @@ internal partial class MessageIdRestClient
/// The handler for diagnostic messaging in the client.
/// The HTTP pipeline for sending and receiving REST requests and responses.
/// The URL of the service account, queue or message that is the target of the desired operation.
- /// Specifies the version of the operation to use for this request. The default value is "2018-03-28".
+ /// Specifies the version of the operation to use for this request. The default value is "2026-02-06".
/// , , or is null.
public MessageIdRestClient(ClientDiagnostics clientDiagnostics, HttpPipeline pipeline, string url, string version)
{
diff --git a/sdk/storage/Azure.Storage.Queues/src/Generated/MessagesRestClient.cs b/sdk/storage/Azure.Storage.Queues/src/Generated/MessagesRestClient.cs
index e25d809efc91..880709e94889 100644
--- a/sdk/storage/Azure.Storage.Queues/src/Generated/MessagesRestClient.cs
+++ b/sdk/storage/Azure.Storage.Queues/src/Generated/MessagesRestClient.cs
@@ -29,7 +29,7 @@ internal partial class MessagesRestClient
/// The handler for diagnostic messaging in the client.
/// The HTTP pipeline for sending and receiving REST requests and responses.
/// The URL of the service account, queue or message that is the target of the desired operation.
- /// Specifies the version of the operation to use for this request. The default value is "2018-03-28".
+ /// Specifies the version of the operation to use for this request. The default value is "2026-02-06".
/// , , or is null.
public MessagesRestClient(ClientDiagnostics clientDiagnostics, HttpPipeline pipeline, string url, string version)
{
diff --git a/sdk/storage/Azure.Storage.Queues/src/Generated/Models/KeyInfo.Serialization.cs b/sdk/storage/Azure.Storage.Queues/src/Generated/Models/KeyInfo.Serialization.cs
new file mode 100644
index 000000000000..20b89e3c39e6
--- /dev/null
+++ b/sdk/storage/Azure.Storage.Queues/src/Generated/Models/KeyInfo.Serialization.cs
@@ -0,0 +1,37 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+//
+
+#nullable disable
+
+using System.Xml;
+using Azure.Core;
+using Azure.Storage.Common;
+
+namespace Azure.Storage.Queues.Models
+{
+ internal partial class KeyInfo : IXmlSerializable
+ {
+ void IXmlSerializable.Write(XmlWriter writer, string nameHint)
+ {
+ writer.WriteStartElement(nameHint ?? "KeyInfo");
+ if (Common.Optional.IsDefined(Start))
+ {
+ writer.WriteStartElement("Start");
+ writer.WriteValue(Start);
+ writer.WriteEndElement();
+ }
+ writer.WriteStartElement("Expiry");
+ writer.WriteValue(Expiry);
+ writer.WriteEndElement();
+ if (Common.Optional.IsDefined(DelegatedUserTid))
+ {
+ writer.WriteStartElement("DelegatedUserTid");
+ writer.WriteValue(DelegatedUserTid);
+ writer.WriteEndElement();
+ }
+ writer.WriteEndElement();
+ }
+ }
+}
diff --git a/sdk/storage/Azure.Storage.Queues/src/Generated/Models/KeyInfo.cs b/sdk/storage/Azure.Storage.Queues/src/Generated/Models/KeyInfo.cs
new file mode 100644
index 000000000000..1637826f094c
--- /dev/null
+++ b/sdk/storage/Azure.Storage.Queues/src/Generated/Models/KeyInfo.cs
@@ -0,0 +1,44 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+//
+
+#nullable disable
+
+using System;
+using Azure.Storage.Common;
+
+namespace Azure.Storage.Queues.Models
+{
+ /// Key information.
+ internal partial class KeyInfo
+ {
+ /// Initializes a new instance of .
+ /// The date-time the key expires in ISO 8601 UTC time.
+ /// is null.
+ public KeyInfo(string expiry)
+ {
+ Argument.AssertNotNull(expiry, nameof(expiry));
+
+ Expiry = expiry;
+ }
+
+ /// Initializes a new instance of .
+ /// The date-time the key is active in ISO 8601 UTC time.
+ /// The date-time the key expires in ISO 8601 UTC time.
+ /// Optional. String, The delegated user tenant ID in Entra ID.
+ internal KeyInfo(string start, string expiry, string delegatedUserTid)
+ {
+ Start = start;
+ Expiry = expiry;
+ DelegatedUserTid = delegatedUserTid;
+ }
+
+ /// The date-time the key is active in ISO 8601 UTC time.
+ public string Start { get; set; }
+ /// The date-time the key expires in ISO 8601 UTC time.
+ public string Expiry { get; }
+ /// Optional. String, The delegated user tenant ID in Entra ID.
+ public string DelegatedUserTid { get; set; }
+ }
+}
diff --git a/sdk/storage/Azure.Storage.Queues/src/Generated/Models/UserDelegationKey.Serialization.cs b/sdk/storage/Azure.Storage.Queues/src/Generated/Models/UserDelegationKey.Serialization.cs
new file mode 100644
index 000000000000..07b6f6633730
--- /dev/null
+++ b/sdk/storage/Azure.Storage.Queues/src/Generated/Models/UserDelegationKey.Serialization.cs
@@ -0,0 +1,63 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+//
+
+#nullable disable
+
+using System;
+using System.Xml.Linq;
+using Azure.Core;
+
+namespace Azure.Storage.Queues.Models
+{
+ public partial class UserDelegationKey
+ {
+ internal static UserDelegationKey DeserializeUserDelegationKey(XElement element)
+ {
+ string signedObjectId = default;
+ string signedTenantId = default;
+ DateTimeOffset signedStartsOn = default;
+ DateTimeOffset signedExpiresOn = default;
+ string signedService = default;
+ string signedVersion = default;
+ string value = default;
+ if (element.Element("SignedOid") is XElement signedOidElement)
+ {
+ signedObjectId = (string)signedOidElement;
+ }
+ if (element.Element("SignedTid") is XElement signedTidElement)
+ {
+ signedTenantId = (string)signedTidElement;
+ }
+ if (element.Element("SignedStart") is XElement signedStartElement)
+ {
+ signedStartsOn = signedStartElement.GetDateTimeOffsetValue("O");
+ }
+ if (element.Element("SignedExpiry") is XElement signedExpiryElement)
+ {
+ signedExpiresOn = signedExpiryElement.GetDateTimeOffsetValue("O");
+ }
+ if (element.Element("SignedService") is XElement signedServiceElement)
+ {
+ signedService = (string)signedServiceElement;
+ }
+ if (element.Element("SignedVersion") is XElement signedVersionElement)
+ {
+ signedVersion = (string)signedVersionElement;
+ }
+ if (element.Element("Value") is XElement valueElement)
+ {
+ value = (string)valueElement;
+ }
+ return new UserDelegationKey(
+ signedObjectId,
+ signedTenantId,
+ signedStartsOn,
+ signedExpiresOn,
+ signedService,
+ signedVersion,
+ value);
+ }
+ }
+}
diff --git a/sdk/storage/Azure.Storage.Queues/src/Generated/Models/UserDelegationKey.cs b/sdk/storage/Azure.Storage.Queues/src/Generated/Models/UserDelegationKey.cs
new file mode 100644
index 000000000000..dc341a1f1483
--- /dev/null
+++ b/sdk/storage/Azure.Storage.Queues/src/Generated/Models/UserDelegationKey.cs
@@ -0,0 +1,42 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+//
+
+#nullable disable
+
+using System;
+using Azure.Storage.Common;
+
+namespace Azure.Storage.Queues.Models
+{
+ /// A user delegation key.
+ public partial class UserDelegationKey
+ {
+ /// Initializes a new instance of .
+ /// The Azure Active Directory object ID in GUID format.
+ /// The Azure Active Directory tenant ID in GUID format.
+ /// The date-time the key is active.
+ /// The date-time the key expires.
+ /// Abbreviation of the Azure Storage service that accepts the key.
+ /// The service version that created the key.
+ /// The key as a base64 string.
+ /// , , , or is null.
+ internal UserDelegationKey(string signedObjectId, string signedTenantId, DateTimeOffset signedStartsOn, DateTimeOffset signedExpiresOn, string signedService, string signedVersion, string value)
+ {
+ Argument.AssertNotNull(signedObjectId, nameof(signedObjectId));
+ Argument.AssertNotNull(signedTenantId, nameof(signedTenantId));
+ Argument.AssertNotNull(signedService, nameof(signedService));
+ Argument.AssertNotNull(signedVersion, nameof(signedVersion));
+ Argument.AssertNotNull(value, nameof(value));
+
+ SignedObjectId = signedObjectId;
+ SignedTenantId = signedTenantId;
+ SignedStartsOn = signedStartsOn;
+ SignedExpiresOn = signedExpiresOn;
+ SignedService = signedService;
+ SignedVersion = signedVersion;
+ Value = value;
+ }
+ }
+}
diff --git a/sdk/storage/Azure.Storage.Queues/src/Generated/QueueRestClient.cs b/sdk/storage/Azure.Storage.Queues/src/Generated/QueueRestClient.cs
index 9fc3133267dd..74f77dafeb91 100644
--- a/sdk/storage/Azure.Storage.Queues/src/Generated/QueueRestClient.cs
+++ b/sdk/storage/Azure.Storage.Queues/src/Generated/QueueRestClient.cs
@@ -29,7 +29,7 @@ internal partial class QueueRestClient
/// The handler for diagnostic messaging in the client.
/// The HTTP pipeline for sending and receiving REST requests and responses.
/// The URL of the service account, queue or message that is the target of the desired operation.
- /// Specifies the version of the operation to use for this request. The default value is "2018-03-28".
+ /// Specifies the version of the operation to use for this request. The default value is "2026-02-06".
/// , , or is null.
public QueueRestClient(ClientDiagnostics clientDiagnostics, HttpPipeline pipeline, string url, string version)
{
diff --git a/sdk/storage/Azure.Storage.Queues/src/Generated/ServiceGetUserDelegationKeyHeaders.cs b/sdk/storage/Azure.Storage.Queues/src/Generated/ServiceGetUserDelegationKeyHeaders.cs
new file mode 100644
index 000000000000..c18a4a5fe94d
--- /dev/null
+++ b/sdk/storage/Azure.Storage.Queues/src/Generated/ServiceGetUserDelegationKeyHeaders.cs
@@ -0,0 +1,22 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+//
+
+#nullable disable
+
+using Azure.Core;
+
+namespace Azure.Storage.Queues
+{
+ internal partial class ServiceGetUserDelegationKeyHeaders
+ {
+ private readonly Response _response;
+ public ServiceGetUserDelegationKeyHeaders(Response response)
+ {
+ _response = response;
+ }
+ /// Indicates the version of the Blob service used to execute the request. This header is returned for requests made against version 2009-09-19 and above.
+ public string Version => _response.Headers.TryGetValue("x-ms-version", out string value) ? value : null;
+ }
+}
diff --git a/sdk/storage/Azure.Storage.Queues/src/Generated/ServiceRestClient.cs b/sdk/storage/Azure.Storage.Queues/src/Generated/ServiceRestClient.cs
index 773878c5be81..4002fe504c2b 100644
--- a/sdk/storage/Azure.Storage.Queues/src/Generated/ServiceRestClient.cs
+++ b/sdk/storage/Azure.Storage.Queues/src/Generated/ServiceRestClient.cs
@@ -30,7 +30,7 @@ internal partial class ServiceRestClient
/// The handler for diagnostic messaging in the client.
/// The HTTP pipeline for sending and receiving REST requests and responses.
/// The URL of the service account, queue or message that is the target of the desired operation.
- /// Specifies the version of the operation to use for this request. The default value is "2018-03-28".
+ /// Specifies the version of the operation to use for this request. The default value is "2026-02-06".
/// , , or is null.
public ServiceRestClient(ClientDiagnostics clientDiagnostics, HttpPipeline pipeline, string url, string version)
{
@@ -252,6 +252,94 @@ public ResponseWithHeaders
}
}
+ internal HttpMessage CreateGetUserDelegationKeyRequest(KeyInfo keyInfo, int? timeout)
+ {
+ var message = _pipeline.CreateMessage();
+ var request = message.Request;
+ request.Method = RequestMethod.Post;
+ var uri = new RawRequestUriBuilder();
+ uri.AppendRaw(_url, false);
+ uri.AppendPath("/", false);
+ uri.AppendQuery("restype", "service", true);
+ uri.AppendQuery("comp", "userdelegationkey", true);
+ if (timeout != null)
+ {
+ uri.AppendQuery("timeout", timeout.Value, true);
+ }
+ request.Uri = uri;
+ request.Headers.Add("x-ms-version", _version);
+ request.Headers.Add("Accept", "application/xml");
+ request.Headers.Add("Content-Type", "application/xml");
+ var content = new XmlWriterContent();
+ content.XmlWriter.WriteObjectValue(keyInfo, "KeyInfo");
+ request.Content = content;
+ return message;
+ }
+
+ /// Retrieves a user delegation key for the Queue service. This is only a valid operation when using bearer token authentication.
+ /// Key information.
+ /// The The timeout parameter is expressed in seconds. For more information, see <a href="https://learn.microsoft.com/rest/api/storageservices/setting-timeouts-for-queue-service-operations>Setting Timeouts for Queue Service Operations.</a>.
+ /// The cancellation token to use.
+ /// is null.
+ public async Task> GetUserDelegationKeyAsync(KeyInfo keyInfo, int? timeout = null, CancellationToken cancellationToken = default)
+ {
+ if (keyInfo == null)
+ {
+ throw new ArgumentNullException(nameof(keyInfo));
+ }
+
+ using var message = CreateGetUserDelegationKeyRequest(keyInfo, timeout);
+ await _pipeline.SendAsync(message, cancellationToken).ConfigureAwait(false);
+ var headers = new ServiceGetUserDelegationKeyHeaders(message.Response);
+ switch (message.Response.Status)
+ {
+ case 200:
+ {
+ UserDelegationKey value = default;
+ var document = XDocument.Load(message.Response.ContentStream, LoadOptions.PreserveWhitespace);
+ if (document.Element("UserDelegationKey") is XElement userDelegationKeyElement)
+ {
+ value = UserDelegationKey.DeserializeUserDelegationKey(userDelegationKeyElement);
+ }
+ return ResponseWithHeaders.FromValue(value, headers, message.Response);
+ }
+ default:
+ throw new RequestFailedException(message.Response);
+ }
+ }
+
+ /// Retrieves a user delegation key for the Queue service. This is only a valid operation when using bearer token authentication.
+ /// Key information.
+ /// The The timeout parameter is expressed in seconds. For more information, see <a href="https://learn.microsoft.com/rest/api/storageservices/setting-timeouts-for-queue-service-operations>Setting Timeouts for Queue Service Operations.</a>.
+ /// The cancellation token to use.
+ /// is null.
+ public ResponseWithHeaders GetUserDelegationKey(KeyInfo keyInfo, int? timeout = null, CancellationToken cancellationToken = default)
+ {
+ if (keyInfo == null)
+ {
+ throw new ArgumentNullException(nameof(keyInfo));
+ }
+
+ using var message = CreateGetUserDelegationKeyRequest(keyInfo, timeout);
+ _pipeline.Send(message, cancellationToken);
+ var headers = new ServiceGetUserDelegationKeyHeaders(message.Response);
+ switch (message.Response.Status)
+ {
+ case 200:
+ {
+ UserDelegationKey value = default;
+ var document = XDocument.Load(message.Response.ContentStream, LoadOptions.PreserveWhitespace);
+ if (document.Element("UserDelegationKey") is XElement userDelegationKeyElement)
+ {
+ value = UserDelegationKey.DeserializeUserDelegationKey(userDelegationKeyElement);
+ }
+ return ResponseWithHeaders.FromValue(value, headers, message.Response);
+ }
+ default:
+ throw new RequestFailedException(message.Response);
+ }
+ }
+
internal HttpMessage CreateListQueuesSegmentRequest(string prefix, string marker, int? maxresults, IEnumerable include, int? timeout)
{
var message = _pipeline.CreateMessage();
diff --git a/sdk/storage/Azure.Storage.Queues/src/Models/Internal/KeyInfo.cs b/sdk/storage/Azure.Storage.Queues/src/Models/Internal/KeyInfo.cs
new file mode 100644
index 000000000000..1d2c0b5992cc
--- /dev/null
+++ b/sdk/storage/Azure.Storage.Queues/src/Models/Internal/KeyInfo.cs
@@ -0,0 +1,9 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+namespace Azure.Storage.Queues.Models
+{
+ internal partial class KeyInfo
+ {
+ }
+}
diff --git a/sdk/storage/Azure.Storage.Queues/src/Models/UserDelegationKey.cs b/sdk/storage/Azure.Storage.Queues/src/Models/UserDelegationKey.cs
new file mode 100644
index 000000000000..56579552a153
--- /dev/null
+++ b/sdk/storage/Azure.Storage.Queues/src/Models/UserDelegationKey.cs
@@ -0,0 +1,58 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using System;
+using Azure.Core;
+
+namespace Azure.Storage.Queues.Models
+{
+ ///
+ /// A user delegation key.
+ ///
+ public partial class UserDelegationKey
+ {
+ ///
+ /// The Azure Active Directory object ID in GUID format.
+ ///
+ [CodeGenMember("SignedOid")]
+ public string SignedObjectId { get; internal set; }
+
+ ///
+ /// The Azure Active Directory tenant ID in GUID format.
+ ///
+ [CodeGenMember("SignedTid")]
+ public string SignedTenantId { get; internal set; }
+
+ ///
+ /// The date-time the key expires.
+ ///
+ [CodeGenMember("SignedExpiry")]
+ public DateTimeOffset SignedExpiresOn { get; internal set; }
+
+ ///
+ /// The date-time the key is active.
+ ///
+ [CodeGenMember("SignedStart")]
+ public DateTimeOffset SignedStartsOn { get; internal set; }
+
+ ///
+ /// Abbreviation of the Azure Storage service that accepts the key.
+ ///
+ public string SignedService { get; internal set; }
+
+ ///
+ /// The service version that created the key.
+ ///
+ public string SignedVersion { get; internal set; }
+
+ ///
+ /// The key as a base64 string.
+ ///
+ public string Value { get; internal set; }
+
+ ///
+ /// Constructor.
+ ///
+ internal UserDelegationKey() { }
+ }
+}
diff --git a/sdk/storage/Azure.Storage.Queues/src/QueueClient.cs b/sdk/storage/Azure.Storage.Queues/src/QueueClient.cs
index 5ede94c21ad2..b65bf20d6a40 100644
--- a/sdk/storage/Azure.Storage.Queues/src/QueueClient.cs
+++ b/sdk/storage/Azure.Storage.Queues/src/QueueClient.cs
@@ -3267,6 +3267,156 @@ public virtual Uri GenerateSasUri(
}
#endregion
+ #region GenerateUserDelegationSas
+ ///
+ /// The
+ /// returns a representing a Queue Service
+ /// Shared Access Signature (SAS) Uri based on the Client properties
+ /// and parameters passed. The SAS is signed by the user delegation key
+ /// that is passed in.
+ ///
+ /// For more information, see
+ ///
+ /// Creating an user delegation SAS.
+ ///
+ ///
+ /// Required. Specifies the list of permissions to be associated with the SAS.
+ /// See .
+ ///
+ ///
+ /// Required. Specifies the time at which the SAS becomes invalid. This field
+ /// must be omitted if it has been specified in an associated stored access policy.
+ ///
+ ///
+ /// Required. A returned from
+ /// .
+ ///
+ ///
+ /// A containing the SAS Uri.
+ ///
+ ///
+ /// A will be thrown if a failure occurs.
+ ///
+ [CallerShouldAudit("https://aka.ms/azsdk/callershouldaudit/storage-queues")]
+ public virtual Uri GenerateUserDelegationSasUri(QueueSasPermissions permissions, DateTimeOffset expiresOn, UserDelegationKey userDelegationKey) =>
+ GenerateUserDelegationSasUri(permissions, expiresOn, userDelegationKey, out _);
+
+ ///
+ /// The
+ /// returns a representing a Blob Container Service
+ /// Shared Access Signature (SAS) Uri based on the Client properties
+ /// and parameters passed. The SAS is signed by the user delegation key
+ /// that is passed in.
+ ///
+ /// For more information, see
+ ///
+ /// Creating an user delegation SAS.
+ ///
+ ///
+ /// Required. Specifies the list of permissions to be associated with the SAS.
+ /// See .
+ ///
+ ///
+ /// Required. Specifies the time at which the SAS becomes invalid. This field
+ /// must be omitted if it has been specified in an associated stored access policy.
+ ///
+ ///
+ /// Required. A returned from
+ /// .
+ ///
+ ///
+ /// For debugging purposes only. This string will be overwritten with the string to sign that was used to generate the SAS Uri.
+ ///
+ ///
+ /// A containing the SAS Uri.
+ ///
+ ///
+ /// A will be thrown if a failure occurs.
+ ///
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ [CallerShouldAudit("https://aka.ms/azsdk/callershouldaudit/storage-queues")]
+ public virtual Uri GenerateUserDelegationSasUri(QueueSasPermissions permissions, DateTimeOffset expiresOn, UserDelegationKey userDelegationKey, out string stringToSign) =>
+ GenerateUserDelegationSasUri(new QueueSasBuilder(permissions, expiresOn) { QueueName = Name }, userDelegationKey, out stringToSign);
+
+ ///
+ /// The
+ /// returns a representing a Queue Service
+ /// Shared Access Signature (SAS) Uri based on the Client properties
+ /// and builder passed. The SAS is signed by the user delegation key
+ /// that is passed in.
+ ///
+ /// For more information, see
+ ///
+ /// Creating an user delegation SAS.
+ ///
+ ///
+ /// Required. Used to generate a Shared Access Signature (SAS).
+ ///
+ ///
+ /// Required. A returned from
+ /// .
+ ///
+ ///
+ /// A containing the SAS Uri.
+ ///
+ ///
+ /// A will be thrown if a failure occurs.
+ ///
+ [CallerShouldAudit("https://aka.ms/azsdk/callershouldaudit/storage-queues")]
+ public virtual Uri GenerateUserDelegationSasUri(QueueSasBuilder builder, UserDelegationKey userDelegationKey) =>
+ GenerateUserDelegationSasUri(builder, userDelegationKey, out _);
+
+ ///
+ /// The
+ /// returns a representing a Queue Client Service
+ /// Shared Access Signature (SAS) Uri based on the Client properties
+ /// and builder passed. The SAS is signed by the user delegation key
+ /// that is passed in.
+ ///
+ /// For more information, see
+ ///
+ /// Creating an user delegation SAS.
+ ///
+ ///
+ /// Required. Used to generate a Shared Access Signature (SAS).
+ ///
+ ///
+ /// Required. A returned from
+ /// .
+ ///
+ ///
+ /// For debugging purposes only. This string will be overwritten with the string to sign that was used to generate the SAS Uri.
+ ///
+ ///
+ /// A containing the SAS Uri.
+ ///
+ ///
+ /// A will be thrown if a failure occurs.
+ ///
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ [CallerShouldAudit("https://aka.ms/azsdk/callershouldaudit/storage-queues")]
+ public virtual Uri GenerateUserDelegationSasUri(QueueSasBuilder builder, UserDelegationKey userDelegationKey, out string stringToSign)
+ {
+ builder = builder ?? throw Errors.ArgumentNull(nameof(builder));
+ userDelegationKey = userDelegationKey ?? throw Errors.ArgumentNull(nameof(userDelegationKey));
+
+ // Deep copy of builder so we don't modify the user's origial BlobSasBuilder.
+ builder = QueueSasBuilder.DeepCopy(builder);
+
+ SetBuilderAndValidate(builder);
+ if (string.IsNullOrEmpty(AccountName))
+ {
+ throw Errors.SasClientMissingData(nameof(AccountName));
+ }
+
+ QueueUriBuilder sasUri = new QueueUriBuilder(Uri)
+ {
+ Sas = builder.ToSasQueryParameters(userDelegationKey, AccountName, out stringToSign)
+ };
+ return sasUri.ToUri();
+ }
+ #endregion
+
#region Encoding
private static BinaryData ToBinaryData(string input)
{
@@ -3343,6 +3493,21 @@ protected internal virtual QueueServiceClient GetParentQueueServiceClientCore()
return _parentQueueServiceClient;
}
#endregion
+
+ private void SetBuilderAndValidate(QueueSasBuilder builder)
+ {
+ // Assign builder's ContainerName if it is null.
+ builder.QueueName ??= Name;
+
+ // Validate that builder is properly set
+ if (!builder.QueueName.Equals(Name, StringComparison.InvariantCulture))
+ {
+ throw Errors.SasNamesNotMatching(
+ nameof(builder.QueueName),
+ nameof(QueueSasBuilder),
+ nameof(Name));
+ }
+ }
}
}
diff --git a/sdk/storage/Azure.Storage.Queues/src/QueueServiceClient.cs b/sdk/storage/Azure.Storage.Queues/src/QueueServiceClient.cs
index b64f23a7fb98..3718413aecf7 100644
--- a/sdk/storage/Azure.Storage.Queues/src/QueueServiceClient.cs
+++ b/sdk/storage/Azure.Storage.Queues/src/QueueServiceClient.cs
@@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
+using System.Globalization;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
@@ -964,6 +965,191 @@ await GetQueueClient(queueName)
.ConfigureAwait(false);
#endregion DeleteQueue
+ #region GetUserDelegationKey
+ ///
+ /// The operation retrieves a
+ /// key that can be used to delegate Active Directory authorization to
+ /// shared access signatures created with .
+ ///
+ ///
+ /// Start time for the key's validity, with null indicating an
+ /// immediate start. The time should be specified in UTC.
+ ///
+ /// Note: If you set the start time to the current time, failures
+ /// might occur intermittently for the first few minutes. This is due to different
+ /// machines having slightly different current times (known as clock skew).
+ ///
+ ///
+ /// Expiration of the key's validity. The time should be specified
+ /// in UTC.
+ ///
+ ///
+ /// Optional to propagate
+ /// notifications that the operation should be cancelled.
+ ///
+ ///
+ /// A describing
+ /// the service replication statistics.
+ ///
+ ///
+ /// A will be thrown if
+ /// a failure occurs.
+ /// If multiple failures occur, an will be thrown,
+ /// containing each failure instance.
+ ///
+ [CallerShouldAudit("https://aka.ms/azsdk/callershouldaudit/storage-blobs")]
+ public virtual Response GetUserDelegationKey(
+ DateTimeOffset? startsOn,
+ DateTimeOffset expiresOn,
+ CancellationToken cancellationToken = default) =>
+ GetUserDelegationKeyInternal(
+ startsOn,
+ expiresOn,
+ false, // async
+ cancellationToken)
+ .EnsureCompleted();
+
+ ///
+ /// The operation retrieves a
+ /// key that can be used to delegate Active Directory authorization to
+ /// shared access signatures created with .
+ ///
+ ///
+ /// Start time for the key's validity, with null indicating an
+ /// immediate start. The time should be specified in UTC.
+ ///
+ /// Note: If you set the start time to the current time, failures
+ /// might occur intermittently for the first few minutes. This is due to different
+ /// machines having slightly different current times (known as clock skew).
+ ///
+ ///
+ /// Expiration of the key's validity. The time should be specified
+ /// in UTC.
+ ///
+ ///
+ /// Optional to propagate
+ /// notifications that the operation should be cancelled.
+ ///
+ ///
+ /// A describing
+ /// the service replication statistics.
+ ///
+ ///
+ /// A will be thrown if
+ /// a failure occurs.
+ /// If multiple failures occur, an will be thrown,
+ /// containing each failure instance.
+ ///
+ [CallerShouldAudit("https://aka.ms/azsdk/callershouldaudit/storage-blobs")]
+ public virtual async Task> GetUserDelegationKeyAsync(
+ DateTimeOffset? startsOn,
+ DateTimeOffset expiresOn,
+ CancellationToken cancellationToken = default) =>
+ await GetUserDelegationKeyInternal(
+ startsOn,
+ expiresOn,
+ true, // async
+ cancellationToken)
+ .ConfigureAwait(false);
+
+ ///
+ /// The operation retrieves a
+ /// key that can be used to delegate Active Directory authorization to
+ /// shared access signatures created with .
+ ///
+ ///
+ /// Start time for the key's validity, with null indicating an
+ /// immediate start. The time should be specified in UTC.
+ ///
+ /// Note: If you set the start time to the current time, failures
+ /// might occur intermittently for the first few minutes. This is due to different
+ /// machines having slightly different current times (known as clock skew).
+ ///
+ ///
+ /// Expiration of the key's validity. The time should be specified
+ /// in UTC.
+ ///
+ ///
+ /// Optional to propagate
+ /// notifications that the operation should be cancelled.
+ ///
+ ///
+ ///
+ /// A describing
+ /// the service replication statistics.
+ ///
+ ///
+ /// A will be thrown if
+ /// a failure occurs.
+ /// If multiple failures occur, an will be thrown,
+ /// containing each failure instance.
+ ///
+ private async Task> GetUserDelegationKeyInternal(
+ DateTimeOffset? startsOn,
+ DateTimeOffset expiresOn,
+ bool async,
+ CancellationToken cancellationToken)
+ {
+ using (ClientConfiguration.Pipeline.BeginLoggingScope(nameof(QueueServiceClient)))
+ {
+ ClientConfiguration.Pipeline.LogMethodEnter(nameof(QueueServiceClient), message: $"{nameof(Uri)}: {Uri}");
+
+ DiagnosticScope scope = ClientConfiguration.ClientDiagnostics.CreateScope($"{nameof(QueueServiceClient)}.{nameof(GetUserDelegationKey)}");
+
+ try
+ {
+ scope.Start();
+
+ if (startsOn.HasValue && startsOn.Value.Offset != TimeSpan.Zero)
+ {
+ throw Errors.InvalidDateTimeUtc(nameof(startsOn));
+ }
+
+ if (expiresOn.Offset != TimeSpan.Zero)
+ {
+ throw Errors.InvalidDateTimeUtc(nameof(expiresOn));
+ }
+
+ KeyInfo keyInfo = new KeyInfo(expiresOn.ToString(Constants.Iso8601Format, CultureInfo.InvariantCulture))
+ {
+ Start = startsOn?.ToString(Constants.Iso8601Format, CultureInfo.InvariantCulture)
+ };
+
+ ResponseWithHeaders response;
+
+ if (async)
+ {
+ response = await ServiceRestClient.GetUserDelegationKeyAsync(
+ keyInfo: keyInfo,
+ cancellationToken: cancellationToken)
+ .ConfigureAwait(false);
+ }
+ else
+ {
+ response = ServiceRestClient.GetUserDelegationKey(
+ keyInfo: keyInfo,
+ cancellationToken: cancellationToken);
+ }
+
+ return Response.FromValue(
+ response.Value,
+ response.GetRawResponse());
+ }
+ catch (Exception ex)
+ {
+ ClientConfiguration.Pipeline.LogException(ex);
+ scope.Failed(ex);
+ throw;
+ }
+ finally
+ {
+ ClientConfiguration.Pipeline.LogMethodExit(nameof(QueueServiceClient));
+ scope.Dispose();
+ }
+ }
+ }
+ #endregion GetUserDelegationKey
+
#region GenerateSas
///
/// The
diff --git a/sdk/storage/Azure.Storage.Queues/src/Sas/QueueSasBuilder.cs b/sdk/storage/Azure.Storage.Queues/src/Sas/QueueSasBuilder.cs
index 6706bb0d028f..0ea5cc419879 100644
--- a/sdk/storage/Azure.Storage.Queues/src/Sas/QueueSasBuilder.cs
+++ b/sdk/storage/Azure.Storage.Queues/src/Sas/QueueSasBuilder.cs
@@ -4,9 +4,12 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
+using System.Net.Mime;
using System.Text;
using Azure.Core;
using Azure.Storage.Queues;
+using Azure.Storage.Queues.Models;
+using static Azure.Storage.Constants.Sas;
namespace Azure.Storage.Sas
{
@@ -89,6 +92,13 @@ public class QueueSasBuilder
///
public string QueueName { get; set; }
+ ///
+ /// Optional. Beginning in version 2025-07-05, this value specifies the Entra ID of the user would is authorized to
+ /// use the resulting SAS URL. The resulting SAS URL must be used in conjunction with an Entra ID token that has been
+ /// issued to the user specified in this value.
+ ///
+ public string DelegatedUserObjectId { get; set; }
+
///
/// Initializes a new instance of the
/// class.
@@ -273,6 +283,98 @@ public SasQueryParameters ToSasQueryParameters(StorageSharedKeyCredential shared
return p;
}
+ ///
+ /// Use an account's to sign this
+ /// shared access signature values to produce the proper SAS query
+ /// parameters for authenticating requests.
+ ///
+ ///
+ /// A returned from
+ /// .
+ ///
+ /// The name of the storage account.
+ ///
+ /// The used for authenticating requests.
+ ///
+ [CallerShouldAudit("https://aka.ms/azsdk/callershouldaudit/storage-blobs")]
+ public QueueSasQueryParameters ToSasQueryParameters(UserDelegationKey userDelegationKey, string accountName)
+ => ToSasQueryParameters(userDelegationKey, accountName, out _);
+
+ ///
+ /// Use an account's to sign this
+ /// shared access signature values to produce the proper SAS query
+ /// parameters for authenticating requests.
+ ///
+ ///
+ /// A returned from
+ /// .
+ ///
+ /// The name of the storage account.
+ ///
+ ///
+ /// For debugging purposes only. This string will be overwritten with the string to sign that was used to generate the .
+ ///
+ /// The used for authenticating requests.
+ ///
+ [CallerShouldAudit("https://aka.ms/azsdk/callershouldaudit/storage-blobs")]
+ public QueueSasQueryParameters ToSasQueryParameters(UserDelegationKey userDelegationKey, string accountName, out string stringToSign)
+ {
+ userDelegationKey = userDelegationKey ?? throw Errors.ArgumentNull(nameof(userDelegationKey));
+
+ EnsureState();
+
+ stringToSign = ToStringToSign(userDelegationKey, accountName);
+
+ string signature = SasExtensions.ComputeHMACSHA256(userDelegationKey.Value, stringToSign);
+
+ QueueSasQueryParameters p = new QueueSasQueryParameters(
+ version: Version,
+ services: default,
+ resourceTypes: default,
+ protocol: Protocol,
+ startsOn: StartsOn,
+ expiresOn: ExpiresOn,
+ ipRange: IPRange,
+ identifier: null,
+ resource: Resource.Queue,
+ permissions: Permissions,
+ keyOid: userDelegationKey.SignedObjectId,
+ keyTid: userDelegationKey.SignedTenantId,
+ keyStart: userDelegationKey.SignedStartsOn,
+ keyExpiry: userDelegationKey.SignedExpiresOn,
+ keyService: userDelegationKey.SignedService,
+ keyVersion: userDelegationKey.SignedVersion,
+ delegatedUserObjectId: DelegatedUserObjectId,
+ signature: signature);
+ return p;
+ }
+
+ private string ToStringToSign(UserDelegationKey userDelegationKey, string accountName)
+ {
+ string startTime = SasExtensions.FormatTimesForSasSigning(StartsOn);
+ string expiryTime = SasExtensions.FormatTimesForSasSigning(ExpiresOn);
+ string signedStart = SasExtensions.FormatTimesForSasSigning(userDelegationKey.SignedStartsOn);
+ string signedExpiry = SasExtensions.FormatTimesForSasSigning(userDelegationKey.SignedExpiresOn);
+
+ // See http://msdn.microsoft.com/en-us/library/azure/dn140255.aspx
+ return string.Join("\n",
+ Permissions,
+ startTime,
+ expiryTime,
+ GetCanonicalName(accountName, QueueName ?? string.Empty),
+ userDelegationKey.SignedObjectId,
+ userDelegationKey.SignedTenantId,
+ signedStart,
+ signedExpiry,
+ userDelegationKey.SignedService,
+ userDelegationKey.SignedVersion,
+ null, // SignedKeyDelegatedUserTenantId, will be added in a future release.
+ DelegatedUserObjectId,
+ IPRange.ToString(),
+ SasExtensions.ToProtocolString(Protocol),
+ Version);
+ }
+
///
/// For debugging purposes only.
/// Returns the string to sign that will be used to generate the signature for the SAS URL.
diff --git a/sdk/storage/Azure.Storage.Queues/src/Sas/QueueSasQueryParameters.cs b/sdk/storage/Azure.Storage.Queues/src/Sas/QueueSasQueryParameters.cs
new file mode 100644
index 000000000000..44da5ec9d9a7
--- /dev/null
+++ b/sdk/storage/Azure.Storage.Queues/src/Sas/QueueSasQueryParameters.cs
@@ -0,0 +1,149 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Azure.Storage.Sas
+{
+ ///
+ /// A object represents the components
+ /// that make up an Azure Storage Shared Access Signature's query
+ /// parameters. You can construct a new instance using
+ /// .
+ ///
+ /// For more information,
+ ///
+ /// Create a service SAS.
+ ///
+ public sealed class QueueSasQueryParameters : SasQueryParameters
+ {
+ internal UserDelegationKeyProperties KeyProperties { get; set; }
+
+ ///
+ /// Gets the Azure Active Directory object ID in GUID format.
+ ///
+ public string KeyObjectId => KeyProperties?.ObjectId;
+
+ ///
+ /// Gets the Azure Active Directory tenant ID in GUID format
+ ///
+ public string KeyTenantId => KeyProperties?.TenantId;
+
+ ///
+ /// Gets the time at which the key becomes valid.
+ ///
+ public DateTimeOffset KeyStartsOn => KeyProperties == null ? default : KeyProperties.StartsOn;
+
+ ///
+ /// Gets the time at which the key becomes expires.
+ ///
+ public DateTimeOffset KeyExpiresOn => KeyProperties == null ? default : KeyProperties.ExpiresOn;
+
+ ///
+ /// Gets the Storage service that accepts the key.
+ ///
+ public string KeyService => KeyProperties?.Service;
+
+ ///
+ /// Gets the Storage service version that created the key.
+ ///
+ public string KeyVersion => KeyProperties?.Version;
+
+ ///
+ /// Gets empty shared access signature query parameters.
+ ///
+ public static new QueueSasQueryParameters Empty => new QueueSasQueryParameters();
+
+ internal QueueSasQueryParameters()
+ : base()
+ {
+ }
+
+ ///
+ /// Creates a new BlobSasQueryParameters instance.
+ ///
+ internal QueueSasQueryParameters(
+ string version,
+ AccountSasServices? services,
+ AccountSasResourceTypes? resourceTypes,
+ SasProtocol protocol,
+ DateTimeOffset startsOn,
+ DateTimeOffset expiresOn,
+ SasIPRange ipRange,
+ string identifier,
+ string resource,
+ string permissions,
+ string signature,
+ string keyOid = default,
+ string keyTid = default,
+ DateTimeOffset keyStart = default,
+ DateTimeOffset keyExpiry = default,
+ string keyService = default,
+ string keyVersion = default,
+ string delegatedUserObjectId = default)
+ : base(
+ version,
+ services,
+ resourceTypes,
+ protocol,
+ startsOn,
+ expiresOn,
+ ipRange,
+ identifier,
+ resource,
+ permissions,
+ signature,
+ cacheControl: null,
+ contentDisposition: null,
+ contentEncoding: null,
+ contentLanguage: null,
+ contentType: null,
+ authorizedAadObjectId: null,
+ unauthorizedAadObjectId: null,
+ correlationId: null,
+ directoryDepth: null,
+ encryptionScope: null,
+ delegatedUserObjectId)
+ {
+ KeyProperties = new UserDelegationKeyProperties
+ {
+ ObjectId = keyOid,
+ TenantId = keyTid,
+ StartsOn = keyStart,
+ ExpiresOn = keyExpiry,
+ Service = keyService,
+ Version = keyVersion
+ };
+ }
+
+ ///
+ /// Creates a new instance of the
+ /// type based on the supplied query parameters .
+ /// All SAS-related query parameters will be removed from
+ /// .
+ ///
+ /// URI query parameters
+ internal QueueSasQueryParameters(
+ IDictionary values)
+ : base(values)
+ {
+ this.ParseKeyProperties(values);
+ }
+
+ ///
+ /// Convert the SAS query parameters into a URL encoded query string.
+ ///
+ ///
+ /// A URL encoded query string representing the SAS.
+ ///
+ public override string ToString()
+ {
+ StringBuilder sb = new StringBuilder();
+ KeyProperties.AppendProperties(sb);
+ AppendProperties(sb);
+ return sb.ToString();
+ }
+ }
+}
diff --git a/sdk/storage/Azure.Storage.Queues/src/autorest.md b/sdk/storage/Azure.Storage.Queues/src/autorest.md
index cf00b5b15086..d4424b8d23b9 100644
--- a/sdk/storage/Azure.Storage.Queues/src/autorest.md
+++ b/sdk/storage/Azure.Storage.Queues/src/autorest.md
@@ -4,7 +4,7 @@ Run `dotnet build /t:GenerateCode` to generate code.
``` yaml
input-file:
- - https://raw.githubusercontent.com/Azure/azure-rest-api-specs/596d8d2a8c1c50bd6ebe60036143f4c4787fc816/specification/storage/data-plane/Microsoft.QueueStorage/stable/2018-03-28/queue.json
+ - Q:\src\azure-rest-api-specs\specification\storage\data-plane\Microsoft.QueueStorage\stable\2026-02-06\queue.json
generation1-convenience-client: true
# https://github.com/Azure/autorest/issues/4075
skip-semantics-validation: true
diff --git a/sdk/storage/Azure.Storage.Queues/tests/Azure.Storage.Queues.Tests.csproj b/sdk/storage/Azure.Storage.Queues/tests/Azure.Storage.Queues.Tests.csproj
index e0a6fab3c753..019c6084d49c 100644
--- a/sdk/storage/Azure.Storage.Queues/tests/Azure.Storage.Queues.Tests.csproj
+++ b/sdk/storage/Azure.Storage.Queues/tests/Azure.Storage.Queues.Tests.csproj
@@ -17,6 +17,7 @@
+
diff --git a/sdk/storage/Azure.Storage.Queues/tests/QueueClientTestFixtureAttribute.cs b/sdk/storage/Azure.Storage.Queues/tests/QueueClientTestFixtureAttribute.cs
index bbb8e93f0e3d..d98155ff8a3a 100644
--- a/sdk/storage/Azure.Storage.Queues/tests/QueueClientTestFixtureAttribute.cs
+++ b/sdk/storage/Azure.Storage.Queues/tests/QueueClientTestFixtureAttribute.cs
@@ -47,7 +47,7 @@ public QueueClientTestFixtureAttribute(params object[] additionalParameters)
additionalParameters: additionalParameters)
{
RecordingServiceVersion = StorageVersionExtensions.MaxVersion;
- LiveServiceVersions = new object[] { StorageVersionExtensions.LatestVersion, };
+ LiveServiceVersions = new object[] { StorageVersionExtensions.MaxVersion };
}
}
}
diff --git a/sdk/storage/Azure.Storage.Queues/tests/QueueSasBuilderTests.cs b/sdk/storage/Azure.Storage.Queues/tests/QueueSasBuilderTests.cs
index f5d5fb736aa6..0560d1620e88 100644
--- a/sdk/storage/Azure.Storage.Queues/tests/QueueSasBuilderTests.cs
+++ b/sdk/storage/Azure.Storage.Queues/tests/QueueSasBuilderTests.cs
@@ -2,8 +2,11 @@
// Licensed under the MIT License.
using System;
+using System.Security.Cryptography;
+using System.Text;
using System.Threading.Tasks;
using Azure.Core.TestFramework;
+using Azure.Storage.Queues.Models;
using Azure.Storage.Queues.Tests;
using Azure.Storage.Sas;
using Azure.Storage.Test;
@@ -21,6 +24,41 @@ public QueueSasBuilderTests(bool async, QueueClientOptions.ServiceVersion servic
{
}
+ [RecordedTest]
+ public void QueueSasBuilder_ToSasQueryParameters_IdentitySas()
+ {
+ // Arrange
+ var constants = TestConstants.Create(this);
+ string queueName = GetNewQueueName();
+ QueueSasBuilder queueSasBuilder = BuildQueueSasBuilder(constants, queueName);
+ string signature = BuildUserDelegationSignature(constants, queueName);
+ string stringToSign = null;
+
+ // Act
+ QueueSasQueryParameters sasQueryParameters = queueSasBuilder.ToSasQueryParameters(GetUserDelegationKey(constants), constants.Sas.Account, out stringToSign);
+
+ // Assert
+ Assert.AreEqual(SasQueryParametersInternals.DefaultSasVersionInternal, sasQueryParameters.Version);
+ Assert.IsNull(sasQueryParameters.Services);
+ Assert.IsNull(sasQueryParameters.ResourceTypes);
+ Assert.AreEqual(constants.Sas.Protocol, sasQueryParameters.Protocol);
+ Assert.AreEqual(constants.Sas.StartTime, sasQueryParameters.StartsOn);
+ Assert.AreEqual(constants.Sas.ExpiryTime, sasQueryParameters.ExpiresOn);
+ Assert.AreEqual(constants.Sas.IPRange, sasQueryParameters.IPRange);
+ Assert.AreEqual(String.Empty, sasQueryParameters.Identifier);
+ Assert.AreEqual(constants.Sas.KeyObjectId, sasQueryParameters.KeyObjectId);
+ Assert.AreEqual(constants.Sas.KeyTenantId, sasQueryParameters.KeyTenantId);
+ Assert.AreEqual(constants.Sas.KeyStart, sasQueryParameters.KeyStartsOn);
+ Assert.AreEqual(constants.Sas.KeyExpiry, sasQueryParameters.KeyExpiresOn);
+ Assert.AreEqual(constants.Sas.KeyService, sasQueryParameters.KeyService);
+ Assert.AreEqual(constants.Sas.KeyVersion, sasQueryParameters.KeyVersion);
+ Assert.AreEqual(Constants.Sas.Resource.Queue, sasQueryParameters.Resource);
+ Assert.AreEqual(Permissions, sasQueryParameters.Permissions);
+ Assert.AreEqual(constants.Sas.DelegatedObjectId, sasQueryParameters.DelegatedUserObjectId);
+ Assert.AreEqual(signature, sasQueryParameters.Signature);
+ Assert.IsNotNull(stringToSign);
+ }
+
[RecordedTest]
public void QueueSasBuilder_ToSasQueryParameters_VersionTest()
{
@@ -285,6 +323,7 @@ private QueueSasBuilder BuildQueueSasBuilder(TestConstants constants, string que
IPRange = constants.Sas.IPRange,
Identifier = constants.Sas.Identifier,
QueueName = queueName,
+ DelegatedUserObjectId = constants.Sas.DelegatedObjectId,
};
queueSasBuilder.SetPermissions(Permissions);
@@ -305,5 +344,45 @@ private string BuildSignature(TestConstants constants, string queueName)
return StorageSharedKeyCredentialInternals.ComputeSasSignature(constants.Sas.SharedKeyCredential, stringToSign);
}
+
+ private string BuildUserDelegationSignature(TestConstants constants, string queueName)
+ {
+ var stringToSign = string.Join("\n",
+ Permissions,
+ SasExtensions.FormatTimesForSasSigning(constants.Sas.StartTime),
+ SasExtensions.FormatTimesForSasSigning(constants.Sas.ExpiryTime),
+ "/queue/" + constants.Sas.Account + "/" + queueName,
+ constants.Sas.KeyObjectId,
+ constants.Sas.KeyTenantId,
+ SasExtensions.FormatTimesForSasSigning(constants.Sas.KeyStart),
+ SasExtensions.FormatTimesForSasSigning(constants.Sas.KeyExpiry),
+ constants.Sas.KeyService,
+ constants.Sas.KeyVersion,
+ null,
+ constants.Sas.DelegatedObjectId,
+ constants.Sas.IPRange.ToString(),
+ SasExtensions.ToProtocolString(SasProtocol.Https),
+ SasQueryParametersInternals.DefaultSasVersionInternal);
+
+ return ComputeHMACSHA256(constants.Sas.KeyValue, stringToSign);
+ }
+
+ private string ComputeHMACSHA256(string userDelegationKeyValue, string message) =>
+ Convert.ToBase64String(
+ new HMACSHA256(
+ Convert.FromBase64String(userDelegationKeyValue))
+ .ComputeHash(Encoding.UTF8.GetBytes(message)));
+
+ private static UserDelegationKey GetUserDelegationKey(TestConstants constants)
+ => new UserDelegationKey
+ {
+ SignedObjectId = constants.Sas.KeyObjectId,
+ SignedTenantId = constants.Sas.KeyTenantId,
+ SignedStartsOn = constants.Sas.KeyStart,
+ SignedExpiresOn = constants.Sas.KeyExpiry,
+ SignedService = constants.Sas.KeyService,
+ SignedVersion = constants.Sas.KeyVersion,
+ Value = constants.Sas.KeyValue
+ };
}
}
diff --git a/sdk/storage/Azure.Storage.Queues/tests/QueueSasTests.cs b/sdk/storage/Azure.Storage.Queues/tests/QueueSasTests.cs
index 25859c0b01d8..4b5639546352 100644
--- a/sdk/storage/Azure.Storage.Queues/tests/QueueSasTests.cs
+++ b/sdk/storage/Azure.Storage.Queues/tests/QueueSasTests.cs
@@ -3,12 +3,17 @@
using System;
using System.Collections.Generic;
+using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Text;
+using System.Threading;
using System.Threading.Tasks;
+using Azure.Core;
using Azure.Core.TestFramework;
+using Azure.Storage.Queues.Models;
using Azure.Storage.Queues.Specialized;
using Azure.Storage.Queues.Tests;
+using Azure.Storage.Sas;
using Azure.Storage.Test;
using Azure.Storage.Test.Shared;
using NUnit.Framework;
@@ -118,6 +123,132 @@ public async Task AccountSas_ServiceOrder(string services)
await InvokeAccountSasTest(services: services);
}
+ [RecordedTest]
+ [ServiceVersion(Min = QueueClientOptions.ServiceVersion.V2026_02_06)]
+ public async Task SendMessageAsync_UserDelegationSAS()
+ {
+ // Arrange
+ string queueName = GetNewQueueName();
+ QueueServiceClient service = GetServiceClient_OAuth();
+ await using DisposingQueue test = await GetTestQueueAsync(service);
+
+ Response userDelegationKeyResponse = await service.GetUserDelegationKeyAsync(
+ startsOn: null,
+ expiresOn: Recording.UtcNow.AddHours(1));
+
+ UserDelegationKey userDelegationKey = userDelegationKeyResponse.Value;
+
+ Uri queueUri = test.Queue.GenerateUserDelegationSasUri(QueueSasPermissions.All, Recording.UtcNow.AddHours(1), userDelegationKey);
+
+ QueueClient queueClient = InstrumentClient(new QueueClient(queueUri, GetOptions()));
+
+ // Act
+ Response response = await queueClient.SendMessageAsync(
+ messageText: GetNewString(),
+ visibilityTimeout: new TimeSpan(0, 0, 1),
+ timeToLive: new TimeSpan(1, 0, 0));
+
+ // Assert
+ Assert.NotNull(response.Value);
+ }
+
+ [RecordedTest]
+ [LiveOnly] // Cannot record Entra ID token
+ [ServiceVersion(Min = QueueClientOptions.ServiceVersion.V2026_02_06)]
+ public async Task SendMessageAsync_UserDelegationSAS_DelegatedObjectId()
+ {
+ // Arrange
+ QueueServiceClient service = GetServiceClient_OAuth();
+ await using DisposingQueue test = await GetTestQueueAsync(service);
+
+ Response userDelegationKeyResponse = await service.GetUserDelegationKeyAsync(
+ startsOn: null,
+ expiresOn: Recording.UtcNow.AddHours(1));
+
+ // We need to get the object ID from the token credential used to authenticate the request
+ TokenCredential tokenCredential = TestEnvironment.Credential;
+ AccessToken accessToken = await tokenCredential.GetTokenAsync(
+ new TokenRequestContext(Scopes),
+ CancellationToken.None);
+
+ JwtSecurityToken jwtSecurityToken = new JwtSecurityTokenHandler().ReadJwtToken(accessToken.Token);
+ jwtSecurityToken.Payload.TryGetValue(Constants.Sas.ObjectId, out object objectId);
+
+ UserDelegationKey userDelegationKey = userDelegationKeyResponse.Value;
+
+ QueueSasBuilder queueSasBuilder = new QueueSasBuilder(QueueSasPermissions.All, Recording.UtcNow.AddHours(1))
+ {
+ QueueName = test.Queue.Name,
+ DelegatedUserObjectId = objectId?.ToString()
+ };
+
+ QueueSasQueryParameters sasQueryParameters = queueSasBuilder.ToSasQueryParameters(userDelegationKey, service.AccountName, out string stringToSign);
+
+ QueueUriBuilder uriBuilder = new QueueUriBuilder(test.Queue.Uri)
+ {
+ Sas = sasQueryParameters
+ };
+
+ QueueClient identityQueueClient = InstrumentClient(new QueueClient(uriBuilder.ToUri(), TestEnvironment.Credential, GetOptions()));
+
+ // Act
+ Response response = await identityQueueClient.SendMessageAsync(
+ messageText: GetNewString(),
+ visibilityTimeout: new TimeSpan(0, 0, 1),
+ timeToLive: new TimeSpan(1, 0, 0));
+
+ // Assert
+ Assert.NotNull(response.Value);
+ }
+
+ [RecordedTest]
+ [LiveOnly] // Cannot record Entra ID token
+ [ServiceVersion(Min = QueueClientOptions.ServiceVersion.V2026_02_06)]
+ public async Task SendMessageAsync_UserDelegationSAS_DelegatedObjectId_Fail()
+ {
+ // Arrange
+ QueueServiceClient service = GetServiceClient_OAuth();
+ await using DisposingQueue test = await GetTestQueueAsync(service);
+
+ Response userDelegationKeyResponse = await service.GetUserDelegationKeyAsync(
+ startsOn: null,
+ expiresOn: Recording.UtcNow.AddHours(1));
+
+ // We need to get the object ID from the token credential used to authenticate the request
+ TokenCredential tokenCredential = TestEnvironment.Credential;
+ AccessToken accessToken = await tokenCredential.GetTokenAsync(
+ new TokenRequestContext(Scopes),
+ CancellationToken.None);
+
+ JwtSecurityToken jwtSecurityToken = new JwtSecurityTokenHandler().ReadJwtToken(accessToken.Token);
+ jwtSecurityToken.Payload.TryGetValue(Constants.Sas.ObjectId, out object objectId);
+
+ UserDelegationKey userDelegationKey = userDelegationKeyResponse.Value;
+
+ QueueSasBuilder queueSasBuilder = new QueueSasBuilder(QueueSasPermissions.All, Recording.UtcNow.AddHours(1))
+ {
+ QueueName = test.Queue.Name,
+ DelegatedUserObjectId = objectId?.ToString()
+ };
+
+ QueueSasQueryParameters sasQueryParameters = queueSasBuilder.ToSasQueryParameters(userDelegationKey, service.AccountName, out string stringToSign);
+
+ QueueUriBuilder uriBuilder = new QueueUriBuilder(test.Queue.Uri)
+ {
+ Sas = sasQueryParameters
+ };
+
+ QueueClient identityQueueClient = InstrumentClient(new QueueClient(uriBuilder.ToUri(), GetOptions()));
+
+ // Act
+ await TestHelper.AssertExpectedExceptionAsync(
+ identityQueueClient.SendMessageAsync(
+ messageText: GetNewString(),
+ visibilityTimeout: new TimeSpan(0, 0, 1),
+ timeToLive: new TimeSpan(1, 0, 0)),
+ e => Assert.AreEqual("AuthenticationFailed", e.ErrorCode));
+ }
+
// Creating Client from GetStorageClient
#region QueueServiceClient
private async Task InvokeAccountServiceToQueueSasTest(
diff --git a/sdk/storage/Azure.Storage.Queues/tests/ServiceClientTests.cs b/sdk/storage/Azure.Storage.Queues/tests/ServiceClientTests.cs
index fff33753243e..bedab12900e4 100644
--- a/sdk/storage/Azure.Storage.Queues/tests/ServiceClientTests.cs
+++ b/sdk/storage/Azure.Storage.Queues/tests/ServiceClientTests.cs
@@ -300,6 +300,53 @@ await TestHelper.AssertExpectedExceptionAsync(
e => Assert.AreEqual("OutOfRangeInput", e.ErrorCode));
}
+ [RecordedTest]
+ [ServiceVersion(Min = QueueClientOptions.ServiceVersion.V2026_02_06)]
+ public async Task GetUserDelegatioKey()
+ {
+ // Arrange
+ QueueServiceClient service = GetServiceClient_OAuth();
+
+ // Act
+ Response response = await service.GetUserDelegationKeyAsync(startsOn: null, expiresOn: Recording.UtcNow.AddHours(1));
+
+ // Assert
+ Assert.IsNotNull(response.Value);
+ }
+
+ [RecordedTest]
+ [ServiceVersion(Min = QueueClientOptions.ServiceVersion.V2026_02_06)]
+ public async Task GetUserDelegationKey_Error()
+ {
+ // Arrange
+ QueueServiceClient service = GetServiceClient_SharedKey();
+
+ // Act
+ await TestHelper.AssertExpectedExceptionAsync(
+ service.GetUserDelegationKeyAsync(startsOn: null, expiresOn: Recording.UtcNow.AddHours(1)),
+ e => Assert.AreEqual("AuthenticationFailed", e.ErrorCode));
+ }
+
+ [RecordedTest]
+ [ServiceVersion(Min = QueueClientOptions.ServiceVersion.V2026_02_06)]
+ public async Task GetUserDelegationKey_ArgumentException()
+ {
+ // Arrange
+ QueueServiceClient service = GetServiceClient_OAuth();
+
+ // Act
+ await TestHelper.AssertExpectedExceptionAsync(
+ service.GetUserDelegationKeyAsync(
+ startsOn: null,
+ // ensure the time used is not UTC, as DateTimeOffset.Now could actually be UTC based on OS settings
+ // Use a custom time zone so we aren't dependent on OS having specific standard time zone.
+ expiresOn: TimeZoneInfo.ConvertTime(
+ Recording.Now.AddHours(1),
+ TimeZoneInfo.CreateCustomTimeZone("Storage Test Custom Time Zone", TimeSpan.FromHours(-3), "CTZ", "CTZ"))),
+ e => Assert.AreEqual("expiresOn must be UTC", e.Message));
+ ;
+ }
+
#region Secondary Storage
[RecordedTest]
public async Task GetQueuesAsync_SecondaryStorageFirstRetrySuccessful()