Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Storage] SAS Credential in Storage. #17646

Merged
merged 32 commits into from
Jan 11, 2021
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
a4c5ebc
Add AzureSasCredential
kasobol-msft Dec 17, 2020
5fdb953
corner case.
kasobol-msft Dec 17, 2020
bb719f2
Merge remote-tracking branch 'upstream/master' into sas-credential
kasobol-msft Dec 17, 2020
61c3839
api.
kasobol-msft Dec 17, 2020
2504f78
core as project ref (todo undo this)
kasobol-msft Dec 18, 2020
fa6536d
first client.
kasobol-msft Dec 18, 2020
2bccb52
hack azure core in webjobs for now.
kasobol-msft Dec 18, 2020
bd20b54
api.
kasobol-msft Dec 18, 2020
fd86385
constructors.
kasobol-msft Dec 18, 2020
4b38c1e
blob tests.
kasobol-msft Dec 18, 2020
8becf87
datalake tests.
kasobol-msft Dec 18, 2020
e117feb
share tests + take out sas on share and service clients as there isn'…
kasobol-msft Dec 18, 2020
f35672e
queues tests.
kasobol-msft Dec 18, 2020
454586c
well that works. nvm.
kasobol-msft Dec 18, 2020
1740da6
remarks.
kasobol-msft Dec 28, 2020
d68dd4e
error message.
kasobol-msft Dec 28, 2020
0f83c9b
predicate shouldn't be optional.
kasobol-msft Dec 28, 2020
0aa98f0
merge master
kasobol-msft Jan 5, 2021
01c538b
post-merge tweaks.
kasobol-msft Jan 5, 2021
730a3b5
message about right UriBuilder.
kasobol-msft Jan 5, 2021
31d9c96
added uri validation.
kasobol-msft Jan 5, 2021
3389e39
changelog.
kasobol-msft Jan 5, 2021
39d059b
user delegation sas change.
kasobol-msft Jan 5, 2021
43dcaf6
batch.
kasobol-msft Jan 5, 2021
f8ab972
merge master.
kasobol-msft Jan 5, 2021
e33ca4c
this test doesn't work well in playback mode.
kasobol-msft Jan 5, 2021
f108a76
pr feedback.
kasobol-msft Jan 6, 2021
c35d5e2
comments.
kasobol-msft Jan 6, 2021
5975ee9
validation.
kasobol-msft Jan 6, 2021
f68c029
Merge remote-tracking branch 'upstream/master' into sas-credential-in…
kasobol-msft Jan 11, 2021
4deba35
undo project references workaround.
kasobol-msft Jan 11, 2021
4b59f79
undo webjobs project ref workaround.
kasobol-msft Jan 11, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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\AzureSasCredentialPolicy.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 signature without creating a new client.
/// </summary>
public class AzureSasCredential
{
private string _signature;

/// <summary>
/// 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">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 signature.
/// This is intended to be used when you've regenerated your signature
/// and want to update long lived clients.
/// </summary>
/// <param name="signature">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.AssertNotNullOrEmpty(signature, nameof(signature));
Signature = signature;
}
}
}
40 changes: 40 additions & 0 deletions sdk/core/Azure.Core/src/Shared/AzureSasCredentialPolicy.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System;
using Azure.Core.Pipeline;

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

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

/// <inheritdoc/>
public override void OnSendingRequest(HttpMessage message)
{
base.OnSendingRequest(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;
}
}
}
}
77 changes: 77 additions & 0 deletions sdk/core/Azure.Core/tests/AzureSasCredentialPolicyTests.cs
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 AzureSasCredentialPolicyTests : 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 AzureSasCredentialPolicy(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 AzureSasCredentialPolicy(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 AzureSasCredentialPolicy(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 AzureSasCredentialPolicy(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
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ public partial class BlobChangeFeedClient
protected BlobChangeFeedClient() { }
public BlobChangeFeedClient(string connectionString) { }
public BlobChangeFeedClient(string connectionString, Azure.Storage.Blobs.BlobClientOptions options) { }
public BlobChangeFeedClient(System.Uri serviceUri, Azure.AzureSasCredential credential, Azure.Storage.Blobs.BlobClientOptions options = null) { }
public BlobChangeFeedClient(System.Uri serviceUri, Azure.Core.TokenCredential credential, Azure.Storage.Blobs.BlobClientOptions options = null) { }
public BlobChangeFeedClient(System.Uri serviceUri, Azure.Storage.Blobs.BlobClientOptions options = null) { }
public BlobChangeFeedClient(System.Uri serviceUri, Azure.Storage.StorageSharedKeyCredential credential, Azure.Storage.Blobs.BlobClientOptions options = null) { }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,28 @@ public BlobChangeFeedClient(Uri serviceUri, StorageSharedKeyCredential credentia
_blobServiceClient = new BlobServiceClient(serviceUri, credential, options);
}

/// <summary>
/// Initializes a new instance of the <see cref="BlobChangeFeedClient"/>
/// class.
Copy link
Member

Choose a reason for hiding this comment

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

We should add a blurb to all these doc comments telling people to only use this overload if they need to roll SAS signatures.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

added remarks.

/// </summary>
/// <param name="serviceUri">
/// A <see cref="Uri"/> referencing the blob service.
/// This is likely to be similar to "https://{account_name}.blob.core.windows.net".
/// Must not contain shared access signature.
/// </param>
/// <param name="credential">
/// The shared access signature credential used to sign requests.
/// </param>
/// <param name="options">
/// Optional client options that define the transport pipeline
/// policies for authentication, retries, etc., that are applied to
/// every request.
/// </param>
public BlobChangeFeedClient(Uri serviceUri, AzureSasCredential credential, BlobClientOptions options = default)
{
_blobServiceClient = new BlobServiceClient(serviceUri, credential, options);
}

/// <summary>
/// Initializes a new instance of the <see cref="BlobChangeFeedClient"/>
/// class.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ public partial class BlobClient : Azure.Storage.Blobs.Specialized.BlobBaseClient
protected BlobClient() { }
public BlobClient(string connectionString, string blobContainerName, string blobName) { }
public BlobClient(string connectionString, string blobContainerName, string blobName, Azure.Storage.Blobs.BlobClientOptions options) { }
public BlobClient(System.Uri blobUri, Azure.AzureSasCredential credential, Azure.Storage.Blobs.BlobClientOptions options = null) { }
public BlobClient(System.Uri blobUri, Azure.Core.TokenCredential credential, Azure.Storage.Blobs.BlobClientOptions options = null) { }
public BlobClient(System.Uri blobUri, Azure.Storage.Blobs.BlobClientOptions options = null) { }
public BlobClient(System.Uri blobUri, Azure.Storage.StorageSharedKeyCredential credential, Azure.Storage.Blobs.BlobClientOptions options = null) { }
Expand Down Expand Up @@ -60,6 +61,7 @@ public partial class BlobContainerClient
protected BlobContainerClient() { }
public BlobContainerClient(string connectionString, string blobContainerName) { }
public BlobContainerClient(string connectionString, string blobContainerName, Azure.Storage.Blobs.BlobClientOptions options) { }
public BlobContainerClient(System.Uri blobContainerUri, Azure.AzureSasCredential credential, Azure.Storage.Blobs.BlobClientOptions options = null) { }
public BlobContainerClient(System.Uri blobContainerUri, Azure.Core.TokenCredential credential, Azure.Storage.Blobs.BlobClientOptions options = null) { }
public BlobContainerClient(System.Uri blobContainerUri, Azure.Storage.Blobs.BlobClientOptions options = null) { }
public BlobContainerClient(System.Uri blobContainerUri, Azure.Storage.StorageSharedKeyCredential credential, Azure.Storage.Blobs.BlobClientOptions options = null) { }
Expand Down Expand Up @@ -119,6 +121,7 @@ public partial class BlobServiceClient
protected BlobServiceClient() { }
public BlobServiceClient(string connectionString) { }
public BlobServiceClient(string connectionString, Azure.Storage.Blobs.BlobClientOptions options) { }
public BlobServiceClient(System.Uri serviceUri, Azure.AzureSasCredential credential, Azure.Storage.Blobs.BlobClientOptions options = null) { }
public BlobServiceClient(System.Uri serviceUri, Azure.Core.TokenCredential credential, Azure.Storage.Blobs.BlobClientOptions options = null) { }
public BlobServiceClient(System.Uri serviceUri, Azure.Storage.Blobs.BlobClientOptions options = null) { }
public BlobServiceClient(System.Uri serviceUri, Azure.Storage.StorageSharedKeyCredential credential, Azure.Storage.Blobs.BlobClientOptions options = null) { }
Expand Down Expand Up @@ -1224,6 +1227,7 @@ public partial class AppendBlobClient : Azure.Storage.Blobs.Specialized.BlobBase
protected AppendBlobClient() { }
public AppendBlobClient(string connectionString, string blobContainerName, string blobName) { }
public AppendBlobClient(string connectionString, string blobContainerName, string blobName, Azure.Storage.Blobs.BlobClientOptions options) { }
public AppendBlobClient(System.Uri blobUri, Azure.AzureSasCredential credential, Azure.Storage.Blobs.BlobClientOptions options = null) { }
public AppendBlobClient(System.Uri blobUri, Azure.Core.TokenCredential credential, Azure.Storage.Blobs.BlobClientOptions options = null) { }
public AppendBlobClient(System.Uri blobUri, Azure.Storage.Blobs.BlobClientOptions options = null) { }
public AppendBlobClient(System.Uri blobUri, Azure.Storage.StorageSharedKeyCredential credential, Azure.Storage.Blobs.BlobClientOptions options = null) { }
Expand Down Expand Up @@ -1257,6 +1261,7 @@ public partial class BlobBaseClient
protected BlobBaseClient() { }
public BlobBaseClient(string connectionString, string blobContainerName, string blobName) { }
public BlobBaseClient(string connectionString, string blobContainerName, string blobName, Azure.Storage.Blobs.BlobClientOptions options) { }
public BlobBaseClient(System.Uri blobUri, Azure.AzureSasCredential credential, Azure.Storage.Blobs.BlobClientOptions options = null) { }
public BlobBaseClient(System.Uri blobUri, Azure.Core.TokenCredential credential, Azure.Storage.Blobs.BlobClientOptions options = null) { }
public BlobBaseClient(System.Uri blobUri, Azure.Storage.Blobs.BlobClientOptions options = null) { }
public BlobBaseClient(System.Uri blobUri, Azure.Storage.StorageSharedKeyCredential credential, Azure.Storage.Blobs.BlobClientOptions options = null) { }
Expand Down Expand Up @@ -1361,6 +1366,7 @@ public partial class BlockBlobClient : Azure.Storage.Blobs.Specialized.BlobBaseC
protected BlockBlobClient() { }
public BlockBlobClient(string connectionString, string containerName, string blobName) { }
public BlockBlobClient(string connectionString, string blobContainerName, string blobName, Azure.Storage.Blobs.BlobClientOptions options) { }
public BlockBlobClient(System.Uri blobUri, Azure.AzureSasCredential credential, Azure.Storage.Blobs.BlobClientOptions options = null) { }
public BlockBlobClient(System.Uri blobUri, Azure.Core.TokenCredential credential, Azure.Storage.Blobs.BlobClientOptions options = null) { }
public BlockBlobClient(System.Uri blobUri, Azure.Storage.Blobs.BlobClientOptions options = null) { }
public BlockBlobClient(System.Uri blobUri, Azure.Storage.StorageSharedKeyCredential credential, Azure.Storage.Blobs.BlobClientOptions options = null) { }
Expand Down Expand Up @@ -1407,6 +1413,7 @@ public partial class PageBlobClient : Azure.Storage.Blobs.Specialized.BlobBaseCl
protected PageBlobClient() { }
public PageBlobClient(string connectionString, string blobContainerName, string blobName) { }
public PageBlobClient(string connectionString, string blobContainerName, string blobName, Azure.Storage.Blobs.BlobClientOptions options) { }
public PageBlobClient(System.Uri blobUri, Azure.AzureSasCredential credential, Azure.Storage.Blobs.BlobClientOptions options = null) { }
public PageBlobClient(System.Uri blobUri, Azure.Core.TokenCredential credential, Azure.Storage.Blobs.BlobClientOptions options = null) { }
public PageBlobClient(System.Uri blobUri, Azure.Storage.Blobs.BlobClientOptions options = null) { }
public PageBlobClient(System.Uri blobUri, Azure.Storage.StorageSharedKeyCredential credential, Azure.Storage.Blobs.BlobClientOptions options = null) { }
Expand Down
Loading