Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -2001,6 +2001,8 @@ public BlobSasBuilder(Azure.Storage.Sas.BlobSasPermissions permissions, System.D
public string Permissions { get { throw null; } }
public string PreauthorizedAgentObjectId { get { throw null; } set { } }
public Azure.Storage.Sas.SasProtocol Protocol { get { throw null; } set { } }
public System.Collections.Generic.Dictionary<string, System.Collections.Generic.List<string>> RequestHeaders { get { throw null; } set { } }
public System.Collections.Generic.Dictionary<string, System.Collections.Generic.List<string>> RequestQueryParameters { get { throw null; } set { } }
public string Resource { get { throw null; } set { } }
public string Snapshot { get { throw null; } set { } }
public System.DateTimeOffset StartsOn { get { throw null; } set { } }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2001,6 +2001,8 @@ public BlobSasBuilder(Azure.Storage.Sas.BlobSasPermissions permissions, System.D
public string Permissions { get { throw null; } }
public string PreauthorizedAgentObjectId { get { throw null; } set { } }
public Azure.Storage.Sas.SasProtocol Protocol { get { throw null; } set { } }
public System.Collections.Generic.Dictionary<string, System.Collections.Generic.List<string>> RequestHeaders { get { throw null; } set { } }
public System.Collections.Generic.Dictionary<string, System.Collections.Generic.List<string>> RequestQueryParameters { get { throw null; } set { } }
public string Resource { get { throw null; } set { } }
public string Snapshot { get { throw null; } set { } }
public System.DateTimeOffset StartsOn { get { throw null; } set { } }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2001,6 +2001,8 @@ public BlobSasBuilder(Azure.Storage.Sas.BlobSasPermissions permissions, System.D
public string Permissions { get { throw null; } }
public string PreauthorizedAgentObjectId { get { throw null; } set { } }
public Azure.Storage.Sas.SasProtocol Protocol { get { throw null; } set { } }
public System.Collections.Generic.Dictionary<string, System.Collections.Generic.List<string>> RequestHeaders { get { throw null; } set { } }
public System.Collections.Generic.Dictionary<string, System.Collections.Generic.List<string>> RequestQueryParameters { get { throw null; } set { } }
public string Resource { get { throw null; } set { } }
public string Snapshot { get { throw null; } set { } }
public System.DateTimeOffset StartsOn { get { throw null; } set { } }
Expand Down
21 changes: 20 additions & 1 deletion sdk/storage/Azure.Storage.Blobs/src/Sas/BlobSasBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,18 @@ public class BlobSasBuilder
/// </summary>
public string DelegatedUserObjectId { get; set; }

/// <summary>
/// Optional. Custom Request Headers to include in the SAS. Any usage of the SAS must
/// include these headers and values in the request. A header may have multiple values.
/// </summary>
public Dictionary<string, List<string>> RequestHeaders { get; set; }

/// <summary>
/// Optional. Custom Request Query Parameters to include in the SAS. Any usage of the SAS must
/// include these query parameters and values in the request. A query parameter may have multiple values.
/// </summary>
public Dictionary<string, List<string>> RequestQueryParameters { get; set; }

/// <summary>
/// Initializes a new instance of the <see cref="BlobSasBuilder"/>
/// class.
Expand Down Expand Up @@ -462,6 +474,7 @@ public BlobSasQueryParameters ToSasQueryParameters(UserDelegationKey userDelegat
EnsureState();

stringToSign = ToStringToSign(userDelegationKey, accountName);
Console.WriteLine($"StringToSign = \n{stringToSign}\nEND");
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Todo: Remove this Console log and others


string signature = SasExtensions.ComputeHMACSHA256(userDelegationKey.Value, stringToSign);

Expand Down Expand Up @@ -491,7 +504,9 @@ public BlobSasQueryParameters ToSasQueryParameters(UserDelegationKey userDelegat
authorizedAadObjectId: PreauthorizedAgentObjectId,
correlationId: CorrelationId,
encryptionScope: EncryptionScope,
delegatedUserObjectId: DelegatedUserObjectId);
delegatedUserObjectId: DelegatedUserObjectId,
requestHeaders: SasExtensions.ConvertRequestDictToKeyList(RequestHeaders),
requestQueryParameters: SasExtensions.ConvertRequestDictToKeyList(RequestQueryParameters));
return p;
}

Expand All @@ -501,6 +516,8 @@ private string ToStringToSign(UserDelegationKey userDelegationKey, string accoun
string expiryTime = SasExtensions.FormatTimesForSasSigning(ExpiresOn);
string signedStart = SasExtensions.FormatTimesForSasSigning(userDelegationKey.SignedStartsOn);
string signedExpiry = SasExtensions.FormatTimesForSasSigning(userDelegationKey.SignedExpiresOn);
string canonicalizedSignedRequestHeaders = SasExtensions.FormatRequestHeadersForSasSigning(RequestHeaders);
string canonicalizedSignedRequestQueryParameters = SasExtensions.FormatRequestQueryParametersForSasSigning(RequestQueryParameters);

// See http://msdn.microsoft.com/en-us/library/azure/dn140255.aspx
return string.Join("\n",
Expand All @@ -525,6 +542,8 @@ private string ToStringToSign(UserDelegationKey userDelegationKey, string accoun
Resource,
Snapshot ?? BlobVersionId,
EncryptionScope,
canonicalizedSignedRequestHeaders,
canonicalizedSignedRequestQueryParameters,
CacheControl,
ContentDisposition,
ContentEncoding,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ internal BlobSasQueryParameters()
/// <summary>
/// Creates a new BlobSasQueryParameters instance.
/// </summary>
internal BlobSasQueryParameters (
internal BlobSasQueryParameters(
string version,
AccountSasServices? services,
AccountSasResourceTypes? resourceTypes,
Expand All @@ -91,7 +91,9 @@ internal BlobSasQueryParameters (
string unauthorizedAadObjectId = default,
string correlationId = default,
string encryptionScope = default,
string delegatedUserObjectId = default)
string delegatedUserObjectId = default,
List<string> requestHeaders = default,
List<string> requestQueryParameters = default)
: base(
version,
services,
Expand All @@ -114,7 +116,9 @@ internal BlobSasQueryParameters (
correlationId,
directoryDepth: null,
encryptionScope,
delegatedUserObjectId)
delegatedUserObjectId,
requestHeaders,
requestQueryParameters)
{
KeyProperties = new UserDelegationKeyProperties
{
Expand All @@ -134,7 +138,7 @@ internal BlobSasQueryParameters (
/// <paramref name="values"/>.
/// </summary>
/// <param name="values">URI query parameters</param>
internal BlobSasQueryParameters (
internal BlobSasQueryParameters(
IDictionary<string, string> values)
: base(values)
{
Expand Down
38 changes: 35 additions & 3 deletions sdk/storage/Azure.Storage.Blobs/tests/BlobSasBuilderTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ public void ToSasQueryParameters_ContainerTest()
includeBlob: false,
includeSnapshot: false,
includeDelegatedObjectId: false,
includeRequestHeaders: false,
includeRequestQueryParameters: false,
containerName,
blobName,
constants);
Expand All @@ -77,7 +79,7 @@ public void ToSasQueryParameters_ContainerTest()
}

[RecordedTest]
[ServiceVersion(Min = BlobClientOptions.ServiceVersion.V2020_12_06)]
[ServiceVersion(Min = BlobClientOptions.ServiceVersion.V2026_04_06)]
public void ToSasQueryParameters_ContainerIdentityTest()
{
// Arrange
Expand All @@ -88,6 +90,8 @@ public void ToSasQueryParameters_ContainerIdentityTest()
includeBlob: false,
includeSnapshot: false,
includeDelegatedObjectId: true,
includeRequestHeaders: true,
includeRequestQueryParameters: true,
containerName,
blobName,
constants);
Expand Down Expand Up @@ -115,6 +119,8 @@ public void ToSasQueryParameters_ContainerIdentityTest()
Assert.AreEqual(Constants.Sas.Resource.Container, sasQueryParameters.Resource);
Assert.AreEqual(Permissions, sasQueryParameters.Permissions);
Assert.AreEqual(constants.Sas.DelegatedObjectId, sasQueryParameters.DelegatedUserObjectId);
Assert.AreEqual(SasExtensions.ConvertRequestDictToKeyList(constants.Sas.RequestHeaders), sasQueryParameters.RequestHeaders);
Assert.AreEqual(SasExtensions.ConvertRequestDictToKeyList(constants.Sas.RequestQueryParameters), sasQueryParameters.RequestQueryParameters);
Assert.AreEqual(signature, sasQueryParameters.Signature);
AssertResponseHeaders(constants, sasQueryParameters);
Assert.IsNotNull(stringToSign);
Expand All @@ -132,6 +138,8 @@ public void ToSasQueryParameters_BlobTest()
includeBlob: true,
includeSnapshot: false,
includeDelegatedObjectId: false,
includeRequestHeaders: false,
includeRequestQueryParameters: false,
containerName,
blobName,
constants);
Expand All @@ -158,7 +166,7 @@ public void ToSasQueryParameters_BlobTest()
}

[RecordedTest]
[ServiceVersion(Min = BlobClientOptions.ServiceVersion.V2026_02_06)]
[ServiceVersion(Min = BlobClientOptions.ServiceVersion.V2026_04_06)]
public void ToSasQueryParameters_BlobIdentityTest()
{
// Arrange
Expand All @@ -169,6 +177,8 @@ public void ToSasQueryParameters_BlobIdentityTest()
includeBlob: true,
includeSnapshot: false,
includeDelegatedObjectId: true,
includeRequestHeaders: true,
includeRequestQueryParameters: true,
containerName,
blobName,
constants);
Expand All @@ -195,6 +205,8 @@ public void ToSasQueryParameters_BlobIdentityTest()
Assert.AreEqual(Constants.Sas.Resource.Blob, sasQueryParameters.Resource);
Assert.AreEqual(Permissions, sasQueryParameters.Permissions);
Assert.AreEqual(constants.Sas.DelegatedObjectId, sasQueryParameters.DelegatedUserObjectId);
Assert.AreEqual(SasExtensions.ConvertRequestDictToKeyList(constants.Sas.RequestHeaders), sasQueryParameters.RequestHeaders);
Assert.AreEqual(SasExtensions.ConvertRequestDictToKeyList(constants.Sas.RequestQueryParameters), sasQueryParameters.RequestQueryParameters);
Assert.AreEqual(signature, sasQueryParameters.Signature);
AssertResponseHeaders(constants, sasQueryParameters);
}
Expand All @@ -211,6 +223,8 @@ public void ToSasQueryParameters_SnapshotTest()
includeBlob: true,
includeSnapshot: true,
includeDelegatedObjectId: false,
includeRequestHeaders: false,
includeRequestQueryParameters: false,
containerName,
blobName,
constants);
Expand All @@ -235,7 +249,7 @@ public void ToSasQueryParameters_SnapshotTest()
}

[RecordedTest]
[ServiceVersion(Min = BlobClientOptions.ServiceVersion.V2020_12_06)]
[ServiceVersion(Min = BlobClientOptions.ServiceVersion.V2026_04_06)]
public void ToSasQueryParameters_SnapshotIdentityTest()
{
// Arrange
Expand All @@ -246,6 +260,8 @@ public void ToSasQueryParameters_SnapshotIdentityTest()
includeBlob: true,
includeSnapshot: true,
includeDelegatedObjectId: true,
includeRequestHeaders: true,
includeRequestQueryParameters: true,
containerName,
blobName,
constants);
Expand Down Expand Up @@ -277,6 +293,8 @@ public void ToSasQueryParameters_SnapshotIdentityTest()
Assert.AreEqual(Constants.Sas.Resource.BlobSnapshot, sasQueryParameters.Resource);
Assert.AreEqual(Permissions, sasQueryParameters.Permissions);
Assert.AreEqual(constants.Sas.DelegatedObjectId, sasQueryParameters.DelegatedUserObjectId);
Assert.AreEqual(SasExtensions.ConvertRequestDictToKeyList(constants.Sas.RequestHeaders), sasQueryParameters.RequestHeaders);
Assert.AreEqual(SasExtensions.ConvertRequestDictToKeyList(constants.Sas.RequestQueryParameters), sasQueryParameters.RequestQueryParameters);
Assert.AreEqual(signature, sasQueryParameters.Signature);
AssertResponseHeaders(constants, sasQueryParameters);
}
Expand All @@ -292,6 +310,8 @@ public void ToSasQueryParameters_NullSharedKeyCredentialTest()
includeBlob: true,
includeSnapshot: true,
includeDelegatedObjectId: false,
includeRequestHeaders: false,
includeRequestQueryParameters: false,
containerName,
blobName,
constants);
Expand Down Expand Up @@ -507,6 +527,8 @@ private BlobSasBuilder BuildBlobSasBuilder(
bool includeBlob,
bool includeSnapshot,
bool includeDelegatedObjectId,
bool includeRequestHeaders,
bool includeRequestQueryParameters,
string containerName,
string blobName, TestConstants constants)
{
Expand All @@ -533,6 +555,14 @@ private BlobSasBuilder BuildBlobSasBuilder(
{
builder.DelegatedUserObjectId = constants.Sas.DelegatedObjectId;
}
if (includeRequestHeaders)
{
builder.RequestHeaders = constants.Sas.RequestHeaders;
}
if (includeRequestQueryParameters)
{
builder.RequestQueryParameters = constants.Sas.RequestQueryParameters;
}

builder.SetPermissions(BlobAccountSasPermissions.Read | BlobAccountSasPermissions.Write | BlobAccountSasPermissions.Delete);
return builder;
Expand Down Expand Up @@ -737,6 +767,8 @@ private string BuildIdentitySignature(
resource,
includeSnapshot ? Snapshot : null,
constants.Sas.EncryptionScope,
SasExtensions.FormatRequestHeadersForSasSigning(constants.Sas.RequestHeaders),
SasExtensions.FormatRequestQueryParametersForSasSigning(constants.Sas.RequestQueryParameters),
constants.Sas.CacheControl,
constants.Sas.ContentDisposition,
constants.Sas.ContentEncoding,
Expand Down
Loading