From 4af30fd1b37ebf0d10e3c162752a7d2a7f0cc3e7 Mon Sep 17 00:00:00 2001 From: Timothy Mothra Date: Mon, 21 Jun 2021 15:24:12 -0700 Subject: [PATCH] AAD: change to CredentialEnvelope to expose Expiration (#2306) --- .../net452/PublicAPI.Unshipped.txt | 17 +- .../net46/PublicAPI.Unshipped.txt | 17 +- .../netstandard2.0/PublicAPI.Unshipped.txt | 17 +- .../ReflectionCredentialEnvelopeTests.cs | 182 +++++++++++++++--- .../TransmissionCredentialEnvelopeTests.cs | 9 +- .../Channel/Transmission.cs | 4 +- .../Authentication/AuthToken.cs | 62 ++++++ .../Authentication/CredentialEnvelope.cs | 5 +- .../ReflectionCredentialEnvelope.cs | 130 ++++++++----- .../QuickPulseCollectionStateManager.cs | 9 +- 10 files changed, 359 insertions(+), 93 deletions(-) create mode 100644 BASE/src/Microsoft.ApplicationInsights/Extensibility/Implementation/Authentication/AuthToken.cs diff --git a/.publicApi/Microsoft.ApplicationInsights.dll/net452/PublicAPI.Unshipped.txt b/.publicApi/Microsoft.ApplicationInsights.dll/net452/PublicAPI.Unshipped.txt index 39e0d1c109..076d707d9b 100644 --- a/.publicApi/Microsoft.ApplicationInsights.dll/net452/PublicAPI.Unshipped.txt +++ b/.publicApi/Microsoft.ApplicationInsights.dll/net452/PublicAPI.Unshipped.txt @@ -1,5 +1,12 @@ -abstract Microsoft.ApplicationInsights.Extensibility.Implementation.Authentication.CredentialEnvelope.GetToken(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> string -abstract Microsoft.ApplicationInsights.Extensibility.Implementation.Authentication.CredentialEnvelope.GetTokenAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task +abstract Microsoft.ApplicationInsights.Extensibility.Implementation.Authentication.CredentialEnvelope.GetToken(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> Microsoft.ApplicationInsights.Extensibility.Implementation.Authentication.AuthToken +abstract Microsoft.ApplicationInsights.Extensibility.Implementation.Authentication.CredentialEnvelope.GetTokenAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task +Microsoft.ApplicationInsights.Extensibility.Implementation.Authentication.AuthToken +Microsoft.ApplicationInsights.Extensibility.Implementation.Authentication.AuthToken.AuthToken() -> void +Microsoft.ApplicationInsights.Extensibility.Implementation.Authentication.AuthToken.AuthToken(string token, System.DateTimeOffset expiresOn) -> void +Microsoft.ApplicationInsights.Extensibility.Implementation.Authentication.AuthToken.ExpiresOn.get -> System.DateTimeOffset +Microsoft.ApplicationInsights.Extensibility.Implementation.Authentication.AuthToken.ExpiresOn.set -> void +Microsoft.ApplicationInsights.Extensibility.Implementation.Authentication.AuthToken.Token.get -> string +Microsoft.ApplicationInsights.Extensibility.Implementation.Authentication.AuthToken.Token.set -> void Microsoft.ApplicationInsights.Extensibility.Implementation.Authentication.CredentialEnvelope Microsoft.ApplicationInsights.Extensibility.Implementation.Authentication.CredentialEnvelope.CredentialEnvelope() -> void Microsoft.ApplicationInsights.Extensibility.TelemetryConfiguration.CredentialEnvelope.get -> Microsoft.ApplicationInsights.Extensibility.Implementation.Authentication.CredentialEnvelope @@ -7,4 +14,8 @@ Microsoft.ApplicationInsights.Extensibility.TelemetryConfiguration.SetAzureToken Microsoft.ApplicationInsights.Channel.IAsyncFlushable Microsoft.ApplicationInsights.Channel.IAsyncFlushable.FlushAsync(System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task Microsoft.ApplicationInsights.Channel.InMemoryChannel.FlushAsync(System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task -Microsoft.ApplicationInsights.TelemetryClient.FlushAsync(System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task \ No newline at end of file +Microsoft.ApplicationInsights.TelemetryClient.FlushAsync(System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task +override Microsoft.ApplicationInsights.Extensibility.Implementation.Authentication.AuthToken.Equals(object obj) -> bool +override Microsoft.ApplicationInsights.Extensibility.Implementation.Authentication.AuthToken.GetHashCode() -> int +static Microsoft.ApplicationInsights.Extensibility.Implementation.Authentication.AuthToken.operator !=(Microsoft.ApplicationInsights.Extensibility.Implementation.Authentication.AuthToken left, Microsoft.ApplicationInsights.Extensibility.Implementation.Authentication.AuthToken right) -> bool +static Microsoft.ApplicationInsights.Extensibility.Implementation.Authentication.AuthToken.operator ==(Microsoft.ApplicationInsights.Extensibility.Implementation.Authentication.AuthToken left, Microsoft.ApplicationInsights.Extensibility.Implementation.Authentication.AuthToken right) -> bool \ No newline at end of file diff --git a/.publicApi/Microsoft.ApplicationInsights.dll/net46/PublicAPI.Unshipped.txt b/.publicApi/Microsoft.ApplicationInsights.dll/net46/PublicAPI.Unshipped.txt index 39e0d1c109..076d707d9b 100644 --- a/.publicApi/Microsoft.ApplicationInsights.dll/net46/PublicAPI.Unshipped.txt +++ b/.publicApi/Microsoft.ApplicationInsights.dll/net46/PublicAPI.Unshipped.txt @@ -1,5 +1,12 @@ -abstract Microsoft.ApplicationInsights.Extensibility.Implementation.Authentication.CredentialEnvelope.GetToken(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> string -abstract Microsoft.ApplicationInsights.Extensibility.Implementation.Authentication.CredentialEnvelope.GetTokenAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task +abstract Microsoft.ApplicationInsights.Extensibility.Implementation.Authentication.CredentialEnvelope.GetToken(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> Microsoft.ApplicationInsights.Extensibility.Implementation.Authentication.AuthToken +abstract Microsoft.ApplicationInsights.Extensibility.Implementation.Authentication.CredentialEnvelope.GetTokenAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task +Microsoft.ApplicationInsights.Extensibility.Implementation.Authentication.AuthToken +Microsoft.ApplicationInsights.Extensibility.Implementation.Authentication.AuthToken.AuthToken() -> void +Microsoft.ApplicationInsights.Extensibility.Implementation.Authentication.AuthToken.AuthToken(string token, System.DateTimeOffset expiresOn) -> void +Microsoft.ApplicationInsights.Extensibility.Implementation.Authentication.AuthToken.ExpiresOn.get -> System.DateTimeOffset +Microsoft.ApplicationInsights.Extensibility.Implementation.Authentication.AuthToken.ExpiresOn.set -> void +Microsoft.ApplicationInsights.Extensibility.Implementation.Authentication.AuthToken.Token.get -> string +Microsoft.ApplicationInsights.Extensibility.Implementation.Authentication.AuthToken.Token.set -> void Microsoft.ApplicationInsights.Extensibility.Implementation.Authentication.CredentialEnvelope Microsoft.ApplicationInsights.Extensibility.Implementation.Authentication.CredentialEnvelope.CredentialEnvelope() -> void Microsoft.ApplicationInsights.Extensibility.TelemetryConfiguration.CredentialEnvelope.get -> Microsoft.ApplicationInsights.Extensibility.Implementation.Authentication.CredentialEnvelope @@ -7,4 +14,8 @@ Microsoft.ApplicationInsights.Extensibility.TelemetryConfiguration.SetAzureToken Microsoft.ApplicationInsights.Channel.IAsyncFlushable Microsoft.ApplicationInsights.Channel.IAsyncFlushable.FlushAsync(System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task Microsoft.ApplicationInsights.Channel.InMemoryChannel.FlushAsync(System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task -Microsoft.ApplicationInsights.TelemetryClient.FlushAsync(System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task \ No newline at end of file +Microsoft.ApplicationInsights.TelemetryClient.FlushAsync(System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task +override Microsoft.ApplicationInsights.Extensibility.Implementation.Authentication.AuthToken.Equals(object obj) -> bool +override Microsoft.ApplicationInsights.Extensibility.Implementation.Authentication.AuthToken.GetHashCode() -> int +static Microsoft.ApplicationInsights.Extensibility.Implementation.Authentication.AuthToken.operator !=(Microsoft.ApplicationInsights.Extensibility.Implementation.Authentication.AuthToken left, Microsoft.ApplicationInsights.Extensibility.Implementation.Authentication.AuthToken right) -> bool +static Microsoft.ApplicationInsights.Extensibility.Implementation.Authentication.AuthToken.operator ==(Microsoft.ApplicationInsights.Extensibility.Implementation.Authentication.AuthToken left, Microsoft.ApplicationInsights.Extensibility.Implementation.Authentication.AuthToken right) -> bool \ No newline at end of file diff --git a/.publicApi/Microsoft.ApplicationInsights.dll/netstandard2.0/PublicAPI.Unshipped.txt b/.publicApi/Microsoft.ApplicationInsights.dll/netstandard2.0/PublicAPI.Unshipped.txt index 39e0d1c109..076d707d9b 100644 --- a/.publicApi/Microsoft.ApplicationInsights.dll/netstandard2.0/PublicAPI.Unshipped.txt +++ b/.publicApi/Microsoft.ApplicationInsights.dll/netstandard2.0/PublicAPI.Unshipped.txt @@ -1,5 +1,12 @@ -abstract Microsoft.ApplicationInsights.Extensibility.Implementation.Authentication.CredentialEnvelope.GetToken(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> string -abstract Microsoft.ApplicationInsights.Extensibility.Implementation.Authentication.CredentialEnvelope.GetTokenAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task +abstract Microsoft.ApplicationInsights.Extensibility.Implementation.Authentication.CredentialEnvelope.GetToken(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> Microsoft.ApplicationInsights.Extensibility.Implementation.Authentication.AuthToken +abstract Microsoft.ApplicationInsights.Extensibility.Implementation.Authentication.CredentialEnvelope.GetTokenAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task +Microsoft.ApplicationInsights.Extensibility.Implementation.Authentication.AuthToken +Microsoft.ApplicationInsights.Extensibility.Implementation.Authentication.AuthToken.AuthToken() -> void +Microsoft.ApplicationInsights.Extensibility.Implementation.Authentication.AuthToken.AuthToken(string token, System.DateTimeOffset expiresOn) -> void +Microsoft.ApplicationInsights.Extensibility.Implementation.Authentication.AuthToken.ExpiresOn.get -> System.DateTimeOffset +Microsoft.ApplicationInsights.Extensibility.Implementation.Authentication.AuthToken.ExpiresOn.set -> void +Microsoft.ApplicationInsights.Extensibility.Implementation.Authentication.AuthToken.Token.get -> string +Microsoft.ApplicationInsights.Extensibility.Implementation.Authentication.AuthToken.Token.set -> void Microsoft.ApplicationInsights.Extensibility.Implementation.Authentication.CredentialEnvelope Microsoft.ApplicationInsights.Extensibility.Implementation.Authentication.CredentialEnvelope.CredentialEnvelope() -> void Microsoft.ApplicationInsights.Extensibility.TelemetryConfiguration.CredentialEnvelope.get -> Microsoft.ApplicationInsights.Extensibility.Implementation.Authentication.CredentialEnvelope @@ -7,4 +14,8 @@ Microsoft.ApplicationInsights.Extensibility.TelemetryConfiguration.SetAzureToken Microsoft.ApplicationInsights.Channel.IAsyncFlushable Microsoft.ApplicationInsights.Channel.IAsyncFlushable.FlushAsync(System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task Microsoft.ApplicationInsights.Channel.InMemoryChannel.FlushAsync(System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task -Microsoft.ApplicationInsights.TelemetryClient.FlushAsync(System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task \ No newline at end of file +Microsoft.ApplicationInsights.TelemetryClient.FlushAsync(System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task +override Microsoft.ApplicationInsights.Extensibility.Implementation.Authentication.AuthToken.Equals(object obj) -> bool +override Microsoft.ApplicationInsights.Extensibility.Implementation.Authentication.AuthToken.GetHashCode() -> int +static Microsoft.ApplicationInsights.Extensibility.Implementation.Authentication.AuthToken.operator !=(Microsoft.ApplicationInsights.Extensibility.Implementation.Authentication.AuthToken left, Microsoft.ApplicationInsights.Extensibility.Implementation.Authentication.AuthToken right) -> bool +static Microsoft.ApplicationInsights.Extensibility.Implementation.Authentication.AuthToken.operator ==(Microsoft.ApplicationInsights.Extensibility.Implementation.Authentication.AuthToken left, Microsoft.ApplicationInsights.Extensibility.Implementation.Authentication.AuthToken right) -> bool \ No newline at end of file diff --git a/BASE/Test/Microsoft.ApplicationInsights.Test/Microsoft.ApplicationInsights.Tests/Extensibility/Implementation/Authentication/ReflectionCredentialEnvelopeTests.cs b/BASE/Test/Microsoft.ApplicationInsights.Test/Microsoft.ApplicationInsights.Tests/Extensibility/Implementation/Authentication/ReflectionCredentialEnvelopeTests.cs index c6cf0dcc70..9aefba0658 100644 --- a/BASE/Test/Microsoft.ApplicationInsights.Test/Microsoft.ApplicationInsights.Tests/Extensibility/Implementation/Authentication/ReflectionCredentialEnvelopeTests.cs +++ b/BASE/Test/Microsoft.ApplicationInsights.Test/Microsoft.ApplicationInsights.Tests/Extensibility/Implementation/Authentication/ReflectionCredentialEnvelopeTests.cs @@ -2,6 +2,8 @@ namespace Microsoft.ApplicationInsights.TestFramework.Extensibility.Implementation.Authentication { using System; + using System.Linq.Expressions; + using System.Reflection; using System.Threading; using System.Threading.Tasks; @@ -67,10 +69,12 @@ public void VerifyGetToken_UsingCompileTimeTypes() { var mockCredential = new MockCredential(); var requestContext = new TokenRequestContext(new string[] { "test/scope" }); + var tokenFromCredential = mockCredential.GetToken(requestContext, CancellationToken.None); - var testResult = ReflectionCredentialEnvelope.AzureCore.InvokeGetToken(mockCredential, requestContext, CancellationToken.None); - - Assert.AreEqual("TEST TOKEN test/scope", testResult); + var tokenFromReflection = ReflectionCredentialEnvelope.AzureCore.InvokeGetToken(mockCredential, requestContext, CancellationToken.None); + + Assert.AreEqual(tokenFromCredential.Token, tokenFromReflection.Token); + Assert.AreEqual(tokenFromCredential.ExpiresOn, tokenFromReflection.ExpiresOn); } [TestMethod] @@ -78,10 +82,12 @@ public async Task VerifyGetTokenAsync_UsingCompileTimeTypes() { var mockCredential = new MockCredential(); var requestContext = new TokenRequestContext(new string[] { "test/scope" }); + var tokenFromCredential = mockCredential.GetToken(requestContext, CancellationToken.None); - var testResult = await ReflectionCredentialEnvelope.AzureCore.InvokeGetTokenAsync(mockCredential, requestContext, CancellationToken.None); + var tokenFromReflection = await ReflectionCredentialEnvelope.AzureCore.InvokeGetTokenAsync(mockCredential, requestContext, CancellationToken.None); - Assert.AreEqual("TEST TOKEN test/scope", testResult); + Assert.AreEqual(tokenFromCredential.Token, tokenFromReflection.Token); + Assert.AreEqual(tokenFromCredential.ExpiresOn, tokenFromReflection.ExpiresOn); } /// @@ -90,12 +96,15 @@ public async Task VerifyGetTokenAsync_UsingCompileTimeTypes() [TestMethod] public void VerifyGetToken_UsingDynamicTypes() { - var mockCredential = (object)new MockCredential(); - var requestContext = ReflectionCredentialEnvelope.AzureCore.MakeTokenRequestContext(new[] { "test/scope" }); - - var testResult = ReflectionCredentialEnvelope.AzureCore.InvokeGetToken(mockCredential, requestContext, CancellationToken.None); + var mockCredential = new MockCredential(); + var requestContext = new TokenRequestContext(new string[] { "test/scope" }); + var tokenFromCredential = mockCredential.GetToken(requestContext, CancellationToken.None); - Assert.AreEqual("TEST TOKEN test/scope", testResult); + var requestContextFromReflection = ReflectionCredentialEnvelope.AzureCore.MakeTokenRequestContext(new[] { "test/scope" }); + var tokenFromReflection = ReflectionCredentialEnvelope.AzureCore.InvokeGetToken((object)mockCredential, requestContextFromReflection, CancellationToken.None); + + Assert.AreEqual(tokenFromCredential.Token, tokenFromReflection.Token); + Assert.AreEqual(tokenFromCredential.ExpiresOn, tokenFromReflection.ExpiresOn); } /// @@ -104,12 +113,15 @@ public void VerifyGetToken_UsingDynamicTypes() [TestMethod] public async Task VerifyGetTokenAsync_UsingDynamicTypes() { - var mockCredential = (object)new MockCredential(); - var requestContext = ReflectionCredentialEnvelope.AzureCore.MakeTokenRequestContext(new[] { "test/scope" }); + var mockCredential = new MockCredential(); + var requestContext = new TokenRequestContext(new string[] { "test/scope" }); + var tokenFromCredential = mockCredential.GetToken(requestContext, CancellationToken.None); - var testResult = await ReflectionCredentialEnvelope.AzureCore.InvokeGetTokenAsync(mockCredential, requestContext, CancellationToken.None); + var requestContextFromReflection = ReflectionCredentialEnvelope.AzureCore.MakeTokenRequestContext(new[] { "test/scope" }); + var tokenFromReflection = await ReflectionCredentialEnvelope.AzureCore.InvokeGetTokenAsync((object)mockCredential, requestContextFromReflection, CancellationToken.None); - Assert.AreEqual("TEST TOKEN test/scope", testResult); + Assert.AreEqual(tokenFromCredential.Token, tokenFromReflection.Token); + Assert.AreEqual(tokenFromCredential.ExpiresOn, tokenFromReflection.ExpiresOn); } /// @@ -120,12 +132,13 @@ public void VerifyGetToken_ReturnsValidToken() { var requestContext = new TokenRequestContext(scopes: AuthConstants.GetScopes()); var mockCredential = new MockCredential(); - var tokenUsingTypes = mockCredential.GetToken(requestContext, CancellationToken.None); + var tokenFromCredential = mockCredential.GetToken(requestContext, CancellationToken.None); var reflectionCredentialEnvelope = new ReflectionCredentialEnvelope(mockCredential); - var tokenUsingReflection = reflectionCredentialEnvelope.GetToken(); + var tokenFromReflection = reflectionCredentialEnvelope.GetToken(); - Assert.AreEqual(tokenUsingTypes.Token, tokenUsingReflection); + Assert.AreEqual(tokenFromCredential.Token, tokenFromReflection.Token); + Assert.AreEqual(tokenFromCredential.ExpiresOn, tokenFromReflection.ExpiresOn); } /// @@ -136,12 +149,13 @@ public async Task VerifyGetTokenAsync_ReturnsValidToken() { var requestContext = new TokenRequestContext(scopes: AuthConstants.GetScopes()); var mockCredential = new MockCredential(); - var tokenUsingTypes = await mockCredential.GetTokenAsync(requestContext, CancellationToken.None); + var tokenFromCredential = await mockCredential.GetTokenAsync(requestContext, CancellationToken.None); var reflectionCredentialEnvelope = new ReflectionCredentialEnvelope(mockCredential); - var tokenUsingReflection = await reflectionCredentialEnvelope.GetTokenAsync(); + var tokenFromReflection = await reflectionCredentialEnvelope.GetTokenAsync(); - Assert.AreEqual(tokenUsingTypes.Token, tokenUsingReflection); + Assert.AreEqual(tokenFromCredential.Token, tokenFromReflection.Token); + Assert.AreEqual(tokenFromCredential.ExpiresOn, tokenFromReflection.ExpiresOn); } [TestMethod] @@ -153,7 +167,7 @@ public void VerifyGetToken_IfCredentialThrowsException_EnvelopeReturnsNull() var reflectionCredentialEnvelope = new ReflectionCredentialEnvelope(mockCredential); var token = reflectionCredentialEnvelope.GetToken(); - Assert.IsNull(token); + Assert.AreEqual(default(AuthToken), token); } [TestMethod] @@ -165,11 +179,131 @@ public async Task VerifyGetTokenAsync_IfCredentialThrowsException_EnvelopeReturn var reflectionCredentialEnvelope = new ReflectionCredentialEnvelope(mockCredential); var token = await reflectionCredentialEnvelope.GetTokenAsync(); - Assert.IsNull(token); + Assert.AreEqual(default(AuthToken), token); } -#region TestClasses - + [TestMethod] + public void TestReflection() + { + Type typeTokenCredential = Type.GetType("Azure.Core.TokenCredential, Azure.Core"); + Type typeTokenRequestContext = Type.GetType("Azure.Core.TokenRequestContext, Azure.Core"); + Type typeCancellationToken = typeof(CancellationToken); + + var parameterExpression_tokenCredential = Expression.Parameter(type: typeTokenCredential, name: "parameterExpression_TokenCredential"); + var parameterExpression_requestContext = Expression.Parameter(type: typeTokenRequestContext, name: "parameterExpression_RequestContext"); + var parameterExpression_cancellationToken = Expression.Parameter(type: typeCancellationToken, name: "parameterExpression_CancellationToken"); + + var exprGetToken = Expression.Call( + instance: parameterExpression_tokenCredential, + method: typeTokenCredential.GetMethod(name: "GetToken", types: new Type[] { typeTokenRequestContext, typeCancellationToken }), + arg0: parameterExpression_requestContext, + arg1: parameterExpression_cancellationToken); + + var exprTokenProperty = Expression.Property( + expression: exprGetToken, + propertyName: "Token"); + + var exprExpiresOnProperty = Expression.Property( + expression: exprGetToken, + propertyName: "ExpiresOn"); + + Type typeAuthToken = typeof(AuthToken); + ConstructorInfo authTokenCtor = typeAuthToken.GetConstructor(new Type[] { typeof(string), typeof(DateTimeOffset) }); + + var exprAuthTokenCtor = Expression.New(authTokenCtor, exprTokenProperty, exprExpiresOnProperty); + //Expression.init + + + var compiledExpression = Expression.Lambda( + //body: exprTokenProperty, + body: exprAuthTokenCtor, + parameters: new ParameterExpression[] + { + parameterExpression_tokenCredential, + parameterExpression_requestContext, + parameterExpression_cancellationToken, + }).Compile(); + + //---------------------------------- + + var mockCredential = new MockCredential(); + var requestContext = new TokenRequestContext(new string[] { "test/scope" }); + CancellationToken ct = default; + + var test = (AuthToken)compiledExpression.DynamicInvoke(mockCredential, requestContext, ct); + + } + + + [TestMethod] + public void TestReflection2() + { + Type typeTokenCredential = Type.GetType("Azure.Core.TokenCredential, Azure.Core"); + Type typeTokenRequestContext = Type.GetType("Azure.Core.TokenRequestContext, Azure.Core"); + Type typeCancellationToken = typeof(CancellationToken); + + var parameterExpression_tokenCredential = Expression.Parameter(type: typeTokenCredential, name: "parameterExpression_TokenCredential"); + var parameterExpression_requestContext = Expression.Parameter(type: typeTokenRequestContext, name: "parameterExpression_RequestContext"); + var parameterExpression_cancellationToken = Expression.Parameter(type: typeCancellationToken, name: "parameterExpression_CancellationToken"); + + var exprGetToken = Expression.Call( + instance: parameterExpression_tokenCredential, + method: typeTokenCredential.GetMethod(name: "GetToken", types: new Type[] { typeTokenRequestContext, typeCancellationToken }), + arg0: parameterExpression_requestContext, + arg1: parameterExpression_cancellationToken); + + var compiledExpression1 = Expression.Lambda( + body: exprGetToken, + parameters: new ParameterExpression[] + { + parameterExpression_tokenCredential, + parameterExpression_requestContext, + parameterExpression_cancellationToken, + }).Compile(); + + // ------------------------------ + + var mockCredential = new MockCredential(); + var requestContext = new TokenRequestContext(new string[] { "test/scope" }); + CancellationToken ct = default; + + var objAccessToken = compiledExpression1.DynamicInvoke(mockCredential, requestContext, ct); + + + // ---------------------------- + + Type typeAccessToken = Type.GetType("Azure.Core.AccessToken, Azure.Core"); + + var parameterExpression_AccessToken = Expression.Parameter(typeAccessToken, "parameterExpression_AccessToken"); + + var exprTokenProperty = Expression.Property( + expression: parameterExpression_AccessToken, + propertyName: "Token"); + + var exprExpiresOnProperty = Expression.Property( + expression: parameterExpression_AccessToken, + propertyName: "ExpiresOn"); + + Type typeAuthToken = typeof(AuthToken); + ConstructorInfo authTokenCtor = typeAuthToken.GetConstructor(new Type[] { typeof(string), typeof(DateTimeOffset) }); + + var exprAuthTokenCtor = Expression.New(authTokenCtor, exprTokenProperty, exprExpiresOnProperty); + + var compiledExpression2 = Expression.Lambda( + //body: exprTokenProperty, + body: exprAuthTokenCtor, + parameters: new ParameterExpression[] + { + parameterExpression_AccessToken, + }).Compile(); + + //---------------------------------- + + var test = compiledExpression2.DynamicInvoke(objAccessToken); + + } + #region TestClasses + /// /// This class inherits which inherits . /// This class is used to verify that the can correctly identify tests that inherit . diff --git a/BASE/Test/Microsoft.ApplicationInsights.Test/Microsoft.ApplicationInsights.Tests/Extensibility/Implementation/Authentication/TransmissionCredentialEnvelopeTests.cs b/BASE/Test/Microsoft.ApplicationInsights.Test/Microsoft.ApplicationInsights.Tests/Extensibility/Implementation/Authentication/TransmissionCredentialEnvelopeTests.cs index 61e8145424..346ad0fbb3 100644 --- a/BASE/Test/Microsoft.ApplicationInsights.Test/Microsoft.ApplicationInsights.Tests/Extensibility/Implementation/Authentication/TransmissionCredentialEnvelopeTests.cs +++ b/BASE/Test/Microsoft.ApplicationInsights.Test/Microsoft.ApplicationInsights.Tests/Extensibility/Implementation/Authentication/TransmissionCredentialEnvelopeTests.cs @@ -59,9 +59,8 @@ public async Task VerifyTransmissionSendAsync_Default() [TestMethod] public async Task VerifyTransmissionSendAsync_WithCredential_SetsAuthHeader() { - var credendialEnvelope = new ReflectionCredentialEnvelope(new MockCredential()); - var token = credendialEnvelope.GetToken(); - + var credentialEnvelope = new ReflectionCredentialEnvelope(new MockCredential()); + var authToken = credentialEnvelope.GetToken(); var handler = new HandlerForFakeHttpClient { @@ -70,7 +69,7 @@ public async Task VerifyTransmissionSendAsync_WithCredential_SetsAuthHeader() { // VALIDATE Assert.AreEqual(AuthConstants.AuthorizationTokenPrefix.Trim(), req.Headers.Authorization.Scheme); - Assert.AreEqual(token, req.Headers.Authorization.Parameter); + Assert.AreEqual(authToken.Token, req.Headers.Authorization.Parameter); return Task.FromResult(new HttpResponseMessage()); } @@ -84,7 +83,7 @@ public async Task VerifyTransmissionSendAsync_WithCredential_SetsAuthHeader() // Instantiate Transmission with the mock HttpClient var transmission = new Transmission(testUri, new byte[] { 1, 2, 3, 4, 5 }, fakeHttpClient, expectedContentType, expectedContentEncoding); - transmission.CredentialEnvelope = credendialEnvelope; + transmission.CredentialEnvelope = credentialEnvelope; var result = await transmission.SendAsync(); } diff --git a/BASE/src/Microsoft.ApplicationInsights/Channel/Transmission.cs b/BASE/src/Microsoft.ApplicationInsights/Channel/Transmission.cs index 9eb8e7806e..d8276d657b 100644 --- a/BASE/src/Microsoft.ApplicationInsights/Channel/Transmission.cs +++ b/BASE/src/Microsoft.ApplicationInsights/Channel/Transmission.cs @@ -417,14 +417,14 @@ protected virtual HttpRequestMessage CreateRequestMessage(Uri address, Stream co // TODO: NEED TO USE CACHING HERE var authToken = this.CredentialEnvelope.GetToken(); - if (authToken == null) + if (authToken == default(AuthToken)) { // TODO: DO NOT SEND. RETURN FAILURE AND LET CHANNEL DECIDE WHEN TO RETRY. // This could be either a configuration error or the AAD service is unavailable. } else { - request.Headers.TryAddWithoutValidation(AuthConstants.AuthorizationHeaderName, AuthConstants.AuthorizationTokenPrefix + authToken); + request.Headers.TryAddWithoutValidation(AuthConstants.AuthorizationHeaderName, AuthConstants.AuthorizationTokenPrefix + authToken.Token); } } diff --git a/BASE/src/Microsoft.ApplicationInsights/Extensibility/Implementation/Authentication/AuthToken.cs b/BASE/src/Microsoft.ApplicationInsights/Extensibility/Implementation/Authentication/AuthToken.cs new file mode 100644 index 0000000000..c46ecc58dc --- /dev/null +++ b/BASE/src/Microsoft.ApplicationInsights/Extensibility/Implementation/Authentication/AuthToken.cs @@ -0,0 +1,62 @@ +namespace Microsoft.ApplicationInsights.Extensibility.Implementation.Authentication +{ + using System; + + /// + /// This represents the Azure.Core.AccessToken returned by Azure.Core.TokenCredential. + /// (https://github.com/Azure/azure-sdk-for-net/blob/master/sdk/core/Azure.Core/src/AccessToken.cs). + /// + public struct AuthToken + { + /// + /// Initializes a new instance of . + /// + /// Access token. + /// DateTimeOffset representing when the access token expires. + public AuthToken(string token, DateTimeOffset expiresOn) + { + this.Token = token; + this.ExpiresOn = expiresOn; + } + + /// + /// Gets or sets get the access token value. + /// + public string Token { get; set; } + + /// + /// Gets or sets the time when the provided token expires. + /// + public DateTimeOffset ExpiresOn { get; set; } + + /// + /// Determine if two instance of AuthToken are equal. + /// + /// An instance of AuthToken on the left side of the operator. + /// An instance of AuthToken on the right side of the operator. + /// Returns a boolean indicating if the params are equal. + public static bool operator ==(AuthToken left, AuthToken right) => left.Equals(right); + + /// + /// Determine if two instance of AuthToken are not equal. + /// + /// An instance of AuthToken on the left side of the operator. + /// An instance of AuthToken on the right side of the operator. + /// Returns a boolean indicating if the params are not equal. + public static bool operator !=(AuthToken left, AuthToken right) => !left.Equals(right); + + /// + public override bool Equals(object obj) + { + if (obj is AuthToken authToken) + { + return authToken.ExpiresOn == this.ExpiresOn && authToken.Token == this.Token; + } + + return false; + } + + /// + public override int GetHashCode() => this.Token.GetHashCode() ^ this.ExpiresOn.GetHashCode(); + } +} diff --git a/BASE/src/Microsoft.ApplicationInsights/Extensibility/Implementation/Authentication/CredentialEnvelope.cs b/BASE/src/Microsoft.ApplicationInsights/Extensibility/Implementation/Authentication/CredentialEnvelope.cs index 1f25ac8ccf..9948faaf84 100644 --- a/BASE/src/Microsoft.ApplicationInsights/Extensibility/Implementation/Authentication/CredentialEnvelope.cs +++ b/BASE/src/Microsoft.ApplicationInsights/Extensibility/Implementation/Authentication/CredentialEnvelope.cs @@ -5,6 +5,7 @@ /// /// This interface defines a class that can interact with Azure.Core.TokenCredential. + /// See also: (https://github.com/Azure/azure-sdk-for-net/blob/master/sdk/core/Azure.Core/src/TokenCredential.cs). /// public abstract class CredentialEnvelope { @@ -21,7 +22,7 @@ public abstract class CredentialEnvelope /// /// The System.Threading.CancellationToken to use. /// A valid Azure.Core.AccessToken. - public abstract string GetToken(CancellationToken cancellationToken = default); + public abstract AuthToken GetToken(CancellationToken cancellationToken = default); /// /// Gets an Azure.Core.AccessToken. @@ -31,6 +32,6 @@ public abstract class CredentialEnvelope /// /// The System.Threading.CancellationToken to use. /// A valid Azure.Core.AccessToken. - public abstract Task GetTokenAsync(CancellationToken cancellationToken = default); + public abstract Task GetTokenAsync(CancellationToken cancellationToken = default); } } \ No newline at end of file diff --git a/BASE/src/Microsoft.ApplicationInsights/Extensibility/Implementation/Authentication/ReflectionCredentialEnvelope.cs b/BASE/src/Microsoft.ApplicationInsights/Extensibility/Implementation/Authentication/ReflectionCredentialEnvelope.cs index 61e2d8f81e..9304e33ebe 100644 --- a/BASE/src/Microsoft.ApplicationInsights/Extensibility/Implementation/Authentication/ReflectionCredentialEnvelope.cs +++ b/BASE/src/Microsoft.ApplicationInsights/Extensibility/Implementation/Authentication/ReflectionCredentialEnvelope.cs @@ -3,6 +3,7 @@ using System; using System.Linq; using System.Linq.Expressions; + using System.Reflection; using System.Threading; using System.Threading.Tasks; @@ -52,7 +53,7 @@ public ReflectionCredentialEnvelope(object tokenCredential) /// /// The System.Threading.CancellationToken to use. /// A valid Azure.Core.AccessToken. - public override string GetToken(CancellationToken cancellationToken = default) + public override AuthToken GetToken(CancellationToken cancellationToken = default) { try { @@ -61,7 +62,7 @@ public override string GetToken(CancellationToken cancellationToken = default) catch (Exception ex) { CoreEventSource.Log.FailedToGetToken(ex.ToInvariantString()); - return null; + return default; } } @@ -73,7 +74,7 @@ public override string GetToken(CancellationToken cancellationToken = default) /// /// The System.Threading.CancellationToken to use. /// A valid Azure.Core.AccessToken. - public override async Task GetTokenAsync(CancellationToken cancellationToken = default) + public override async Task GetTokenAsync(CancellationToken cancellationToken = default) { try { @@ -82,7 +83,7 @@ public override async Task GetTokenAsync(CancellationToken cancellationT catch (Exception ex) { CoreEventSource.Log.FailedToGetToken(ex.ToInvariantString()); - return null; + return default; } } @@ -140,30 +141,24 @@ private static Type GetTokenCredentialType() /// internal static class AzureCore { - private static readonly Delegate GetTokenValue; - private static readonly Delegate GetTokenAsyncValue; - private static readonly Delegate GetTokenProperty; + private static readonly Delegate GetTokenValue = BuildDelegateGetToken(); + private static readonly Delegate GetTokenAsyncAsTask = BuildDelegateGetTokenAsync(); + private static readonly Delegate GetTaskResult = BuildGetTaskResult(); + private static readonly Delegate AccessTokenToAuthToken = BuildDelegateAccessTokenToAuthToken(); - [System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "CA1810:Initialize reference type static fields inline", Justification = "For both optimization and readability, I'm building these objects in the same method.")] - static AzureCore() + internal static AuthToken InvokeGetToken(object tokenCredential, object tokenRequestContext, CancellationToken cancellationToken) { - GetTokenValue = BuildDelegateGetToken(); - - var asyncDelegates = BuildDelegateGetTokenAsync(); - GetTokenAsyncValue = asyncDelegates[0]; - GetTokenProperty = asyncDelegates[1]; - } - - internal static string InvokeGetToken(object tokenCredential, object tokenRequestContext, CancellationToken cancellationToken) - { - return (string)GetTokenValue.DynamicInvoke(tokenCredential, tokenRequestContext, cancellationToken); + var objAccessToken = GetTokenValue.DynamicInvoke(tokenCredential, tokenRequestContext, cancellationToken); + return (AuthToken)AccessTokenToAuthToken.DynamicInvoke(objAccessToken); } - internal static async Task InvokeGetTokenAsync(object tokenCredential, object tokenRequestContext, CancellationToken cancellationToken) + internal static async Task InvokeGetTokenAsync(object tokenCredential, object tokenRequestContext, CancellationToken cancellationToken) { - var task = (Task)GetTokenAsyncValue.DynamicInvoke(tokenCredential, tokenRequestContext, cancellationToken); + var task = (Task)GetTokenAsyncAsTask.DynamicInvoke(tokenCredential, tokenRequestContext, cancellationToken); await task.ConfigureAwait(false); - return (string)GetTokenProperty.DynamicInvoke(task); + + var objAccessToken = GetTaskResult.DynamicInvoke(task); + return (AuthToken)AccessTokenToAuthToken.DynamicInvoke(objAccessToken); } /// @@ -178,9 +173,49 @@ internal static object MakeTokenRequestContext(string[] scopes) args: new object[] { scopes, null, }); } + /// + /// This is a wrapper for Azure.Core.AccessToken: + /// public struct AccessToken + /// (https://docs.microsoft.com/dotnet/api/azure.core.accesstoken). + /// + /// + /// Returns a delegate that receives an Azure.Core.AccessToken and emits an . + /// + private static Delegate BuildDelegateAccessTokenToAuthToken() + { + Type typeAccessToken = Type.GetType("Azure.Core.AccessToken, Azure.Core"); + + var parameterExpression_AccessToken = Expression.Parameter(typeAccessToken, "parameterExpression_AccessToken"); + + var exprTokenProperty = Expression.Property( + expression: parameterExpression_AccessToken, + propertyName: "Token"); + + var exprExpiresOnProperty = Expression.Property( + expression: parameterExpression_AccessToken, + propertyName: "ExpiresOn"); + + Type typeAuthToken = typeof(AuthToken); + ConstructorInfo authTokenCtor = typeAuthToken.GetConstructor(new Type[] { typeof(string), typeof(DateTimeOffset) }); + + var exprAuthTokenCtor = Expression.New(authTokenCtor, exprTokenProperty, exprExpiresOnProperty); + + return Expression.Lambda( + body: exprAuthTokenCtor, + parameters: new ParameterExpression[] + { + parameterExpression_AccessToken, + }).Compile(); + } + + /// /// This creates a wrapper for the following method: /// public abstract Azure.Core.AccessToken GetToken (Azure.Core.TokenRequestContext requestContext, System.Threading.CancellationToken cancellationToken). /// (https://docs.microsoft.com/dotnet/api/azure.core.tokencredential.gettoken). + /// + /// + /// Returns a delegate that receives an Azure.Core.TokenCredential and emits an Azure.Core.AccessToken. + /// private static Delegate BuildDelegateGetToken() { Type typeTokenCredential = Type.GetType("Azure.Core.TokenCredential, Azure.Core"); @@ -197,12 +232,8 @@ private static Delegate BuildDelegateGetToken() arg0: parameterExpression_requestContext, arg1: parameterExpression_cancellationToken); - var exprTokenProperty = Expression.Property( - expression: exprGetToken, - propertyName: "Token"); - return Expression.Lambda( - body: exprTokenProperty, + body: exprGetToken, parameters: new ParameterExpression[] { parameterExpression_tokenCredential, @@ -217,17 +248,12 @@ private static Delegate BuildDelegateGetToken() /// (https://docs.microsoft.com/dotnet/api/azure.core.tokencredential.gettokenasync). /// /// - /// The Expression Tree library cannot handle async methods. - /// As a workaround, this method returns two Delegates. - /// First; - /// The first Delegate is a wrapper around GetTokenAsync which returns a ValueTask of AccessToken. - /// Then calls ValueTask.GetTask to convert that to a Task which is a known type for older frameworks. - /// This Task can be awaited. - /// Second; - /// The second Delegate is a wrapper around Task.Result which returns the AccessToken. - /// Then calls AccessToken.Token to get the string token. + /// Returns a delegate that is a wrapper around GetTokenAsync which returns a System.Threading.Tasks.ValueTask of Azure.Core.AccessToken. + /// Then converts that System.Threading.Tasks.ValueTask to which can be awaited. + /// NOTE: The Expression Tree library cannot handle async methods. + /// NOTE: ValueTask is not recognized by older versions of .NET Framework. /// - private static Delegate[] BuildDelegateGetTokenAsync() + private static Delegate BuildDelegateGetTokenAsync() { Type typeTokenCredential = Type.GetType("Azure.Core.TokenCredential, Azure.Core"); Type typeTokenRequestContext = Type.GetType("Azure.Core.TokenRequestContext, Azure.Core"); @@ -237,7 +263,6 @@ private static Delegate[] BuildDelegateGetTokenAsync() var parameterExpression_RequestContext = Expression.Parameter(type: typeTokenRequestContext, name: "parameterExpression_RequestContext"); var parameterExpression_CancellationToken = Expression.Parameter(type: typeCancellationToken, name: "parameterExpression_CancellationToken"); - // public abstract System.Threading.Tasks.ValueTask GetTokenAsync (Azure.Core.TokenRequestContext requestContext, System.Threading.CancellationToken cancellationToken); var methodInfo_GetTokenAsync = typeTokenCredential.GetMethod(name: "GetTokenAsync", types: new Type[] { typeTokenRequestContext, typeCancellationToken }); var exprGetTokenAsync = Expression.Call( @@ -252,7 +277,7 @@ private static Delegate[] BuildDelegateGetTokenAsync() instance: exprGetTokenAsync, method: methodInfo_AsTask); - var delegateGetTokenAsync = Expression.Lambda( + return Expression.Lambda( body: exprAsTask, parameters: new ParameterExpression[] { @@ -260,25 +285,36 @@ private static Delegate[] BuildDelegateGetTokenAsync() parameterExpression_RequestContext, parameterExpression_CancellationToken, }).Compile(); + } + /// + /// This is a wrapper for a that came from Azure.Core.AccessToken.GetTokenAsync. + /// public abstract System.Threading.Tasks.ValueTask<Azure.Core.AccessToken> GetTokenAsync (Azure.Core.TokenRequestContext requestContext, System.Threading.CancellationToken cancellationToken); + /// (https://docs.microsoft.com/dotnet/api/azure.core.tokencredential.gettokenasync). + /// + /// + /// Returns a delegate which receives a and emits an Azure.Core.AccessToken. + /// + private static Delegate BuildGetTaskResult() + { + Type typeTokenCredential = Type.GetType("Azure.Core.TokenCredential, Azure.Core"); + Type typeTokenRequestContext = Type.GetType("Azure.Core.TokenRequestContext, Azure.Core"); + Type typeCancellationToken = typeof(CancellationToken); + var methodInfo_GetTokenAsync = typeTokenCredential.GetMethod(name: "GetTokenAsync", types: new Type[] { typeTokenRequestContext, typeCancellationToken }); + var methodInfo_AsTask = methodInfo_GetTokenAsync.ReturnType.GetMethod("AsTask"); + var parameterExpression_Task = Expression.Parameter(type: methodInfo_AsTask.ReturnType, name: "parameterExpression_Task"); var exprResultProperty = Expression.Property( expression: parameterExpression_Task, propertyName: "Result"); - var exprTokenProperty = Expression.Property( - expression: exprResultProperty, - propertyName: "Token"); - - var delegateTokenProperty = Expression.Lambda( - body: exprTokenProperty, + return Expression.Lambda( + body: exprResultProperty, parameters: new ParameterExpression[] { parameterExpression_Task, }).Compile(); - - return new[] { delegateGetTokenAsync, delegateTokenProperty }; } } } diff --git a/WEB/Src/PerformanceCollector/PerformanceCollector/Implementation/QuickPulse/QuickPulseCollectionStateManager.cs b/WEB/Src/PerformanceCollector/PerformanceCollector/Implementation/QuickPulse/QuickPulseCollectionStateManager.cs index 9a280d77a0..b931f06127 100644 --- a/WEB/Src/PerformanceCollector/PerformanceCollector/Implementation/QuickPulse/QuickPulseCollectionStateManager.cs +++ b/WEB/Src/PerformanceCollector/PerformanceCollector/Implementation/QuickPulse/QuickPulseCollectionStateManager.cs @@ -7,6 +7,7 @@ using System.Threading.Tasks; using Microsoft.ApplicationInsights.Extensibility.Filtering; + using Microsoft.ApplicationInsights.Extensibility.Implementation.Authentication; using Microsoft.ApplicationInsights.Extensibility.Implementation.Tracing; using Microsoft.ApplicationInsights.Extensibility.PerfCounterCollector.Implementation.QuickPulse.Helpers; @@ -107,11 +108,11 @@ public TimeSpan UpdateState(string instrumentationKey, string authApiKey) this.firstStateUpdate = false; } - string authToken = null; + AuthToken authToken = default; if (this.telemetryConfiguration.CredentialEnvelope != null) { authToken = this.telemetryConfiguration.CredentialEnvelope.GetToken(); - if (authToken == null) + if (authToken == default) { // If a credential has been set on the configuration and we fail to get a token, do net send. QuickPulseEventSource.Log.FailedToGetAuthToken(); @@ -150,7 +151,7 @@ public TimeSpan UpdateState(string instrumentationKey, string authApiKey) instrumentationKey, this.currentConfigurationETag, authApiKey, - authToken, + authToken.Token, out configurationInfo, this.collectionConfigurationErrors.ToArray()); @@ -185,7 +186,7 @@ public TimeSpan UpdateState(string instrumentationKey, string authApiKey) this.timeProvider.UtcNow, this.currentConfigurationETag, authApiKey, - authToken, + authToken.Token, out configurationInfo, out TimeSpan? servicePollingIntervalHint);