forked from NuGet/NuGetGallery
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add CustomerResourceId to enrich push metrics with tenant ID (NuGet#7936
) Progress on NuGet/Engineering#3106
- Loading branch information
1 parent
74cdb70
commit 51e063c
Showing
13 changed files
with
236 additions
and
7 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
// Copyright (c) .NET Foundation. All rights reserved. | ||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | ||
|
||
namespace NuGetGallery.Authentication | ||
{ | ||
public static class MicrosoftClaims | ||
{ | ||
private const string ClaimsDomain = "http://schemas.microsoft.com/identity/claims/"; | ||
|
||
/// <summary> | ||
/// The claim URL for the claim that stores the user's tenant ID, based on the external credential. | ||
/// </summary> | ||
public const string TenantId = ClaimsDomain + "tenantid"; | ||
} | ||
} |
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
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
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
// Copyright (c) .NET Foundation. All rights reserved. | ||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | ||
|
||
using System; | ||
using System.Collections.Generic; | ||
using System.Web; | ||
using Microsoft.ApplicationInsights.Channel; | ||
using Microsoft.ApplicationInsights.DataContracts; | ||
using Microsoft.ApplicationInsights.Extensibility; | ||
|
||
namespace NuGetGallery | ||
{ | ||
public class CustomerResourceIdEnricher : ITelemetryInitializer | ||
{ | ||
private const string CustomerResourceId = "CustomerResourceId"; | ||
private const string Prefix = "/tenants/"; | ||
private static readonly string Empty = Prefix + Guid.Empty.ToString(); | ||
|
||
private static readonly HashSet<string> CustomMetricNames = new HashSet<string> | ||
{ | ||
TelemetryService.Events.PackagePush, | ||
TelemetryService.Events.PackagePushFailure, | ||
}; | ||
|
||
public void Initialize(ITelemetry telemetry) | ||
{ | ||
var metric = telemetry as MetricTelemetry; | ||
if (metric == null) | ||
{ | ||
return; | ||
} | ||
|
||
if (!CustomMetricNames.Contains(metric.Name)) | ||
{ | ||
return; | ||
} | ||
|
||
var itemTelemetry = telemetry as ISupportProperties; | ||
if (itemTelemetry == null) | ||
{ | ||
return; | ||
} | ||
|
||
var httpContext = GetHttpContext(); | ||
var tenantId = httpContext?.User?.Identity.GetTenantIdOrNull(); | ||
var customerResourceId = tenantId != null ? Prefix + tenantId : Empty; | ||
itemTelemetry.Properties[CustomerResourceId] = customerResourceId; | ||
} | ||
|
||
protected virtual HttpContextBase GetHttpContext() => HttpContextBaseExtensions.GetCurrent(); | ||
} | ||
} |
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
128 changes: 128 additions & 0 deletions
128
tests/NuGetGallery.Facts/Telemetry/CustomerResourceIdEnricherFacts.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,128 @@ | ||
// Copyright (c) .NET Foundation. All rights reserved. | ||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | ||
|
||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Security.Claims; | ||
using System.Security.Principal; | ||
using System.Web; | ||
using Microsoft.ApplicationInsights.Channel; | ||
using Microsoft.ApplicationInsights.DataContracts; | ||
using Moq; | ||
using NuGetGallery.Authentication; | ||
using Xunit; | ||
|
||
namespace NuGetGallery.Telemetry | ||
{ | ||
public class CustomerResourceIdEnricherFacts | ||
{ | ||
private class TestableCustomerResourceIdEnricher : CustomerResourceIdEnricher | ||
{ | ||
private readonly HttpContextBase _httpContextBase; | ||
|
||
public TestableCustomerResourceIdEnricher(HttpContextBase httpContextBase) | ||
{ | ||
_httpContextBase = httpContextBase; | ||
} | ||
|
||
protected override HttpContextBase GetHttpContext() | ||
{ | ||
return _httpContextBase; | ||
} | ||
} | ||
|
||
[Theory] | ||
[InlineData(typeof(RequestTelemetry), 0)] | ||
[InlineData(typeof(DependencyTelemetry), 0)] | ||
[InlineData(typeof(TraceTelemetry), 0)] | ||
[InlineData(typeof(ExceptionTelemetry), 0)] | ||
[InlineData(typeof(MetricTelemetry), 1)] | ||
public void EnrichesOnlyMetricTelemetry(Type telemetryType, int addedProperties) | ||
{ | ||
// Arrange | ||
var telemetry = (ITelemetry)Activator.CreateInstance(telemetryType); | ||
if (telemetry is MetricTelemetry metric) | ||
{ | ||
metric.Name = "PackagePush"; | ||
} | ||
|
||
var itemTelemetry = telemetry as ISupportProperties; | ||
itemTelemetry.Properties.Add("Test", "blala"); | ||
|
||
var enricher = CreateTestEnricher(new Dictionary<string, string> { { MicrosoftClaims.TenantId, "tenant-id" } }); | ||
|
||
// Act | ||
enricher.Initialize(telemetry); | ||
|
||
// Assert | ||
Assert.Equal(1 + addedProperties, itemTelemetry.Properties.Count); | ||
} | ||
|
||
[Fact] | ||
public void DoesNotEnrichMetricNotInAllowList() | ||
{ | ||
// Arrange | ||
var telemetry = new MetricTelemetry { Name = "SomethingElse" }; | ||
|
||
var enricher = CreateTestEnricher(new Dictionary<string, string> { { MicrosoftClaims.TenantId, "tenant-id" } }); | ||
|
||
// Act | ||
enricher.Initialize(telemetry); | ||
|
||
// Assert | ||
Assert.Empty(telemetry.Properties); | ||
} | ||
|
||
[Theory] | ||
[MemberData(nameof(MetricNames))] | ||
public void EnrichesTelemetryWithTenantId(string name) | ||
{ | ||
// Arrange | ||
var telemetry = new MetricTelemetry { Name = name }; | ||
|
||
var enricher = CreateTestEnricher(new Dictionary<string, string> { { MicrosoftClaims.TenantId, "tenant-id" } }); | ||
|
||
// Act | ||
enricher.Initialize(telemetry); | ||
|
||
// Assert | ||
Assert.Equal("/tenants/tenant-id", telemetry.Properties["CustomerResourceId"]); | ||
} | ||
|
||
[Theory] | ||
[MemberData(nameof(MetricNames))] | ||
public void EnrichesTelemetryWithEmptyWhenTenantIdIsNotInClaims(string name) | ||
{ | ||
// Arrange | ||
var telemetry = new MetricTelemetry { Name = name }; | ||
|
||
var enricher = CreateTestEnricher(new Dictionary<string, string>()); | ||
|
||
// Act | ||
enricher.Initialize(telemetry); | ||
|
||
// Assert | ||
Assert.Equal("/tenants/00000000-0000-0000-0000-000000000000", telemetry.Properties["CustomerResourceId"]); | ||
} | ||
|
||
private TestableCustomerResourceIdEnricher CreateTestEnricher(IReadOnlyDictionary<string, string> claims) | ||
{ | ||
var claimsIdentity = new ClaimsIdentity(claims.Select(x => new Claim(x.Key, x.Value))); | ||
|
||
var principal = new Mock<IPrincipal>(); | ||
principal.Setup(x => x.Identity).Returns(claimsIdentity); | ||
|
||
var httpContext = new Mock<HttpContextBase>(MockBehavior.Strict); | ||
httpContext.SetupGet(c => c.User).Returns(principal.Object); | ||
|
||
return new TestableCustomerResourceIdEnricher(httpContext.Object); | ||
} | ||
|
||
public static IEnumerable<object[]> MetricNames => new[] | ||
{ | ||
new object[] { "PackagePush" }, | ||
new object[] { "PackagePushFailure" }, | ||
}; | ||
} | ||
} |