Skip to content

Commit

Permalink
Add AzureSasCredential (Azure#17636)
Browse files Browse the repository at this point in the history
* Add AzureSasCredential

* corner case.

* api.

* doc update.

* less allocations.

* call base last.

* Revert "less allocations."

This reverts commit 6375606.

* validation feedback.

* policy rename.

* another attempt to reduce allocations.

* Revert "another attempt to reduce allocations."

This reverts commit 89cc867.

* changelog.
  • Loading branch information
kasobol-msft authored and annelo-msft committed Feb 17, 2021
1 parent 24d372f commit 4ff3601
Show file tree
Hide file tree
Showing 9 changed files with 205 additions and 1 deletion.
3 changes: 3 additions & 0 deletions sdk/core/Azure.Core/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

## 1.8.0-beta.1 (Unreleased)

### Added
- `AzureSasCredential` and its respective policy.

## 1.7.0 (2020-12-14)

### New Features
Expand Down
7 changes: 7 additions & 0 deletions sdk/core/Azure.Core/api/Azure.Core.net461.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,13 @@ public AzureKeyCredential(string key) { }
public string Key { get { throw null; } }
public void Update(string key) { }
}
public partial class AzureSasCredential
{
public AzureSasCredential(string signature) { }
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)]
public string Signature { get { throw null; } }
public void Update(string signature) { }
}
[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
public readonly partial struct ETag : System.IEquatable<Azure.ETag>
{
Expand Down
7 changes: 7 additions & 0 deletions sdk/core/Azure.Core/api/Azure.Core.net5.0.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,13 @@ public AzureKeyCredential(string key) { }
public string Key { get { throw null; } }
public void Update(string key) { }
}
public partial class AzureSasCredential
{
public AzureSasCredential(string signature) { }
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)]
public string Signature { get { throw null; } }
public void Update(string signature) { }
}
[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
public readonly partial struct ETag : System.IEquatable<Azure.ETag>
{
Expand Down
7 changes: 7 additions & 0 deletions sdk/core/Azure.Core/api/Azure.Core.netstandard2.0.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,13 @@ public AzureKeyCredential(string key) { }
public string Key { get { throw null; } }
public void Update(string key) { }
}
public partial class AzureSasCredential
{
public AzureSasCredential(string signature) { }
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)]
public string Signature { get { throw null; } }
public void Update(string signature) { }
}
[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
public readonly partial struct ETag : System.IEquatable<Azure.ETag>
{
Expand Down
1 change: 1 addition & 0 deletions sdk/core/Azure.Core/src/Azure.Core.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
<Compile Remove="Shared\**\*.cs" />
<Compile Include="Shared\Argument.cs" />
<Compile Include="Shared\AzureKeyCredentialPolicy.cs" />
<Compile Include="Shared\AzureSasCredentialSynchronousPolicy.cs" />
<Compile Include="Shared\EventSourceEventFormatting.cs" />
<Compile Include="Shared\HashCodeBuilder.cs" />
<Compile Include="Shared\ClientDiagnostics.cs" />
Expand Down
60 changes: 60 additions & 0 deletions sdk/core/Azure.Core/src/AzureSasCredential.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System.ComponentModel;
using System.Threading;
using Azure.Core;

namespace Azure
{
/// <summary>
/// Shared access signature credential used to authenticate to an Azure Service.
/// It provides the ability to update the shared access signature without creating a new client.
/// </summary>
public class AzureSasCredential
{
private string _signature;

/// <summary>
/// Shared access signature used to authenticate to an Azure service.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public string Signature
{
get => Volatile.Read(ref _signature);
private set => Volatile.Write(ref _signature, value);
}

/// <summary>
/// Initializes a new instance of the <see cref="AzureSasCredential"/> class.
/// </summary>
/// <param name="signature">Shared access signature to use to authenticate with the Azure service.</param>
/// <exception cref="System.ArgumentNullException">
/// Thrown when the <paramref name="signature"/> is null.
/// </exception>
/// <exception cref="System.ArgumentException">
/// Thrown when the <paramref name="signature"/> is empty.
/// </exception>
#pragma warning disable CS8618 // Non-nullable field is uninitialized. Consider declaring as nullable.
public AzureSasCredential(string signature) => Update(signature);
#pragma warning restore CS8618 // Non-nullable field is uninitialized. Consider declaring as nullable.

/// <summary>
/// Updates the shared access signature.
/// This is intended to be used when you've regenerated your shared access signature
/// and want to update long lived clients.
/// </summary>
/// <param name="signature">Shared access signature to authenticate the service against.</param>
/// <exception cref="System.ArgumentNullException">
/// Thrown when the <paramref name="signature"/> is null.
/// </exception>
/// <exception cref="System.ArgumentException">
/// Thrown when the <paramref name="signature"/> is empty.
/// </exception>
public void Update(string signature)
{
Argument.AssertNotNullOrWhiteSpace(signature, nameof(signature));
Signature = signature;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System;
using Azure.Core.Pipeline;

namespace Azure.Core
{
internal class AzureSasCredentialSynchronousPolicy : HttpPipelineSynchronousPolicy
{
private readonly AzureSasCredential _credential;

/// <summary>
/// Initializes a new instance of the <see cref="AzureSasCredentialSynchronousPolicy"/> class.
/// </summary>
/// <param name="credential">The <see cref="AzureSasCredentialSynchronousPolicy"/> used to authenticate requests.</param>
public AzureSasCredentialSynchronousPolicy(AzureSasCredential credential)
{
Argument.AssertNotNull(credential, nameof(credential));
_credential = credential;
}

/// <inheritdoc/>
public override void OnSendingRequest(HttpMessage message)
{
string query = message.Request.Uri.Query;
string signature = _credential.Signature;
if (signature.StartsWith("?", StringComparison.InvariantCulture))
{
signature = signature.Substring(1);
}
if (!query.Contains(signature))
{
query = string.IsNullOrEmpty(query) ? '?' + signature : query + '&' + signature;
message.Request.Uri.Query = query;
}

base.OnSendingRequest(message);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System.Threading;
using System.Threading.Tasks;
using Azure.Core.Pipeline;
using Azure.Core.TestFramework;
using NUnit.Framework;

namespace Azure.Core.Tests
{
public class AzureSasCredentialSynchronousPolicyTests : PolicyTestBase
{
[TestCase("sig=test_signature_value")]
[TestCase("?sig=test_signature_value")]
public async Task SetsSignatureEmptyQuery(string signatureValue)
{
var transport = new MockTransport(new MockResponse(200));
var sasPolicy = new AzureSasCredentialSynchronousPolicy(new AzureSasCredential(signatureValue));

await SendGetRequest(transport, sasPolicy);

Assert.AreEqual("?sig=test_signature_value", transport.SingleRequest.Uri.Query);
}

[TestCase("sig=test_signature_value")]
[TestCase("?sig=test_signature_value")]
public async Task SetsSignatureNonEmptyQuery(string signatureValue)
{
var transport = new MockTransport(new MockResponse(200));
var sasPolicy = new AzureSasCredentialSynchronousPolicy(new AzureSasCredential(signatureValue));
string query = "?foo=bar";

await SendGetRequest(transport, sasPolicy, query: query);

Assert.AreEqual($"?foo=bar&sig=test_signature_value", transport.SingleRequest.Uri.Query);
}

[TestCase("sig=test_signature_value")]
[TestCase("?sig=test_signature_value")]
public async Task VerifyRetryEmptyQuery(string signatureValue)
{
var transport = new MockTransport(new MockResponse(200), new MockResponse(200));
var sasPolicy = new AzureSasCredentialSynchronousPolicy(new AzureSasCredential(signatureValue));

using (Request request = transport.CreateRequest())
{
request.Method = RequestMethod.Get;
var pipeline = new HttpPipeline(transport, new[] { sasPolicy });
await pipeline.SendRequestAsync(request, CancellationToken.None);
await pipeline.SendRequestAsync(request, CancellationToken.None);
}

Assert.AreEqual("?sig=test_signature_value", transport.Requests[0].Uri.Query);
}

[TestCase("sig=test_signature_value")]
[TestCase("?sig=test_signature_value")]
public async Task VerifyRetryNonEmptyQuery(string signatureValue)
{
var transport = new MockTransport(new MockResponse(200), new MockResponse(200));
var sasPolicy = new AzureSasCredentialSynchronousPolicy(new AzureSasCredential(signatureValue));
string query = "?foo=bar";

using (Request request = transport.CreateRequest())
{
request.Method = RequestMethod.Get;
request.Uri.Query = query;
var pipeline = new HttpPipeline(transport, new[] { sasPolicy });
await pipeline.SendRequestAsync(request, CancellationToken.None);
await pipeline.SendRequestAsync(request, CancellationToken.None);
}

Assert.AreEqual("?foo=bar&sig=test_signature_value", transport.Requests[0].Uri.Query);
}
}
}
3 changes: 2 additions & 1 deletion sdk/core/Azure.Core/tests/PolicyTestBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,15 @@ namespace Azure.Core.Tests
{
public abstract class PolicyTestBase
{
protected static async Task<Response> SendGetRequest(HttpPipelineTransport transport, HttpPipelinePolicy policy, ResponseClassifier responseClassifier = null)
protected static async Task<Response> SendGetRequest(HttpPipelineTransport transport, HttpPipelinePolicy policy, ResponseClassifier responseClassifier = null, string query = null)
{
Assert.IsInstanceOf<HttpPipelineSynchronousPolicy>(policy, "Use SyncAsyncPolicyTestBase base type for non-sync policies");

using (Request request = transport.CreateRequest())
{
request.Method = RequestMethod.Get;
request.Uri.Reset(new Uri("http://example.com"));
request.Uri.Query = query;
var pipeline = new HttpPipeline(transport, new[] { policy }, responseClassifier);
return await pipeline.SendRequestAsync(request, CancellationToken.None);
}
Expand Down

0 comments on commit 4ff3601

Please sign in to comment.