Skip to content

Commit b3e61dd

Browse files
authored
Add AzureSasCredential (Azure#17636)
* 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.
1 parent 54b1d8a commit b3e61dd

File tree

9 files changed

+205
-1
lines changed

9 files changed

+205
-1
lines changed

sdk/core/Azure.Core/CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22

33
## 1.8.0-beta.1 (Unreleased)
44

5+
### Added
6+
- `AzureSasCredential` and its respective policy.
7+
58
## 1.7.0 (2020-12-14)
69

710
### New Features

sdk/core/Azure.Core/api/Azure.Core.net461.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,13 @@ public AzureKeyCredential(string key) { }
2222
public string Key { get { throw null; } }
2323
public void Update(string key) { }
2424
}
25+
public partial class AzureSasCredential
26+
{
27+
public AzureSasCredential(string signature) { }
28+
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)]
29+
public string Signature { get { throw null; } }
30+
public void Update(string signature) { }
31+
}
2532
[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
2633
public readonly partial struct ETag : System.IEquatable<Azure.ETag>
2734
{

sdk/core/Azure.Core/api/Azure.Core.net5.0.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,13 @@ public AzureKeyCredential(string key) { }
2222
public string Key { get { throw null; } }
2323
public void Update(string key) { }
2424
}
25+
public partial class AzureSasCredential
26+
{
27+
public AzureSasCredential(string signature) { }
28+
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)]
29+
public string Signature { get { throw null; } }
30+
public void Update(string signature) { }
31+
}
2532
[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
2633
public readonly partial struct ETag : System.IEquatable<Azure.ETag>
2734
{

sdk/core/Azure.Core/api/Azure.Core.netstandard2.0.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,13 @@ public AzureKeyCredential(string key) { }
2222
public string Key { get { throw null; } }
2323
public void Update(string key) { }
2424
}
25+
public partial class AzureSasCredential
26+
{
27+
public AzureSasCredential(string signature) { }
28+
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)]
29+
public string Signature { get { throw null; } }
30+
public void Update(string signature) { }
31+
}
2532
[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
2633
public readonly partial struct ETag : System.IEquatable<Azure.ETag>
2734
{

sdk/core/Azure.Core/src/Azure.Core.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
<Compile Remove="Shared\**\*.cs" />
2727
<Compile Include="Shared\Argument.cs" />
2828
<Compile Include="Shared\AzureKeyCredentialPolicy.cs" />
29+
<Compile Include="Shared\AzureSasCredentialSynchronousPolicy.cs" />
2930
<Compile Include="Shared\EventSourceEventFormatting.cs" />
3031
<Compile Include="Shared\HashCodeBuilder.cs" />
3132
<Compile Include="Shared\ClientDiagnostics.cs" />
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
using System.ComponentModel;
5+
using System.Threading;
6+
using Azure.Core;
7+
8+
namespace Azure
9+
{
10+
/// <summary>
11+
/// Shared access signature credential used to authenticate to an Azure Service.
12+
/// It provides the ability to update the shared access signature without creating a new client.
13+
/// </summary>
14+
public class AzureSasCredential
15+
{
16+
private string _signature;
17+
18+
/// <summary>
19+
/// Shared access signature used to authenticate to an Azure service.
20+
/// </summary>
21+
[EditorBrowsable(EditorBrowsableState.Never)]
22+
public string Signature
23+
{
24+
get => Volatile.Read(ref _signature);
25+
private set => Volatile.Write(ref _signature, value);
26+
}
27+
28+
/// <summary>
29+
/// Initializes a new instance of the <see cref="AzureSasCredential"/> class.
30+
/// </summary>
31+
/// <param name="signature">Shared access signature to use to authenticate with the Azure service.</param>
32+
/// <exception cref="System.ArgumentNullException">
33+
/// Thrown when the <paramref name="signature"/> is null.
34+
/// </exception>
35+
/// <exception cref="System.ArgumentException">
36+
/// Thrown when the <paramref name="signature"/> is empty.
37+
/// </exception>
38+
#pragma warning disable CS8618 // Non-nullable field is uninitialized. Consider declaring as nullable.
39+
public AzureSasCredential(string signature) => Update(signature);
40+
#pragma warning restore CS8618 // Non-nullable field is uninitialized. Consider declaring as nullable.
41+
42+
/// <summary>
43+
/// Updates the shared access signature.
44+
/// This is intended to be used when you've regenerated your shared access signature
45+
/// and want to update long lived clients.
46+
/// </summary>
47+
/// <param name="signature">Shared access signature to authenticate the service against.</param>
48+
/// <exception cref="System.ArgumentNullException">
49+
/// Thrown when the <paramref name="signature"/> is null.
50+
/// </exception>
51+
/// <exception cref="System.ArgumentException">
52+
/// Thrown when the <paramref name="signature"/> is empty.
53+
/// </exception>
54+
public void Update(string signature)
55+
{
56+
Argument.AssertNotNullOrWhiteSpace(signature, nameof(signature));
57+
Signature = signature;
58+
}
59+
}
60+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
using System;
5+
using Azure.Core.Pipeline;
6+
7+
namespace Azure.Core
8+
{
9+
internal class AzureSasCredentialSynchronousPolicy : HttpPipelineSynchronousPolicy
10+
{
11+
private readonly AzureSasCredential _credential;
12+
13+
/// <summary>
14+
/// Initializes a new instance of the <see cref="AzureSasCredentialSynchronousPolicy"/> class.
15+
/// </summary>
16+
/// <param name="credential">The <see cref="AzureSasCredentialSynchronousPolicy"/> used to authenticate requests.</param>
17+
public AzureSasCredentialSynchronousPolicy(AzureSasCredential credential)
18+
{
19+
Argument.AssertNotNull(credential, nameof(credential));
20+
_credential = credential;
21+
}
22+
23+
/// <inheritdoc/>
24+
public override void OnSendingRequest(HttpMessage message)
25+
{
26+
string query = message.Request.Uri.Query;
27+
string signature = _credential.Signature;
28+
if (signature.StartsWith("?", StringComparison.InvariantCulture))
29+
{
30+
signature = signature.Substring(1);
31+
}
32+
if (!query.Contains(signature))
33+
{
34+
query = string.IsNullOrEmpty(query) ? '?' + signature : query + '&' + signature;
35+
message.Request.Uri.Query = query;
36+
}
37+
38+
base.OnSendingRequest(message);
39+
}
40+
}
41+
}
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
using System.Threading;
5+
using System.Threading.Tasks;
6+
using Azure.Core.Pipeline;
7+
using Azure.Core.TestFramework;
8+
using NUnit.Framework;
9+
10+
namespace Azure.Core.Tests
11+
{
12+
public class AzureSasCredentialSynchronousPolicyTests : PolicyTestBase
13+
{
14+
[TestCase("sig=test_signature_value")]
15+
[TestCase("?sig=test_signature_value")]
16+
public async Task SetsSignatureEmptyQuery(string signatureValue)
17+
{
18+
var transport = new MockTransport(new MockResponse(200));
19+
var sasPolicy = new AzureSasCredentialSynchronousPolicy(new AzureSasCredential(signatureValue));
20+
21+
await SendGetRequest(transport, sasPolicy);
22+
23+
Assert.AreEqual("?sig=test_signature_value", transport.SingleRequest.Uri.Query);
24+
}
25+
26+
[TestCase("sig=test_signature_value")]
27+
[TestCase("?sig=test_signature_value")]
28+
public async Task SetsSignatureNonEmptyQuery(string signatureValue)
29+
{
30+
var transport = new MockTransport(new MockResponse(200));
31+
var sasPolicy = new AzureSasCredentialSynchronousPolicy(new AzureSasCredential(signatureValue));
32+
string query = "?foo=bar";
33+
34+
await SendGetRequest(transport, sasPolicy, query: query);
35+
36+
Assert.AreEqual($"?foo=bar&sig=test_signature_value", transport.SingleRequest.Uri.Query);
37+
}
38+
39+
[TestCase("sig=test_signature_value")]
40+
[TestCase("?sig=test_signature_value")]
41+
public async Task VerifyRetryEmptyQuery(string signatureValue)
42+
{
43+
var transport = new MockTransport(new MockResponse(200), new MockResponse(200));
44+
var sasPolicy = new AzureSasCredentialSynchronousPolicy(new AzureSasCredential(signatureValue));
45+
46+
using (Request request = transport.CreateRequest())
47+
{
48+
request.Method = RequestMethod.Get;
49+
var pipeline = new HttpPipeline(transport, new[] { sasPolicy });
50+
await pipeline.SendRequestAsync(request, CancellationToken.None);
51+
await pipeline.SendRequestAsync(request, CancellationToken.None);
52+
}
53+
54+
Assert.AreEqual("?sig=test_signature_value", transport.Requests[0].Uri.Query);
55+
}
56+
57+
[TestCase("sig=test_signature_value")]
58+
[TestCase("?sig=test_signature_value")]
59+
public async Task VerifyRetryNonEmptyQuery(string signatureValue)
60+
{
61+
var transport = new MockTransport(new MockResponse(200), new MockResponse(200));
62+
var sasPolicy = new AzureSasCredentialSynchronousPolicy(new AzureSasCredential(signatureValue));
63+
string query = "?foo=bar";
64+
65+
using (Request request = transport.CreateRequest())
66+
{
67+
request.Method = RequestMethod.Get;
68+
request.Uri.Query = query;
69+
var pipeline = new HttpPipeline(transport, new[] { sasPolicy });
70+
await pipeline.SendRequestAsync(request, CancellationToken.None);
71+
await pipeline.SendRequestAsync(request, CancellationToken.None);
72+
}
73+
74+
Assert.AreEqual("?foo=bar&sig=test_signature_value", transport.Requests[0].Uri.Query);
75+
}
76+
}
77+
}

sdk/core/Azure.Core/tests/PolicyTestBase.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,15 @@ namespace Azure.Core.Tests
1111
{
1212
public abstract class PolicyTestBase
1313
{
14-
protected static async Task<Response> SendGetRequest(HttpPipelineTransport transport, HttpPipelinePolicy policy, ResponseClassifier responseClassifier = null)
14+
protected static async Task<Response> SendGetRequest(HttpPipelineTransport transport, HttpPipelinePolicy policy, ResponseClassifier responseClassifier = null, string query = null)
1515
{
1616
Assert.IsInstanceOf<HttpPipelineSynchronousPolicy>(policy, "Use SyncAsyncPolicyTestBase base type for non-sync policies");
1717

1818
using (Request request = transport.CreateRequest())
1919
{
2020
request.Method = RequestMethod.Get;
2121
request.Uri.Reset(new Uri("http://example.com"));
22+
request.Uri.Query = query;
2223
var pipeline = new HttpPipeline(transport, new[] { policy }, responseClassifier);
2324
return await pipeline.SendRequestAsync(request, CancellationToken.None);
2425
}

0 commit comments

Comments
 (0)