|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Collections.Generic;
using Microsoft.AspNetCore.Diagnostics.Logging;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.Extensions.Compliance.Classification;
using Microsoft.Extensions.Compliance.Redaction;
using Microsoft.Extensions.Compliance.Testing;
using Moq;
using Xunit;
namespace Microsoft.AspNetCore.Diagnostics.Logging.Test;
public class RequestHeadersEnricherTests
{
private const string HeaderKey1 = "X-RequestID";
private const string HeaderKey2 = "Host";
private const string HeaderKey3 = "NullHeader";
private const string HeaderKey4 = "X-Platform";
private const string RequestId = "RequestIdTestValue";
private const string TestValue = "TestValue";
private const string NormalizedHeaderKey1 = HttpLoggingTagNames.RequestHeaderPrefix + "x-requestid";
private const string NormalizedHeaderKey2 = HttpLoggingTagNames.RequestHeaderPrefix + "host";
private const string NormalizedHeaderKey3 = HttpLoggingTagNames.RequestHeaderPrefix + "nullheader";
private const string NormalizedHeaderKey4 = HttpLoggingTagNames.RequestHeaderPrefix + "x-platform";
private readonly Mock<IHttpContextAccessor> _accessorMock;
private readonly Mock<IRedactorProvider> _redactorProviderMock;
public RequestHeadersEnricherTests()
{
var headers = new HeaderDictionary
{
{ HeaderKey1, RequestId },
{ HeaderKey2, string.Empty },
{ HeaderKey3, (string)null! },
{ HeaderKey4, TestValue },
};
var featureCollection = new FeatureCollection();
var httpContextMock = new Mock<HttpContext>(MockBehavior.Strict);
httpContextMock.SetupGet(c => c.Request.Headers).Returns(headers);
httpContextMock.SetupGet(c => c.Request.HttpContext).Returns(httpContextMock.Object);
httpContextMock.SetupGet(c => c.Features).Returns(featureCollection);
_accessorMock = new Mock<IHttpContextAccessor>(MockBehavior.Strict);
_accessorMock.SetupGet(r => r.HttpContext).Returns(httpContextMock.Object);
var redactor = FakeRedactor.Create();
_redactorProviderMock = new Mock<IRedactorProvider>(MockBehavior.Default);
_redactorProviderMock.SetReturnsDefault<Redactor>(redactor);
}
[Fact]
public void RequestHeadersEnricher_GivenDisabledEnricherOptions_HeaderKeysDataClasses_DoesNotEnrich()
{
// Arrange
var options = new RequestHeadersLogEnricherOptions();
var enricher = new RequestHeadersLogEnricher(_accessorMock.Object, options.ToOptions(), _redactorProviderMock.Object);
var enrichedProperties = new TestLogEnrichmentTagCollector();
// Act
enricher.Enrich(enrichedProperties);
var enrichedState = enrichedProperties.Properties;
// Assert
Assert.Empty(enrichedState);
}
[Fact]
public void RequestHeadersEnricher_GivenEnricherOptions_HeaderKeysDataClasses_Enriches()
{
// Arrange
var options = new RequestHeadersLogEnricherOptions
{
HeadersDataClasses =
{
{ HeaderKey1, FakeTaxonomy.PrivateData },
{ HeaderKey4, FakeTaxonomy.PublicData }
}
};
Mock<IRedactorProvider> redactorProviderMock = new Mock<IRedactorProvider>();
redactorProviderMock.Setup(x => x.GetRedactor(FakeTaxonomy.PublicData))
.Returns(new FakeRedactor());
redactorProviderMock.Setup(x => x.GetRedactor(FakeTaxonomy.PrivateData))
.Returns(FakeRedactor.Create(new FakeRedactorOptions { RedactionFormat = "redacted:{0}" }));
var enricher = new RequestHeadersLogEnricher(_accessorMock.Object, options.ToOptions(), redactorProviderMock.Object);
var enrichedProperties = new TestLogEnrichmentTagCollector();
// Act
enricher.Enrich(enrichedProperties);
var enrichedState = enrichedProperties.Properties;
// Assert
Assert.True(enrichedState.Count == 2);
Assert.Equal($"redacted:{RequestId}", enrichedState[NormalizedHeaderKey1].ToString());
Assert.Equal(TestValue, enrichedState[NormalizedHeaderKey4].ToString());
}
[Fact]
public void RequestHeadersEnricher_GivenEnricherOptions_OneHeaderValueIsEmpty_HeaderKeysDataClasses_PartiallyEnriches()
{
// Arrange
var options = new RequestHeadersLogEnricherOptions
{
HeadersDataClasses =
{
{ HeaderKey1, FakeTaxonomy.PrivateData },
{ HeaderKey2, FakeTaxonomy.PublicData }
}
};
Mock<IRedactorProvider> redactorProviderMock = new Mock<IRedactorProvider>();
redactorProviderMock.Setup(x => x.GetRedactor(FakeTaxonomy.PrivateData))
.Returns(FakeRedactor.Create(new FakeRedactorOptions { RedactionFormat = "REDACTED:{0}" }));
var enricher = new RequestHeadersLogEnricher(_accessorMock.Object, options.ToOptions(), redactorProviderMock.Object);
var enrichedProperties = new TestLogEnrichmentTagCollector();
// Act
enricher.Enrich(enrichedProperties);
var enrichedState = enrichedProperties.Properties;
// Assert
Assert.Single(enrichedState);
Assert.Equal($"REDACTED:{RequestId}", enrichedState[NormalizedHeaderKey1].ToString());
Assert.False(enrichedState.ContainsKey(NormalizedHeaderKey2));
}
[Fact]
public void RequestHeadersEnricher_GivenEnricherOptions_OneHeaderValueIsNull_HeaderKeysDataClasses_PartiallyEnriches()
{
// Arrange
var options = new RequestHeadersLogEnricherOptions
{
HeadersDataClasses =
{
{ HeaderKey1, FakeTaxonomy.PublicData },
{ HeaderKey3, FakeTaxonomy.PublicData }
}
};
var enricher = new RequestHeadersLogEnricher(_accessorMock.Object, options.ToOptions(), _redactorProviderMock.Object);
var enrichedProperties = new TestLogEnrichmentTagCollector();
// Act
enricher.Enrich(enrichedProperties);
var enrichedState = enrichedProperties.Properties;
// Assert
Assert.Single(enrichedState);
Assert.Equal(RequestId, enrichedState[NormalizedHeaderKey1].ToString());
Assert.False(enrichedState.ContainsKey(NormalizedHeaderKey3));
}
[Fact]
public void RequestHeadersEnricher_GivenEnricherOptions_OneHeaderValueIsMissing_HeaderKeysDataClasses_PartiallyEnriches()
{
// Arrange
var headerKey2 = "header_does_not_exist";
var options = new RequestHeadersLogEnricherOptions
{
HeadersDataClasses =
{
{ HeaderKey1, FakeTaxonomy.PublicData },
{ headerKey2, FakeTaxonomy.PublicData }
}
};
var enricher = new RequestHeadersLogEnricher(_accessorMock.Object, options.ToOptions(), _redactorProviderMock.Object);
var enrichedProperties = new TestLogEnrichmentTagCollector();
// Act
enricher.Enrich(enrichedProperties);
var enrichedState = enrichedProperties.Properties;
// Assert
Assert.Equal(RequestId, enrichedState[NormalizedHeaderKey1].ToString());
Assert.False(enrichedState.ContainsKey(HttpLoggingTagNames.RequestHeaderPrefix + headerKey2));
}
[Fact]
public void RequestHeadersEnricher_GivenNullHttpContext_HeaderKeysDataClasses_DoesNotEnrich()
{
// Arrange
var options = new RequestHeadersLogEnricherOptions
{
HeadersDataClasses =
{
{ HeaderKey1, FakeTaxonomy.PublicData }
}
};
var accessorMock = new Mock<IHttpContextAccessor>(MockBehavior.Strict);
accessorMock.SetupGet(r => r.HttpContext).Returns((HttpContext)null!);
var enricher = new RequestHeadersLogEnricher(accessorMock.Object, options.ToOptions(), _redactorProviderMock.Object);
var enrichedProperties = new TestLogEnrichmentTagCollector();
// Act
enricher.Enrich(enrichedProperties);
var enrichedState = enrichedProperties.Properties;
// Assert
Assert.Empty(enrichedState);
}
[Fact]
public void RequestHeadersEnricher_GivenNullRequest_HeaderKeysDataClasses_DoesNotEnrich()
{
// Arrange
var options = new RequestHeadersLogEnricherOptions
{
HeadersDataClasses =
{
{ HeaderKey1, FakeTaxonomy.PublicData }
}
};
var featureCollection = new FeatureCollection();
var httpContextMock = new Mock<HttpContext>(MockBehavior.Strict);
httpContextMock.SetupGet(c => c.Request).Returns((HttpRequest)null!);
httpContextMock.SetupGet(c => c.Features).Returns(featureCollection);
var accessorMock = new Mock<IHttpContextAccessor>(MockBehavior.Strict);
accessorMock.SetupGet(r => r.HttpContext).Returns(httpContextMock.Object);
var enricher = new RequestHeadersLogEnricher(accessorMock.Object, options.ToOptions(), _redactorProviderMock.Object);
var enrichedProperties = new TestLogEnrichmentTagCollector();
// Act
enricher.Enrich(enrichedProperties);
var enrichedState = enrichedProperties.Properties;
// Assert
Assert.Empty(enrichedState);
}
[Fact]
public void RequestHeadersEnricher_ObjectDisposedExceptionIsHandled_DoesNotEnrich()
{
// Arrange
var httpContextMock = new Mock<HttpContext>(MockBehavior.Strict);
httpContextMock.SetupGet(c => c.Request.Headers).Throws(new ObjectDisposedException(""));
httpContextMock.SetupGet(c => c.Request.HttpContext).Returns(httpContextMock.Object);
httpContextMock.SetupGet(c => c.Features).Throws(new ObjectDisposedException(""));
var accessorMock = new Mock<IHttpContextAccessor>(MockBehavior.Strict);
accessorMock.SetupGet(r => r.HttpContext).Returns(httpContextMock.Object);
var options = new RequestHeadersLogEnricherOptions
{
HeadersDataClasses = new Dictionary<string, DataClassification>
{
{ HeaderKey1, FakeTaxonomy.PublicData }
}
};
Mock<IRedactorProvider> redactorProviderMock = new Mock<IRedactorProvider>();
redactorProviderMock.Setup(x => x.GetRedactor(FakeTaxonomy.PublicData))
.Returns(new FakeRedactor());
redactorProviderMock.Setup(x => x.GetRedactor(FakeTaxonomy.PrivateData))
.Returns(FakeRedactor.Create(new FakeRedactorOptions { RedactionFormat = "redacted:{0}" }));
var enricher = new RequestHeadersLogEnricher(accessorMock.Object, options.ToOptions(), redactorProviderMock.Object);
var enrichedProperties = new TestLogEnrichmentTagCollector();
// Act
enricher.Enrich(enrichedProperties);
var enrichedState = enrichedProperties.Properties;
// Assert
Assert.True(enrichedState.Count == 0);
}
}
|