-
Notifications
You must be signed in to change notification settings - Fork 4.9k
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
[Storage] SAS Credential in Storage. #17646
Conversation
…t much APIs that would work with sas there.
@@ -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. |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
added remarks.
=> new ArgumentException($"The resource URI {uri} must not contain SAS if AzureSasCredential is used." + | ||
$" You can use BlobUriBuilder to strip SAS from the resource URI before instantiating clients."); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about something like?
=> new ArgumentException($"The resource URI {uri} must not contain SAS if AzureSasCredential is used." + | |
$" You can use BlobUriBuilder to strip SAS from the resource URI before instantiating clients."); | |
=> new ArgumentException( | |
$"You cannot use {nameof(AzureSasCredential)} when the resource URI also contains a Shared Access Signature: {uri}\n" + | |
$"You can remove the shared access signature by creating a {nameof(BlobUriBuilder)}, setting {nameof(BlobUriBuilder.Sas)} to null, and calling {nameof(BlobUriBuilder.ToUri)}."); |
Anyone getting this exception is probably going to be confused. I'd like to put the URI after the core of the message so it's not lost behind a very long string.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds Good.
/// <returns>An authentication policy.</returns> | ||
public static HttpPipelinePolicy AsPolicy(this AzureSasCredential credential, Uri serviceUri) | ||
{ | ||
var queryParameters = serviceUri.GetQueryParameters(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you need to check if serviceUri
is null? I think this will get called before validation in base(...)
for some .ctors. Consider also taking in the name of the URI param so you can throw the most helpful exception.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I could. However, after writing test for that and wrapping this piece of code in null check I found that NullReferenceException
is thrown here.
Maybe we should add Argument.AssertNotNull
in both AsPolicy
and ctros ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added validations.
@@ -61,7 +61,24 @@ public static void AssertExpectedException<T>(Action action, T expectedException | |||
|
|||
public static void AssertExpectedException<T>(Action action, Func<T, bool> predicate = null) | |||
where T : Exception | |||
=> AssertExpectedException(action, default, GetDefaultExceptionAssertion<T>((_, a) => predicate(a))); | |||
{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should you flip it around and make the other version call this then with a predicate: _ => true
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Which other version do you mean? The one that takes expectedException
?
The problem here was that existing implementation of this overload ended up in this line attempting to assert exception message, which was deemed to always fail... This overload of AssertExpectedException
had zero callers because of that problem.
...torage.Scenario.Tests/tests/Microsoft.Azure.WebJobs.Extensions.Storage.Scenario.Tests.csproj
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about Blob Batch?
sdk/storage/Azure.Storage.Common/src/Azure.Storage.Common.csproj
Outdated
Show resolved
Hide resolved
sdk/storage/Azure.Storage.Common/tests/Azure.Storage.Common.Tests.csproj
Outdated
Show resolved
Hide resolved
The Batch should work without change as long as underlying service client is created with AzureSASCredential. Added test for that. |
@@ -228,6 +231,7 @@ public partial class DataLakePathClient | |||
public DataLakePathClient(string connectionString, string fileSystemName, string path) { } | |||
public DataLakePathClient(string connectionString, string fileSystemName, string path, Azure.Storage.Files.DataLake.DataLakeClientOptions options) { } | |||
public DataLakePathClient(System.Uri pathUri) { } | |||
public DataLakePathClient(System.Uri pathUri, Azure.AzureSasCredential credential, Azure.Storage.Files.DataLake.DataLakeClientOptions options = null) { } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have a different ctor parameter pattern for the TokenCredential (3 overloads instead of defaulted options). Is this intentional?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Less code. I was trying to understand why do we have such pattern in place, but nobody would recall why. From user POV there shouldn't be a difference (unless I'm missing something subtle here).
You can observe similar inconsistencies in APIs implementations. Some would come with multiple overloads, some will use optional parameters. I think those with multiple overloads approach are older.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the goal of all of these is to make mainline APIs look simpler. We've noticed that customers get scared of methods with many parameters even if they are all optional so opted for having overloads in important scenarios.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Gotcha. I'll make it consistent then.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In general looks good. Just a couple of nits and a question about the impact of using generic methods (where we don't have to) on AOT scenarios.
@@ -47,6 +47,12 @@ public static InvalidOperationException StreamMustBeAtPosition0() | |||
public static InvalidOperationException TokenCredentialsRequireHttps() | |||
=> new InvalidOperationException("Use of token credentials requires HTTPS"); | |||
|
|||
public static ArgumentException SasCredentialRequiresUriWithoutSas<TUriBuilder>(Uri uri) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jkotas, how does it affect AOT scenarios? Would it be better to pass the type as a method parameter (object or Type)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Generic arguments are better than passing Type around. It is not just AOT, it is also a for IL trimming. Passing System.Type around is a potential problem for IL trimming and may sometime need annotations to work well with it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In this specific case, it should not really matter since the typeof usage is trivial and the method is small. https://github.com/dotnet/runtime/blob/master/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs in the CoreLib uses both patterns.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great. so this stays I guess.
throw Errors.SasCredentialRequiresUriWithoutSas<TUriBuilder>(resourceUri); | ||
} | ||
return new AzureSasCredentialSynchronousPolicy( | ||
credential ?? throw Errors.ArgumentNull(nameof(credential))); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's better to check preconditions at the beginning of the method body.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done.
/// A <see cref="Uri"/> referencing the directory that includes the | ||
/// name of the account, the name of the file system, and the path of the | ||
/// directory. | ||
/// Must not contain shared access signature. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would add ", which should be passed in the second (following) parameter" ... or something like that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done.
* Add AzureSasCredential * corner case. * api. * core as project ref (todo undo this) * first client. * hack azure core in webjobs for now. * api. * constructors. * blob tests. * datalake tests. * share tests + take out sas on share and service clients as there isn't much APIs that would work with sas there. * queues tests. * well that works. nvm. * remarks. * error message. * predicate shouldn't be optional. * post-merge tweaks. * message about right UriBuilder. * added uri validation. * changelog. * user delegation sas change. * batch. * this test doesn't work well in playback mode. * pr feedback. * comments. * validation. * undo project references workaround. * undo webjobs project ref workaround.
* Add AzureSasCredential * corner case. * api. * core as project ref (todo undo this) * first client. * hack azure core in webjobs for now. * api. * constructors. * blob tests. * datalake tests. * share tests + take out sas on share and service clients as there isn't much APIs that would work with sas there. * queues tests. * well that works. nvm. * remarks. * error message. * predicate shouldn't be optional. * post-merge tweaks. * message about right UriBuilder. * added uri validation. * changelog. * user delegation sas change. * batch. * this test doesn't work well in playback mode. * pr feedback. * comments. * validation. * undo project references workaround. * undo webjobs project ref workaround.
In this PR:
Remarks