From abf9b3c1b78c5225eacf1a6abbe5b8a3cc41bd88 Mon Sep 17 00:00:00 2001 From: Timothy Mothra Lee Date: Fri, 28 May 2021 13:56:33 -0700 Subject: [PATCH 01/10] aad with InMemoryChannel --- .../Channel/InMemoryChannel.cs | 20 ++++++- .../Channel/InMemoryTransmitter.cs | 23 ++++++-- .../Channel/Transmission.cs | 23 +++++++- .../Authentication/AuthConstants.cs | 4 ++ .../Endpoints/EndpointContainer.cs | 13 ++++- .../Extensibility/TelemetryConfiguration.cs | 58 +++++++++++++++---- 6 files changed, 120 insertions(+), 21 deletions(-) diff --git a/BASE/src/Microsoft.ApplicationInsights/Channel/InMemoryChannel.cs b/BASE/src/Microsoft.ApplicationInsights/Channel/InMemoryChannel.cs index c01ccde94d..2c50a75932 100644 --- a/BASE/src/Microsoft.ApplicationInsights/Channel/InMemoryChannel.cs +++ b/BASE/src/Microsoft.ApplicationInsights/Channel/InMemoryChannel.cs @@ -1,10 +1,14 @@ namespace Microsoft.ApplicationInsights.Channel { using System; + using System.ComponentModel; using System.Diagnostics; using System.Threading; using System.Threading.Tasks; + using Microsoft.ApplicationInsights.Common; + using Microsoft.ApplicationInsights.Extensibility; + using Microsoft.ApplicationInsights.Extensibility.Implementation.Authentication; using Microsoft.ApplicationInsights.Extensibility.Implementation.Tracing; /// @@ -122,6 +126,20 @@ public int BacklogSize set { this.buffer.BacklogSize = value; } } + /// + /// Gets or sets the which is used for AAD. + /// FOR INTERNAL USE. Customers should use instead. + /// + /// + /// sets + /// which is used to set just before calling . + /// + internal CredentialEnvelope CredentialEnvelope + { + get => this.transmitter.CredentialEnvelope; + set => this.transmitter.CredentialEnvelope = value; + } + internal bool IsDisposed => this.isDisposed; /// @@ -231,4 +249,4 @@ protected virtual void Dispose(bool disposing) } } } -} +} \ No newline at end of file diff --git a/BASE/src/Microsoft.ApplicationInsights/Channel/InMemoryTransmitter.cs b/BASE/src/Microsoft.ApplicationInsights/Channel/InMemoryTransmitter.cs index 9c80f20676..c122689c32 100644 --- a/BASE/src/Microsoft.ApplicationInsights/Channel/InMemoryTransmitter.cs +++ b/BASE/src/Microsoft.ApplicationInsights/Channel/InMemoryTransmitter.cs @@ -11,9 +11,11 @@ namespace Microsoft.ApplicationInsights.Channel using System.Net.Http; using System.Threading; using System.Threading.Tasks; + using Microsoft.ApplicationInsights.Common.Extensions; using Microsoft.ApplicationInsights.Extensibility; using Microsoft.ApplicationInsights.Extensibility.Implementation; + using Microsoft.ApplicationInsights.Extensibility.Implementation.Authentication; using Microsoft.ApplicationInsights.Extensibility.Implementation.Tracing; /// @@ -32,13 +34,13 @@ internal class InMemoryTransmitter : IDisposable [SuppressMessage("Microsoft.Usage", "CA2213:DisposableFieldsShouldBeDisposed", Justification = "Object is disposed within the using statement of the " + nameof(Runner) + " method.")] private AutoResetEvent startRunnerEvent; private bool enabled = true; - + /// /// The number of times this object was disposed. /// private int disposeCount = 0; private TimeSpan sendingInterval = TimeSpan.FromSeconds(30); - + internal InMemoryTransmitter(TelemetryBuffer buffer) { this.buffer = buffer; @@ -47,11 +49,11 @@ internal InMemoryTransmitter(TelemetryBuffer buffer) // Starting the Runner Task.Factory.StartNew(this.Runner, CancellationToken.None, TaskCreationOptions.LongRunning, TaskScheduler.Default) .ContinueWith( - task => + task => { string msg = string.Format(CultureInfo.InvariantCulture, "InMemoryTransmitter: Unhandled exception in Runner: {0}", task.Exception); CoreEventSource.Log.LogVerbose(msg); - }, + }, TaskContinuationOptions.OnlyOnFaulted); } @@ -63,6 +65,15 @@ internal TimeSpan SendingInterval set { this.sendingInterval = value; } } + /// + /// Gets or sets the which is used for AAD. + /// + /// + /// sets + /// which is used to set just before calling . + /// + internal CredentialEnvelope CredentialEnvelope { get; set; } + /// /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. /// @@ -163,7 +174,7 @@ private Task Send(IEnumerable telemetryItems, TimeSpan timeout) } var transmission = new Transmission(this.EndpointAddress, data, JsonSerializer.ContentType, JsonSerializer.CompressionType, timeout); - + transmission.CredentialEnvelope = this.CredentialEnvelope; return transmission.SendAsync(); } @@ -192,4 +203,4 @@ private void Dispose(bool disposing) } } } -} +} \ No newline at end of file diff --git a/BASE/src/Microsoft.ApplicationInsights/Channel/Transmission.cs b/BASE/src/Microsoft.ApplicationInsights/Channel/Transmission.cs index 2a955baded..097b954e35 100644 --- a/BASE/src/Microsoft.ApplicationInsights/Channel/Transmission.cs +++ b/BASE/src/Microsoft.ApplicationInsights/Channel/Transmission.cs @@ -10,7 +10,9 @@ using System.Net.Http.Headers; using System.Threading; using System.Threading.Tasks; + using Microsoft.ApplicationInsights.Extensibility.Implementation; + using Microsoft.ApplicationInsights.Extensibility.Implementation.Authentication; using Microsoft.ApplicationInsights.Extensibility.Implementation.Tracing; /// @@ -141,6 +143,12 @@ public ICollection TelemetryItems get; private set; } + /// + /// Gets or sets the which is used for AAD. + /// This is used include an AAD token on HTTP Requests sent to ingestion. + /// + internal CredentialEnvelope CredentialEnvelope { get; set; } + /// /// Gets the flush async id for the transmission. /// @@ -404,6 +412,19 @@ protected virtual HttpRequestMessage CreateRequestMessage(Uri address, Stream co request.Content.Headers.Add(ContentEncodingHeader, this.ContentEncoding); } + if (this.CredentialEnvelope != null) + { + // TODO: NEED TO USE CACHING + var authToken = this.CredentialEnvelope.GetToken(); + + if (authToken == null) + { + // TODO: DO NOT SEND. RETURN FAILURE AND LET CHANNEL DECIDE WHEN TO RETRY. + } + + request.Headers.TryAddWithoutValidation(AuthConstants.AuthorizationHeaderName, AuthConstants.AuthorizationTokenPrefix + authToken); + } + return request; } @@ -432,4 +453,4 @@ protected virtual WebRequest CreateRequest(Uri address) return request; } } -} +} \ No newline at end of file diff --git a/BASE/src/Microsoft.ApplicationInsights/Extensibility/Implementation/Authentication/AuthConstants.cs b/BASE/src/Microsoft.ApplicationInsights/Extensibility/Implementation/Authentication/AuthConstants.cs index 8a6da3ed86..9e0c4d3323 100644 --- a/BASE/src/Microsoft.ApplicationInsights/Extensibility/Implementation/Authentication/AuthConstants.cs +++ b/BASE/src/Microsoft.ApplicationInsights/Extensibility/Implementation/Authentication/AuthConstants.cs @@ -2,6 +2,10 @@ { internal static class AuthConstants { + public const string AuthorizationHeaderName = "Authorization"; + + public const string AuthorizationTokenPrefix = "Bearer "; + /// /// Source: /// (https://docs.microsoft.com/azure/active-directory/develop/msal-acquire-cache-tokens#scopes-when-acquiring-tokens). diff --git a/BASE/src/Microsoft.ApplicationInsights/Extensibility/Implementation/Endpoints/EndpointContainer.cs b/BASE/src/Microsoft.ApplicationInsights/Extensibility/Implementation/Endpoints/EndpointContainer.cs index 8b55a1886a..5308ae8388 100644 --- a/BASE/src/Microsoft.ApplicationInsights/Extensibility/Implementation/Endpoints/EndpointContainer.cs +++ b/BASE/src/Microsoft.ApplicationInsights/Extensibility/Implementation/Endpoints/EndpointContainer.cs @@ -30,8 +30,19 @@ internal EndpointContainer(IEndpointProvider endpointProvider) /// Gets the fully formatted endpoint for the ingestion service. internal string FormattedIngestionEndpoint => new Uri(this.Ingestion, "v2/track").AbsoluteUri; + /// Gets the fully formatted endpoint for the ingestion service with support for AAD. + internal string FormattedIngestionAADEndpoint => new Uri(this.Ingestion, "v2.1/track").AbsoluteUri; + /// Gets the fully formatted endpoint for the application id profile service. /// This returns a string without using the Uri for validation because the consuming method needs to do a string replace. internal string FormattedApplicationIdEndpoint => this.Ingestion.AbsoluteUri + "api/profiles/{0}/appId"; + + /// + /// Get the Ingestion Endpoint, depending on if AAD is in use. + /// This can be removed after we fully transition no the newer Ingestion API. + /// + /// Boolean to indicate which ingestion service to use. + /// Fully formatted endpoint for the ingestion service. + internal string GetFormattedIngestionEndpoint(bool enableAAD) => enableAAD ? this.FormattedIngestionAADEndpoint : this.FormattedIngestionEndpoint; } -} +} \ No newline at end of file diff --git a/BASE/src/Microsoft.ApplicationInsights/Extensibility/TelemetryConfiguration.cs b/BASE/src/Microsoft.ApplicationInsights/Extensibility/TelemetryConfiguration.cs index f8b0297828..acc4edfd00 100644 --- a/BASE/src/Microsoft.ApplicationInsights/Extensibility/TelemetryConfiguration.cs +++ b/BASE/src/Microsoft.ApplicationInsights/Extensibility/TelemetryConfiguration.cs @@ -7,6 +7,7 @@ using System.Diagnostics; using System.Threading; using System.Threading.Tasks; + using Microsoft.ApplicationInsights.Channel; using Microsoft.ApplicationInsights.DataContracts; using Microsoft.ApplicationInsights.Extensibility.Implementation; @@ -31,11 +32,11 @@ public sealed class TelemetryConfiguration : IDisposable internal readonly SamplingRateStore LastKnownSampleRateStore = new SamplingRateStore(); private static object syncRoot = new object(); - private static TelemetryConfiguration active; + private static TelemetryConfiguration active; private readonly SnapshottingList telemetryInitializers = new SnapshottingList(); private readonly TelemetrySinkCollection telemetrySinks = new TelemetrySinkCollection(); - + private TelemetryProcessorChain telemetryProcessorChain; private string instrumentationKey = string.Empty; private string connectionString; @@ -43,7 +44,7 @@ public sealed class TelemetryConfiguration : IDisposable private TelemetryProcessorChainBuilder builder; private MetricManager metricManager = null; private IApplicationIdProvider applicationIdProvider; - + /// /// Indicates if this instance has been disposed of. /// @@ -64,7 +65,7 @@ static TelemetryConfiguration() { Activity.DefaultIdFormat = ActivityIdFormat.W3C; Activity.ForceDefaultIdFormat = true; - } + } }); SelfDiagnosticsInitializer.EnsureInitialized(); } @@ -94,7 +95,8 @@ public TelemetryConfiguration(string instrumentationKey, ITelemetryChannel chann { this.instrumentationKey = instrumentationKey ?? throw new ArgumentNullException(nameof(instrumentationKey)); - SetTelemetryChannelEndpoint(channel, this.EndpointContainer.FormattedIngestionEndpoint, force: true); + var ingestionEndpoint = this.EndpointContainer.GetFormattedIngestionEndpoint(enableAAD: this.CredentialEnvelope != null); + SetTelemetryChannelEndpoint(channel, ingestionEndpoint, force: true); var defaultSink = new TelemetrySink(this, channel); defaultSink.Name = "default"; this.telemetrySinks.Add(defaultSink); @@ -241,7 +243,9 @@ public ITelemetryChannel TelemetryChannel if (!this.isDisposed) { this.telemetrySinks.DefaultSink.TelemetryChannel = value; - SetTelemetryChannelEndpoint(this.telemetrySinks.DefaultSink.TelemetryChannel, this.EndpointContainer.FormattedIngestionEndpoint); + var ingestionEndpoint = this.EndpointContainer.GetFormattedIngestionEndpoint(enableAAD: this.CredentialEnvelope != null); + SetTelemetryChannelEndpoint(this.telemetrySinks.DefaultSink.TelemetryChannel, ingestionEndpoint); + SetTelemetryChannelCredentialEnvelope(value, this.CredentialEnvelope); } } } @@ -297,10 +301,8 @@ public string ConnectionString this.EndpointContainer = new EndpointContainer(endpointProvider); // UPDATE TELEMETRY CHANNEL - foreach (var tSink in this.TelemetrySinks) - { - SetTelemetryChannelEndpoint(tSink.TelemetryChannel, this.EndpointContainer.FormattedIngestionEndpoint, force: true); - } + var ingestionEndpoint = this.EndpointContainer.GetFormattedIngestionEndpoint(enableAAD: this.CredentialEnvelope != null); + this.SetTelemetryChannelEndpoint(ingestionEndpoint); // UPDATE APPLICATION ID PROVIDER SetApplicationIdEndpoint(this.ApplicationIdProvider, this.EndpointContainer.FormattedApplicationIdEndpoint, force: true); @@ -414,7 +416,15 @@ public void Dispose() /// /// An instance of Azure.Core.TokenCredential. /// An ArgumentException is thrown if the provided object does not inherit Azure.Core.TokenCredential. - public void SetAzureTokenCredential(object tokenCredential) => this.CredentialEnvelope = new ReflectionCredentialEnvelope(tokenCredential); + public void SetAzureTokenCredential(object tokenCredential) + { + this.CredentialEnvelope = new ReflectionCredentialEnvelope(tokenCredential); + this.SetTelemetryChannelCredentialEnvelope(); + + // Update Ingestion Endpoint. + var ingestionEndpoint = this.EndpointContainer.GetFormattedIngestionEndpoint(enableAAD: true); + this.SetTelemetryChannelEndpoint(ingestionEndpoint); + } internal MetricManager GetMetricManager(bool createIfNotExists) { @@ -492,6 +502,30 @@ private static void SetTelemetryChannelEndpoint(ITelemetryChannel channel, strin } } + private static void SetTelemetryChannelCredentialEnvelope(ITelemetryChannel telemetryChannel, CredentialEnvelope credentialEnvelope) + { + if (telemetryChannel is InMemoryChannel inMemoryChannel) + { + inMemoryChannel.CredentialEnvelope = credentialEnvelope; + } + } + + private void SetTelemetryChannelCredentialEnvelope() + { + foreach (var tSink in this.TelemetrySinks) + { + SetTelemetryChannelCredentialEnvelope(tSink.TelemetryChannel, this.CredentialEnvelope); + } + } + + private void SetTelemetryChannelEndpoint(string ingestionEndpoint) + { + foreach (var tSink in this.TelemetrySinks) + { + SetTelemetryChannelEndpoint(tSink.TelemetryChannel, ingestionEndpoint, force: true); + } + } + /// /// Disposes of resources. /// @@ -525,4 +559,4 @@ private void Dispose(bool disposing) } } } -} +} \ No newline at end of file From 605b597e4d1683210fc0bcaaecf8b45a7527f7e0 Mon Sep 17 00:00:00 2001 From: Timothy Mothra Lee Date: Fri, 28 May 2021 14:01:04 -0700 Subject: [PATCH 02/10] fix end of line --- .../Microsoft.ApplicationInsights/Channel/InMemoryChannel.cs | 2 +- .../Channel/InMemoryTransmitter.cs | 2 +- BASE/src/Microsoft.ApplicationInsights/Channel/Transmission.cs | 2 +- .../Extensibility/Implementation/Endpoints/EndpointContainer.cs | 2 +- .../Extensibility/TelemetryConfiguration.cs | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/BASE/src/Microsoft.ApplicationInsights/Channel/InMemoryChannel.cs b/BASE/src/Microsoft.ApplicationInsights/Channel/InMemoryChannel.cs index 2c50a75932..85ca810ffa 100644 --- a/BASE/src/Microsoft.ApplicationInsights/Channel/InMemoryChannel.cs +++ b/BASE/src/Microsoft.ApplicationInsights/Channel/InMemoryChannel.cs @@ -249,4 +249,4 @@ protected virtual void Dispose(bool disposing) } } } -} \ No newline at end of file +} diff --git a/BASE/src/Microsoft.ApplicationInsights/Channel/InMemoryTransmitter.cs b/BASE/src/Microsoft.ApplicationInsights/Channel/InMemoryTransmitter.cs index c122689c32..58461333d9 100644 --- a/BASE/src/Microsoft.ApplicationInsights/Channel/InMemoryTransmitter.cs +++ b/BASE/src/Microsoft.ApplicationInsights/Channel/InMemoryTransmitter.cs @@ -203,4 +203,4 @@ private void Dispose(bool disposing) } } } -} \ No newline at end of file +} diff --git a/BASE/src/Microsoft.ApplicationInsights/Channel/Transmission.cs b/BASE/src/Microsoft.ApplicationInsights/Channel/Transmission.cs index 097b954e35..ef6053aaec 100644 --- a/BASE/src/Microsoft.ApplicationInsights/Channel/Transmission.cs +++ b/BASE/src/Microsoft.ApplicationInsights/Channel/Transmission.cs @@ -453,4 +453,4 @@ protected virtual WebRequest CreateRequest(Uri address) return request; } } -} \ No newline at end of file +} diff --git a/BASE/src/Microsoft.ApplicationInsights/Extensibility/Implementation/Endpoints/EndpointContainer.cs b/BASE/src/Microsoft.ApplicationInsights/Extensibility/Implementation/Endpoints/EndpointContainer.cs index 5308ae8388..966e3c31d7 100644 --- a/BASE/src/Microsoft.ApplicationInsights/Extensibility/Implementation/Endpoints/EndpointContainer.cs +++ b/BASE/src/Microsoft.ApplicationInsights/Extensibility/Implementation/Endpoints/EndpointContainer.cs @@ -45,4 +45,4 @@ internal EndpointContainer(IEndpointProvider endpointProvider) /// Fully formatted endpoint for the ingestion service. internal string GetFormattedIngestionEndpoint(bool enableAAD) => enableAAD ? this.FormattedIngestionAADEndpoint : this.FormattedIngestionEndpoint; } -} \ No newline at end of file +} diff --git a/BASE/src/Microsoft.ApplicationInsights/Extensibility/TelemetryConfiguration.cs b/BASE/src/Microsoft.ApplicationInsights/Extensibility/TelemetryConfiguration.cs index acc4edfd00..9cd56036b4 100644 --- a/BASE/src/Microsoft.ApplicationInsights/Extensibility/TelemetryConfiguration.cs +++ b/BASE/src/Microsoft.ApplicationInsights/Extensibility/TelemetryConfiguration.cs @@ -559,4 +559,4 @@ private void Dispose(bool disposing) } } } -} \ No newline at end of file +} From cb2163d6e7ba1710be4bf104f0e73e625117bc07 Mon Sep 17 00:00:00 2001 From: Timothy Mothra Lee Date: Fri, 28 May 2021 14:44:19 -0700 Subject: [PATCH 03/10] configuration tests --- ...tryConfigurationCredentialEnvelopeTests.cs | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/BASE/Test/Microsoft.ApplicationInsights.Test/Microsoft.ApplicationInsights.Tests/Extensibility/Implementation/Authentication/TelemetryConfigurationCredentialEnvelopeTests.cs b/BASE/Test/Microsoft.ApplicationInsights.Test/Microsoft.ApplicationInsights.Tests/Extensibility/Implementation/Authentication/TelemetryConfigurationCredentialEnvelopeTests.cs index b3394e32bc..a14c396fce 100644 --- a/BASE/Test/Microsoft.ApplicationInsights.Test/Microsoft.ApplicationInsights.Tests/Extensibility/Implementation/Authentication/TelemetryConfigurationCredentialEnvelopeTests.cs +++ b/BASE/Test/Microsoft.ApplicationInsights.Test/Microsoft.ApplicationInsights.Tests/Extensibility/Implementation/Authentication/TelemetryConfigurationCredentialEnvelopeTests.cs @@ -4,6 +4,7 @@ namespace Microsoft.ApplicationInsights.TestFramework.Extensibility.Implementati using System; using System.Threading.Tasks; + using Microsoft.ApplicationInsights.Channel; using Microsoft.ApplicationInsights.Extensibility; using Microsoft.ApplicationInsights.Extensibility.Implementation.Authentication; using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -47,6 +48,50 @@ public void VerifyCannotSetInvalidObjectOnTelemetryConfiguration() var telemetryConfiguration = new TelemetryConfiguration(); telemetryConfiguration.SetAzureTokenCredential(Guid.Empty); } + + [TestMethod] + public void VerifySetCredential_CorrectlySetsTelemetryChannel_CredentialFirst() + { + // SETUP + var tc = TelemetryConfiguration.CreateDefault(); + Assert.IsInstanceOfType(tc.TelemetryChannel, typeof(InMemoryChannel)); + Assert.IsTrue(tc.TelemetryChannel.EndpointAddress.Contains("v2")); // defaults to old api + + // ACT + // set credential first + tc.SetAzureTokenCredential(new MockCredential()); + Assert.IsTrue(tc.TelemetryChannel.EndpointAddress.Contains("v2.1")); // api switch + + // test new channel + var channel = new InMemoryChannel(); + Assert.IsNull(channel.EndpointAddress); // new channel defaults null + + // change config channel + tc.TelemetryChannel = channel; + Assert.IsTrue(channel.EndpointAddress.Contains("v2.1")); // configuration sets new api + } + + [TestMethod] + public void VerifySetCredential_CorrectlySetsTelemetryChannel_TelemetryChannelFirst() + { + // SETUP + var tc = TelemetryConfiguration.CreateDefault(); + Assert.IsInstanceOfType(tc.TelemetryChannel, typeof(InMemoryChannel)); + Assert.IsTrue(tc.TelemetryChannel.EndpointAddress.Contains("v2")); // defaults to old api + + // ACT + // set new channel first + var channel = new InMemoryChannel(); + Assert.IsNull(channel.EndpointAddress); // new channel defaults null + + // change config channel + tc.TelemetryChannel = channel; + Assert.IsTrue(channel.EndpointAddress.Contains("v2")); // configuration sets new api + + // set credential second + tc.SetAzureTokenCredential(new MockCredential()); + Assert.IsTrue(tc.TelemetryChannel.EndpointAddress.Contains("v2.1")); // api switch + } } } #endif From 6d049fb85ace45ec3ea46ae61d81efb5bad3f875 Mon Sep 17 00:00:00 2001 From: Timothy Mothra Lee Date: Fri, 28 May 2021 15:21:23 -0700 Subject: [PATCH 04/10] Transmission tests --- .../TransmissionCredentialEnvelopeTests.cs | 94 +++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 BASE/Test/Microsoft.ApplicationInsights.Test/Microsoft.ApplicationInsights.Tests/Extensibility/Implementation/Authentication/TransmissionCredentialEnvelopeTests.cs 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 new file mode 100644 index 0000000000..61e8145424 --- /dev/null +++ b/BASE/Test/Microsoft.ApplicationInsights.Test/Microsoft.ApplicationInsights.Tests/Extensibility/Implementation/Authentication/TransmissionCredentialEnvelopeTests.cs @@ -0,0 +1,94 @@ +#if !NET452 && !NET46 +namespace Microsoft.ApplicationInsights.TestFramework.Extensibility.Implementation.Authentication +{ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Net.Http; + using System.Text; + using System.Threading.Tasks; + + using Microsoft.ApplicationInsights.Channel; + using Microsoft.ApplicationInsights.DataContracts; + using Microsoft.ApplicationInsights.Extensibility.Implementation.Authentication; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + /// + /// These tests verify that can receive and store an instance of . + /// + /// + /// These tests do not run in NET452 OR NET46. + /// In these cases, the test runner is NET452 or NET46 and Azure.Core.TokenCredential is NOT SUPPORTED in these frameworks. + /// This does not affect the end user because we REQUIRE the end user to create their own instance of TokenCredential. + /// This ensures that the end user is consuming the AI SDK in one of the newer frameworks. + /// + [TestClass] + [TestCategory("AAD")] + public class TransmissionCredentialEnvelopeTests + { + private readonly Uri testUri = new Uri("https://127.0.0.1/"); + + [TestMethod] + public async Task VerifyTransmissionSendAsync_Default() + { + var handler = new HandlerForFakeHttpClient + { + InnerHandler = new HttpClientHandler(), + OnSendAsync = (req, cancellationToken) => + { + // VALIDATE + Assert.IsNull(req.Headers.Authorization); + + return Task.FromResult(new HttpResponseMessage()); + } + }; + + using (var fakeHttpClient = new HttpClient(handler)) + { + var expectedContentType = "content/type"; + var expectedContentEncoding = "contentEncoding"; + var items = new List { new EventTelemetry() }; + + // Instantiate Transmission with the mock HttpClient + var transmission = new Transmission(testUri, new byte[] { 1, 2, 3, 4, 5 }, fakeHttpClient, expectedContentType, expectedContentEncoding); + + var result = await transmission.SendAsync(); + } + } + + [TestMethod] + public async Task VerifyTransmissionSendAsync_WithCredential_SetsAuthHeader() + { + var credendialEnvelope = new ReflectionCredentialEnvelope(new MockCredential()); + var token = credendialEnvelope.GetToken(); + + + var handler = new HandlerForFakeHttpClient + { + InnerHandler = new HttpClientHandler(), + OnSendAsync = (req, cancellationToken) => + { + // VALIDATE + Assert.AreEqual(AuthConstants.AuthorizationTokenPrefix.Trim(), req.Headers.Authorization.Scheme); + Assert.AreEqual(token, req.Headers.Authorization.Parameter); + + return Task.FromResult(new HttpResponseMessage()); + } + }; + + using (var fakeHttpClient = new HttpClient(handler)) + { + var expectedContentType = "content/type"; + var expectedContentEncoding = "contentEncoding"; + var items = new List { new EventTelemetry() }; + + // Instantiate Transmission with the mock HttpClient + var transmission = new Transmission(testUri, new byte[] { 1, 2, 3, 4, 5 }, fakeHttpClient, expectedContentType, expectedContentEncoding); + transmission.CredentialEnvelope = credendialEnvelope; + + var result = await transmission.SendAsync(); + } + } + } +} +#endif From 6e25d92e2ed80762603dc5d7727d5a6d5cd2accb Mon Sep 17 00:00:00 2001 From: Timothy Mothra Lee Date: Fri, 28 May 2021 16:16:26 -0700 Subject: [PATCH 05/10] comments --- .../Channel/Transmission.cs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/BASE/src/Microsoft.ApplicationInsights/Channel/Transmission.cs b/BASE/src/Microsoft.ApplicationInsights/Channel/Transmission.cs index ef6053aaec..9eb8e7806e 100644 --- a/BASE/src/Microsoft.ApplicationInsights/Channel/Transmission.cs +++ b/BASE/src/Microsoft.ApplicationInsights/Channel/Transmission.cs @@ -414,15 +414,18 @@ protected virtual HttpRequestMessage CreateRequestMessage(Uri address, Stream co if (this.CredentialEnvelope != null) { - // TODO: NEED TO USE CACHING + // TODO: NEED TO USE CACHING HERE var authToken = this.CredentialEnvelope.GetToken(); if (authToken == null) { // 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); } return request; From b07755415d50178b40529bc5bf1e6d87bad301dc Mon Sep 17 00:00:00 2001 From: Timothy Mothra Lee Date: Fri, 28 May 2021 16:16:39 -0700 Subject: [PATCH 06/10] testing fix for InternalOperations --- .../Authentication/ReflectionCredentialEnvelope.cs | 4 ++-- .../AzureSdk/AzureSdkDiagnosticsEventHandler.cs | 7 +++++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/BASE/src/Microsoft.ApplicationInsights/Extensibility/Implementation/Authentication/ReflectionCredentialEnvelope.cs b/BASE/src/Microsoft.ApplicationInsights/Extensibility/Implementation/Authentication/ReflectionCredentialEnvelope.cs index b441f952eb..7a14b65ffc 100644 --- a/BASE/src/Microsoft.ApplicationInsights/Extensibility/Implementation/Authentication/ReflectionCredentialEnvelope.cs +++ b/BASE/src/Microsoft.ApplicationInsights/Extensibility/Implementation/Authentication/ReflectionCredentialEnvelope.cs @@ -50,7 +50,7 @@ public ReflectionCredentialEnvelope(object tokenCredential) /// A valid Azure.Core.AccessToken. public override string GetToken(CancellationToken cancellationToken = default) { - SdkInternalOperationsMonitor.Enter(); + //SdkInternalOperationsMonitor.Enter(); try { return AzureCore.InvokeGetToken(this.tokenCredential, this.tokenRequestContext, cancellationToken); @@ -62,7 +62,7 @@ public override string GetToken(CancellationToken cancellationToken = default) } finally { - SdkInternalOperationsMonitor.Exit(); + //SdkInternalOperationsMonitor.Exit(); } } diff --git a/WEB/Src/DependencyCollector/DependencyCollector/Implementation/AzureSdk/AzureSdkDiagnosticsEventHandler.cs b/WEB/Src/DependencyCollector/DependencyCollector/Implementation/AzureSdk/AzureSdkDiagnosticsEventHandler.cs index 4a25454a48..49a886fbb9 100644 --- a/WEB/Src/DependencyCollector/DependencyCollector/Implementation/AzureSdk/AzureSdkDiagnosticsEventHandler.cs +++ b/WEB/Src/DependencyCollector/DependencyCollector/Implementation/AzureSdk/AzureSdkDiagnosticsEventHandler.cs @@ -38,6 +38,13 @@ public override void OnEvent(KeyValuePair evnt, DiagnosticListen { try { + if (SdkInternalOperationsMonitor.IsEntered()) + { + // Now that Application Insights supports AAD, we need to check if an internal operation is being caught here. + // type = "InProc | Microsoft.AAD" + return; + } + var currentActivity = Activity.Current; if (evnt.Key.EndsWith(".Start", StringComparison.Ordinal)) { From 7f0f92999fe36da2764cae7e43b873abec01dd4d Mon Sep 17 00:00:00 2001 From: Timothy Mothra Lee Date: Fri, 28 May 2021 16:24:05 -0700 Subject: [PATCH 07/10] fxcop --- .../Authentication/ReflectionCredentialEnvelope.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/BASE/src/Microsoft.ApplicationInsights/Extensibility/Implementation/Authentication/ReflectionCredentialEnvelope.cs b/BASE/src/Microsoft.ApplicationInsights/Extensibility/Implementation/Authentication/ReflectionCredentialEnvelope.cs index 7a14b65ffc..d351e192fa 100644 --- a/BASE/src/Microsoft.ApplicationInsights/Extensibility/Implementation/Authentication/ReflectionCredentialEnvelope.cs +++ b/BASE/src/Microsoft.ApplicationInsights/Extensibility/Implementation/Authentication/ReflectionCredentialEnvelope.cs @@ -50,7 +50,7 @@ public ReflectionCredentialEnvelope(object tokenCredential) /// A valid Azure.Core.AccessToken. public override string GetToken(CancellationToken cancellationToken = default) { - //SdkInternalOperationsMonitor.Enter(); + // SdkInternalOperationsMonitor.Enter(); try { return AzureCore.InvokeGetToken(this.tokenCredential, this.tokenRequestContext, cancellationToken); @@ -62,7 +62,7 @@ public override string GetToken(CancellationToken cancellationToken = default) } finally { - //SdkInternalOperationsMonitor.Exit(); + // SdkInternalOperationsMonitor.Exit(); } } From eebc0a3a91b28ac5c03343ccd90f30c6aed41719 Mon Sep 17 00:00:00 2001 From: Timothy Mothra Lee Date: Fri, 28 May 2021 16:54:56 -0700 Subject: [PATCH 08/10] comment about InternalOperations --- .../Authentication/CredentialEnvelope.cs | 6 ++++++ .../ReflectionCredentialEnvelope.cs | 16 ++++++---------- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/BASE/src/Microsoft.ApplicationInsights/Extensibility/Implementation/Authentication/CredentialEnvelope.cs b/BASE/src/Microsoft.ApplicationInsights/Extensibility/Implementation/Authentication/CredentialEnvelope.cs index ecc6931bf9..1f25ac8ccf 100644 --- a/BASE/src/Microsoft.ApplicationInsights/Extensibility/Implementation/Authentication/CredentialEnvelope.cs +++ b/BASE/src/Microsoft.ApplicationInsights/Extensibility/Implementation/Authentication/CredentialEnvelope.cs @@ -16,6 +16,9 @@ public abstract class CredentialEnvelope /// /// Gets an Azure.Core.AccessToken. /// + /// + /// Whomever uses this MUST verify that it's called within otherwise dependency calls will be tracked. + /// /// The System.Threading.CancellationToken to use. /// A valid Azure.Core.AccessToken. public abstract string GetToken(CancellationToken cancellationToken = default); @@ -23,6 +26,9 @@ public abstract class CredentialEnvelope /// /// Gets an Azure.Core.AccessToken. /// + /// + /// Whomever uses this MUST verify that it's called within otherwise dependency calls will be tracked. + /// /// The System.Threading.CancellationToken to use. /// A valid Azure.Core.AccessToken. public abstract Task GetTokenAsync(CancellationToken cancellationToken = default); diff --git a/BASE/src/Microsoft.ApplicationInsights/Extensibility/Implementation/Authentication/ReflectionCredentialEnvelope.cs b/BASE/src/Microsoft.ApplicationInsights/Extensibility/Implementation/Authentication/ReflectionCredentialEnvelope.cs index d351e192fa..b186c42cd2 100644 --- a/BASE/src/Microsoft.ApplicationInsights/Extensibility/Implementation/Authentication/ReflectionCredentialEnvelope.cs +++ b/BASE/src/Microsoft.ApplicationInsights/Extensibility/Implementation/Authentication/ReflectionCredentialEnvelope.cs @@ -46,11 +46,13 @@ public ReflectionCredentialEnvelope(object tokenCredential) /// /// Gets an Azure.Core.AccessToken. /// + /// + /// Whomever uses this MUST verify that it's called within otherwise dependency calls will be tracked. + /// /// The System.Threading.CancellationToken to use. /// A valid Azure.Core.AccessToken. public override string GetToken(CancellationToken cancellationToken = default) { - // SdkInternalOperationsMonitor.Enter(); try { return AzureCore.InvokeGetToken(this.tokenCredential, this.tokenRequestContext, cancellationToken); @@ -60,20 +62,18 @@ public override string GetToken(CancellationToken cancellationToken = default) CoreEventSource.Log.FailedToGetToken(ex.ToInvariantString()); return null; } - finally - { - // SdkInternalOperationsMonitor.Exit(); - } } /// /// Gets an Azure.Core.AccessToken. /// + /// + /// Whomever uses this MUST verify that it's called within otherwise dependency calls will be tracked. + /// /// The System.Threading.CancellationToken to use. /// A valid Azure.Core.AccessToken. public override async Task GetTokenAsync(CancellationToken cancellationToken = default) { - SdkInternalOperationsMonitor.Enter(); try { return await AzureCore.InvokeGetTokenAsync(this.tokenCredential, this.tokenRequestContext, cancellationToken).ConfigureAwait(false); @@ -83,10 +83,6 @@ public override async Task GetTokenAsync(CancellationToken cancellationT CoreEventSource.Log.FailedToGetToken(ex.ToInvariantString()); return null; } - finally - { - SdkInternalOperationsMonitor.Exit(); - } } /// From 8063dcf561e3c7e7da27abdb164c0ebfda8f0e77 Mon Sep 17 00:00:00 2001 From: Timothy Mothra Lee Date: Fri, 28 May 2021 16:55:05 -0700 Subject: [PATCH 09/10] cleanup --- .../Implementation/AzureSdk/AzureSdkDiagnosticsEventHandler.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/WEB/Src/DependencyCollector/DependencyCollector/Implementation/AzureSdk/AzureSdkDiagnosticsEventHandler.cs b/WEB/Src/DependencyCollector/DependencyCollector/Implementation/AzureSdk/AzureSdkDiagnosticsEventHandler.cs index 49a886fbb9..7f351630e4 100644 --- a/WEB/Src/DependencyCollector/DependencyCollector/Implementation/AzureSdk/AzureSdkDiagnosticsEventHandler.cs +++ b/WEB/Src/DependencyCollector/DependencyCollector/Implementation/AzureSdk/AzureSdkDiagnosticsEventHandler.cs @@ -40,8 +40,7 @@ public override void OnEvent(KeyValuePair evnt, DiagnosticListen { if (SdkInternalOperationsMonitor.IsEntered()) { - // Now that Application Insights supports AAD, we need to check if an internal operation is being caught here. - // type = "InProc | Microsoft.AAD" + // Because we support AAD, we must to check if an internal operation is being caught here (type = "InProc | Microsoft.AAD"). return; } From c4dbc45d4c6b8ca265b23846a156b6d33df3d4f0 Mon Sep 17 00:00:00 2001 From: Timothy Mothra Lee Date: Fri, 28 May 2021 17:38:33 -0700 Subject: [PATCH 10/10] update comment --- .../Authentication/ReflectionCredentialEnvelope.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/BASE/src/Microsoft.ApplicationInsights/Extensibility/Implementation/Authentication/ReflectionCredentialEnvelope.cs b/BASE/src/Microsoft.ApplicationInsights/Extensibility/Implementation/Authentication/ReflectionCredentialEnvelope.cs index b186c42cd2..d4a9a49846 100644 --- a/BASE/src/Microsoft.ApplicationInsights/Extensibility/Implementation/Authentication/ReflectionCredentialEnvelope.cs +++ b/BASE/src/Microsoft.ApplicationInsights/Extensibility/Implementation/Authentication/ReflectionCredentialEnvelope.cs @@ -53,6 +53,7 @@ public ReflectionCredentialEnvelope(object tokenCredential) /// A valid Azure.Core.AccessToken. public override string GetToken(CancellationToken cancellationToken = default) { + // TODO: NEED TO FULLY TEST IF WE NEED TO CALL SdkInternalOperationsMonitor.Enter try { return AzureCore.InvokeGetToken(this.tokenCredential, this.tokenRequestContext, cancellationToken); @@ -74,6 +75,7 @@ public override string GetToken(CancellationToken cancellationToken = default) /// A valid Azure.Core.AccessToken. public override async Task GetTokenAsync(CancellationToken cancellationToken = default) { + // TODO: NEED TO FULLY TEST IF WE NEED TO CALL SdkInternalOperationsMonitor.Enter try { return await AzureCore.InvokeGetTokenAsync(this.tokenCredential, this.tokenRequestContext, cancellationToken).ConfigureAwait(false);