-
Notifications
You must be signed in to change notification settings - Fork 494
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
KeyRefresh: Adds AzureKeyCredential support to enable key refresh sce…
…narios (#3276) * Draft version of azureKeyCredential support (for key refresh scenario's) * Defensive coding * Adding tests * Concurrent key update scenario fix * Fixing test to cover for both master and resoruce tokens * Incorporating feedback comment * Addressing Fabian feedback comments * Draft version of azureKeyCredential support (for key refresh scenario's) * Defensive coding * Adding tests * Concurrent key update scenario fix * Fixing test to cover for both master and resoruce tokens * Addressing Fabian feedback comments * Fixing merge rebase issues * Adding a perf benchmark * Pushing contract changes * Fix: handling of concurrent access wile updating. * Fix: one more concurrency related fix. * Documentation suggestions * Added direct mode diagnotics of AuthProvider LifeTime * Update Microsoft.Azure.Cosmos/src/CosmosClient.cs Co-authored-by: Matias Quaranta <[email protected]> * Update Microsoft.Azure.Cosmos/src/CosmosClient.cs Co-authored-by: Matias Quaranta <[email protected]> * Update Microsoft.Azure.Cosmos/src/Fluent/CosmosClientBuilder.cs Co-authored-by: Matias Quaranta <[email protected]> * Addressing review comments * Removing the int casting * Addressing comments * Adding the tracing contract test * Base trace updated Co-authored-by: Matias Quaranta <[email protected]>
- Loading branch information
1 parent
71f39dd
commit 795978a
Showing
14 changed files
with
845 additions
and
16 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
129 changes: 129 additions & 0 deletions
129
Microsoft.Azure.Cosmos/src/Authorization/AzureKeyCredentialAuthorizationTokenProvider.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,129 @@ | ||
//------------------------------------------------------------ | ||
// Copyright (c) Microsoft Corporation. All rights reserved. | ||
//------------------------------------------------------------ | ||
|
||
namespace Microsoft.Azure.Cosmos.Authorization | ||
{ | ||
using System; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
using global::Azure; | ||
using Microsoft.Azure.Cosmos.Tracing; | ||
using Microsoft.Azure.Documents; | ||
using Microsoft.Azure.Documents.Collections; | ||
|
||
internal class AzureKeyCredentialAuthorizationTokenProvider : AuthorizationTokenProvider | ||
{ | ||
private readonly object refreshLock = new object(); | ||
private readonly AzureKeyCredential azureKeyCredential; | ||
|
||
// keyObject is used to check for refresh | ||
private string currentKeyObject = null; | ||
|
||
// Internal for unit testing | ||
internal AuthorizationTokenProvider authorizationTokenProvider; | ||
|
||
public AzureKeyCredentialAuthorizationTokenProvider( | ||
AzureKeyCredential azureKeyCredential) | ||
{ | ||
this.azureKeyCredential = azureKeyCredential ?? throw new ArgumentNullException(nameof(azureKeyCredential)); | ||
this.CheckAndRefreshTokenProvider(); | ||
} | ||
|
||
public override ValueTask AddAuthorizationHeaderAsync( | ||
INameValueCollection headersCollection, | ||
Uri requestAddress, | ||
string verb, | ||
AuthorizationTokenType tokenType) | ||
{ | ||
this.CheckAndRefreshTokenProvider(); | ||
return this.authorizationTokenProvider.AddAuthorizationHeaderAsync( | ||
headersCollection, | ||
requestAddress, | ||
verb, | ||
tokenType); | ||
} | ||
|
||
public override void Dispose() | ||
{ | ||
this.authorizationTokenProvider?.Dispose(); | ||
this.authorizationTokenProvider = null; | ||
} | ||
|
||
public override ValueTask<(string token, string payload)> GetUserAuthorizationAsync( | ||
string resourceAddress, | ||
string resourceType, | ||
string requestVerb, | ||
INameValueCollection headers, | ||
AuthorizationTokenType tokenType) | ||
{ | ||
this.CheckAndRefreshTokenProvider(); | ||
return this.authorizationTokenProvider.GetUserAuthorizationAsync( | ||
resourceAddress, | ||
resourceType, | ||
requestVerb, | ||
headers, | ||
tokenType); | ||
} | ||
|
||
public override ValueTask<string> GetUserAuthorizationTokenAsync( | ||
string resourceAddress, | ||
string resourceType, | ||
string requestVerb, | ||
INameValueCollection headers, | ||
AuthorizationTokenType tokenType, | ||
ITrace trace) | ||
{ | ||
this.CheckAndRefreshTokenProvider(); | ||
return this.authorizationTokenProvider.GetUserAuthorizationTokenAsync( | ||
resourceAddress, | ||
resourceType, | ||
requestVerb, | ||
headers, | ||
tokenType, | ||
trace); | ||
} | ||
|
||
public override void TraceUnauthorized( | ||
DocumentClientException dce, | ||
string authorizationToken, | ||
string payload) | ||
{ | ||
this.authorizationTokenProvider.TraceUnauthorized( | ||
dce, | ||
authorizationToken, | ||
payload); | ||
} | ||
|
||
public override TimeSpan GetAge() | ||
{ | ||
return this.authorizationTokenProvider.GetAge(); | ||
} | ||
|
||
private void CheckAndRefreshTokenProvider() | ||
{ | ||
if (!Object.ReferenceEquals(this.currentKeyObject, this.azureKeyCredential.Key)) | ||
{ | ||
// Change is immediate for all new reqeust flows (pure compute work and should be very very quick) | ||
// With-out lock possibility of concurrent updates (== #inflight reqeust's) but eventually only one will win | ||
lock (this.refreshLock) | ||
{ | ||
// Process only if the authProvider is not yet exchanged | ||
if (!Object.ReferenceEquals(this.currentKeyObject, this.azureKeyCredential.Key)) | ||
{ | ||
AuthorizationTokenProvider newAuthProvider = AuthorizationTokenProvider.CreateWithResourceTokenOrAuthKey(this.azureKeyCredential.Key); | ||
AuthorizationTokenProvider currentAuthProvider = Interlocked.Exchange(ref this.authorizationTokenProvider, newAuthProvider); | ||
|
||
if (!Object.ReferenceEquals(newAuthProvider, currentAuthProvider)) | ||
{ | ||
// NewAuthProvider => | ||
// 1. Credentials changed | ||
// 2. Dispose current token provider: NOT Disposed actively: There might be inflight usage => leaving it to GC finalizers | ||
Interlocked.Exchange(ref this.currentKeyObject, this.azureKeyCredential.Key); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.