-
Notifications
You must be signed in to change notification settings - Fork 287
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
177 additions
and
1 deletion.
There are no files selected for viewing
27 changes: 27 additions & 0 deletions
27
...ationInsights.Tests/Extensibility/Implementation/Authentication/CountingMockCredential.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,27 @@ | ||
#if !NET452 && !NET46 | ||
namespace Microsoft.ApplicationInsights.TestFramework.Extensibility.Implementation.Authentication | ||
{ | ||
using System.Threading; | ||
|
||
using Azure.Core; | ||
|
||
/// <summary> | ||
/// A <see cref="MockCredential"/> that counts the number of calls to <see cref="GetToken(Azure.Core.TokenRequestContext, System.Threading.CancellationToken)"/>. | ||
/// </summary> | ||
public class CountingMockCredential : MockCredential | ||
{ | ||
private int getTokenCallCount; | ||
|
||
/// <summary> | ||
/// Gets or sets the call count. | ||
/// </summary> | ||
public int GetTokenCallCount { get => getTokenCallCount; private set => getTokenCallCount = value; } | ||
|
||
public override AccessToken GetToken(TokenRequestContext requestContext, CancellationToken cancellationToken) | ||
{ | ||
Interlocked.Increment(ref getTokenCallCount); | ||
return base.GetToken(requestContext, cancellationToken); | ||
} | ||
} | ||
} | ||
#endif |
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
111 changes: 111 additions & 0 deletions
111
...nsights/Extensibility/Implementation/Authentication/CachedReflectionCredentialEnvelope.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,111 @@ | ||
namespace Microsoft.ApplicationInsights.Extensibility.Implementation.Authentication | ||
{ | ||
using System; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
|
||
/// <summary> | ||
/// This is a version of <see cref="ReflectionCredentialEnvelope"/> that caches and reuses | ||
/// the auth token for most of its lifetime, thereby preventing repeated calls to fetch | ||
/// new tokens. | ||
/// </summary> | ||
internal sealed class CachedReflectionCredentialEnvelope : ReflectionCredentialEnvelope | ||
{ | ||
/// <summary> | ||
/// The token refresh interval. This is the minimum lifetime required | ||
/// for the auth token. A cached token can be re-used if its remaining | ||
/// lifetime is at least as long as this interval. | ||
/// </summary> | ||
private readonly TimeSpan tokenRefreshOffset; | ||
|
||
/// <summary> | ||
/// The cached token, if any. | ||
/// </summary> | ||
private AuthToken? cachedToken; | ||
|
||
/// <summary> | ||
/// Create an instance of <see cref="CachedReflectionCredentialEnvelope"/>. | ||
/// </summary> | ||
/// <param name="tokenCredential">An instance of Azure.Core.TokenCredential.</param> | ||
/// <remarks> | ||
/// The default 5 minute token refresh interval matches the default | ||
/// tokenRefreshOffset in Azure.Core's BearerTokenAuthenticationPolicy. It's | ||
/// reasonable to assume that callers will use the auth token to make an | ||
/// authenticated call well within 5 minutes of calling GetToken. | ||
/// </remarks> | ||
public CachedReflectionCredentialEnvelope(object tokenCredential) : this(tokenCredential, TimeSpan.FromMinutes(5)) | ||
{ | ||
} | ||
|
||
/// <summary> | ||
/// Create an instance of <see cref="CachedReflectionCredentialEnvelope"/>. | ||
/// </summary> | ||
/// <param name="tokenCredential">An instance of Azure.Core.TokenCredential.</param> | ||
/// <param name="tokenRefreshOffset">The remaining lifetime allowed before a cached token must be refreshed.</param> | ||
public CachedReflectionCredentialEnvelope(object tokenCredential, TimeSpan tokenRefreshOffset) : base(tokenCredential) | ||
{ | ||
this.tokenRefreshOffset = tokenRefreshOffset; | ||
} | ||
|
||
/// <summary> | ||
/// Gets an Azure.Core.AccessToken. | ||
/// </summary> | ||
/// <remarks> | ||
/// Whomever uses this MUST verify that it's called within <see cref="SdkInternalOperationsMonitor.Enter"/> otherwise dependency calls will be tracked. | ||
/// </remarks> | ||
/// <param name="cancellationToken">The System.Threading.CancellationToken to use.</param> | ||
/// <returns>A valid Azure.Core.AccessToken.</returns> | ||
public override AuthToken GetToken(CancellationToken cancellationToken = default) | ||
{ | ||
if (TryUseCachedToken(out AuthToken authToken)) | ||
{ | ||
return authToken; | ||
} | ||
|
||
return (this.cachedToken = base.GetToken(cancellationToken)).Value; | ||
} | ||
|
||
/// <summary> | ||
/// Gets an Azure.Core.AccessToken. | ||
/// </summary> | ||
/// <remarks> | ||
/// Whomever uses this MUST verify that it's called within <see cref="SdkInternalOperationsMonitor.Enter"/> otherwise dependency calls will be tracked. | ||
/// </remarks> | ||
/// <param name="cancellationToken">The System.Threading.CancellationToken to use.</param> | ||
/// <returns>A valid Azure.Core.AccessToken.</returns> | ||
public override async Task<AuthToken> GetTokenAsync(CancellationToken cancellationToken = default) | ||
{ | ||
if (TryUseCachedToken(out AuthToken authToken)) | ||
{ | ||
return authToken; | ||
} | ||
|
||
return (this.cachedToken = await base.GetTokenAsync(cancellationToken)).Value; | ||
} | ||
|
||
/// <summary> | ||
/// Check whether we can use the cached authentication token because its remaining | ||
/// lifetime is longer than the minimum required lifetime (5 minutes). | ||
/// </summary> | ||
/// <param name="cachedToken">On success, the value of the cached token.</param> | ||
/// <returns>True if the cached token can be used. False otherwise.</returns> | ||
private bool TryUseCachedToken(out AuthToken cachedToken) | ||
{ | ||
AuthToken? token = this.cachedToken; | ||
if (token.HasValue) | ||
{ | ||
TimeSpan timeRemaining = token.Value.ExpiresOn - DateTimeOffset.UtcNow; | ||
if (timeRemaining >= tokenRefreshOffset) | ||
{ | ||
cachedToken = token.Value; | ||
return true; | ||
} | ||
|
||
// Cached token must be refreshed. | ||
} | ||
|
||
cachedToken = default; | ||
return false; | ||
} | ||
} | ||
} |
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