[Fixes #105] Disable caching when response uses antiforgery
This commit is contained in:
parent
08cb67b7e4
commit
3fc090e2fe
|
|
@ -14,7 +14,8 @@ namespace Microsoft.AspNetCore.Antiforgery
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Generates an <see cref="AntiforgeryTokenSet"/> for this request and stores the cookie token
|
/// Generates an <see cref="AntiforgeryTokenSet"/> for this request and stores the cookie token
|
||||||
/// in the response.
|
/// in the response. This operation also sets the "Cache-control" and "Pragma" headers to "no-cache" and
|
||||||
|
/// the "X-Frame-Options" header to "SAMEORIGIN".
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="httpContext">The <see cref="HttpContext"/> associated with the current request.</param>
|
/// <param name="httpContext">The <see cref="HttpContext"/> associated with the current request.</param>
|
||||||
/// <returns>An <see cref="AntiforgeryTokenSet" /> with tokens for the response.</returns>
|
/// <returns>An <see cref="AntiforgeryTokenSet" /> with tokens for the response.</returns>
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
|
||||||
private static readonly Action<ILogger, Exception> _newCookieToken;
|
private static readonly Action<ILogger, Exception> _newCookieToken;
|
||||||
private static readonly Action<ILogger, Exception> _reusedCookieToken;
|
private static readonly Action<ILogger, Exception> _reusedCookieToken;
|
||||||
private static readonly Action<ILogger, Exception> _tokenDeserializeException;
|
private static readonly Action<ILogger, Exception> _tokenDeserializeException;
|
||||||
|
private static readonly Action<ILogger, Exception> _responseCacheHeadersOverridenToNoCache;
|
||||||
|
|
||||||
static AntiforgeryLoggerExtensions()
|
static AntiforgeryLoggerExtensions()
|
||||||
{
|
{
|
||||||
|
|
@ -47,6 +48,11 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
|
||||||
LogLevel.Error,
|
LogLevel.Error,
|
||||||
7,
|
7,
|
||||||
"An exception was thrown while deserializing the token.");
|
"An exception was thrown while deserializing the token.");
|
||||||
|
_responseCacheHeadersOverridenToNoCache = LoggerMessage.Define(
|
||||||
|
LogLevel.Warning,
|
||||||
|
8,
|
||||||
|
"The 'Cache-Control' and 'Pragma' headers have been overridden and set to 'no-cache' to prevent " +
|
||||||
|
"caching of this response. Any response that uses antiforgery should not be cached.");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void ValidationFailed(this ILogger logger, string message)
|
public static void ValidationFailed(this ILogger logger, string message)
|
||||||
|
|
@ -83,5 +89,10 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
|
||||||
{
|
{
|
||||||
_tokenDeserializeException(logger, exception);
|
_tokenDeserializeException(logger, exception);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void ResponseCacheHeadersOverridenToNoCache(this ILogger logger)
|
||||||
|
{
|
||||||
|
_responseCacheHeadersOverridenToNoCache(logger, null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
|
using Microsoft.Net.Http.Headers;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Antiforgery.Internal
|
namespace Microsoft.AspNetCore.Antiforgery.Internal
|
||||||
{
|
{
|
||||||
|
|
@ -66,6 +67,10 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Explicitly set the cache headers to 'no-cache'. This could override any user set value but this is fine
|
||||||
|
// as a response with antiforgery token must never be cached.
|
||||||
|
SetDoNotCacheHeaders(httpContext);
|
||||||
|
|
||||||
return tokenSet;
|
return tokenSet;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -237,6 +242,10 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
|
||||||
{
|
{
|
||||||
_logger.ReusedCookieToken();
|
_logger.ReusedCookieToken();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Explicitly set the cache headers to 'no-cache'. This could override any user set value but this is fine
|
||||||
|
// as a response with antiforgery token must never be cached.
|
||||||
|
SetDoNotCacheHeaders(httpContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SaveCookieTokenAndHeader(HttpContext httpContext, string cookieToken)
|
private void SaveCookieTokenAndHeader(HttpContext httpContext, string cookieToken)
|
||||||
|
|
@ -358,6 +367,43 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
|
||||||
return antiforgeryFeature;
|
return antiforgeryFeature;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void SetDoNotCacheHeaders(HttpContext httpContext)
|
||||||
|
{
|
||||||
|
// Since antifogery token generation is not very obvious to the end users (ex: MVC's form tag generates them
|
||||||
|
// by default), log a warning to let users know of the change in behavior to any cache headers they might
|
||||||
|
// have set explicitly.
|
||||||
|
LogCacheHeaderOverrideWarning(httpContext.Response);
|
||||||
|
|
||||||
|
httpContext.Response.Headers[HeaderNames.CacheControl] = "no-cache";
|
||||||
|
httpContext.Response.Headers[HeaderNames.Pragma] = "no-cache";
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LogCacheHeaderOverrideWarning(HttpResponse response)
|
||||||
|
{
|
||||||
|
var logWarning = false;
|
||||||
|
CacheControlHeaderValue cacheControlHeaderValue;
|
||||||
|
if (CacheControlHeaderValue.TryParse(response.Headers[HeaderNames.CacheControl], out cacheControlHeaderValue))
|
||||||
|
{
|
||||||
|
if (!cacheControlHeaderValue.NoCache)
|
||||||
|
{
|
||||||
|
logWarning = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var pragmaHeader = response.Headers[HeaderNames.Pragma];
|
||||||
|
if (!logWarning
|
||||||
|
&& !string.IsNullOrEmpty(pragmaHeader)
|
||||||
|
&& string.Compare(pragmaHeader, "no-cache", ignoreCase: true) != 0)
|
||||||
|
{
|
||||||
|
logWarning = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (logWarning)
|
||||||
|
{
|
||||||
|
_logger.ResponseCacheHeadersOverridenToNoCache();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private AntiforgeryTokenSet Serialize(IAntiforgeryFeature antiforgeryFeature)
|
private AntiforgeryTokenSet Serialize(IAntiforgeryFeature antiforgeryFeature)
|
||||||
{
|
{
|
||||||
// Should only be called after new tokens have been generated.
|
// Should only be called after new tokens have been generated.
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Microsoft.AspNetCore.DataProtection": "1.1.0-*",
|
"Microsoft.AspNetCore.DataProtection": "1.1.0-*",
|
||||||
"Microsoft.AspNetCore.Http.Abstractions": "1.1.0-*",
|
"Microsoft.AspNetCore.Http.Abstractions": "1.1.0-*",
|
||||||
|
"Microsoft.AspNetCore.Http.Extensions": "1.1.0-*",
|
||||||
"Microsoft.AspNetCore.WebUtilities": "1.1.0-*",
|
"Microsoft.AspNetCore.WebUtilities": "1.1.0-*",
|
||||||
"Microsoft.Extensions.ObjectPool": "1.1.0-*",
|
"Microsoft.Extensions.ObjectPool": "1.1.0-*",
|
||||||
"NETStandard.Library": "1.6.1-*"
|
"NETStandard.Library": "1.6.1-*"
|
||||||
|
|
|
||||||
|
|
@ -2,12 +2,15 @@
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Linq;
|
||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Microsoft.Extensions.Logging.Testing;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
|
using Microsoft.Net.Http.Headers;
|
||||||
using Moq;
|
using Moq;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
|
|
@ -15,6 +18,10 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
|
||||||
{
|
{
|
||||||
public class DefaultAntiforgeryTest
|
public class DefaultAntiforgeryTest
|
||||||
{
|
{
|
||||||
|
private const string ResponseCacheHeadersOverrideWarningMessage =
|
||||||
|
"The 'Cache-Control' and 'Pragma' headers have been overridden and set to 'no-cache' to prevent caching " +
|
||||||
|
"of this response. Any response that uses antiforgery should not be cached.";
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task ChecksSSL_ValidateRequestAsync_Throws()
|
public async Task ChecksSSL_ValidateRequestAsync_Throws()
|
||||||
{
|
{
|
||||||
|
|
@ -275,6 +282,67 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
|
||||||
Assert.Equal(context.TestTokenSet.FormTokenString, antiforgeryFeature.NewRequestTokenString);
|
Assert.Equal(context.TestTokenSet.FormTokenString, antiforgeryFeature.NewRequestTokenString);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetAndStoreTokens_ExistingValidCookieToken_NotOverriden_AndSetsDoNotCacheHeaders()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var antiforgeryFeature = new AntiforgeryFeature();
|
||||||
|
var context = CreateMockContext(
|
||||||
|
new AntiforgeryOptions(),
|
||||||
|
useOldCookie: true,
|
||||||
|
isOldCookieValid: true,
|
||||||
|
antiforgeryFeature: antiforgeryFeature);
|
||||||
|
var antiforgery = GetAntiforgery(context);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var tokenSet = antiforgery.GetAndStoreTokens(context.HttpContext);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
// We shouldn't have saved the cookie because it already existed.
|
||||||
|
context.TokenStore.Verify(
|
||||||
|
t => t.SaveCookieToken(It.IsAny<HttpContext>(), It.IsAny<string>()),
|
||||||
|
Times.Never);
|
||||||
|
|
||||||
|
Assert.Null(tokenSet.CookieToken);
|
||||||
|
Assert.Equal(context.TestTokenSet.FormTokenString, tokenSet.RequestToken);
|
||||||
|
|
||||||
|
Assert.NotNull(antiforgeryFeature);
|
||||||
|
Assert.Equal(context.TestTokenSet.OldCookieToken, antiforgeryFeature.CookieToken);
|
||||||
|
Assert.Equal("no-cache", context.HttpContext.Response.Headers[HeaderNames.CacheControl]);
|
||||||
|
Assert.Equal("no-cache", context.HttpContext.Response.Headers[HeaderNames.Pragma]);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetAndStoreTokens_ExistingCachingHeaders_Overriden()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var antiforgeryFeature = new AntiforgeryFeature();
|
||||||
|
var context = CreateMockContext(
|
||||||
|
new AntiforgeryOptions(),
|
||||||
|
useOldCookie: true,
|
||||||
|
isOldCookieValid: true,
|
||||||
|
antiforgeryFeature: antiforgeryFeature);
|
||||||
|
var antiforgery = GetAntiforgery(context);
|
||||||
|
context.HttpContext.Response.Headers["Cache-Control"] = "public";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var tokenSet = antiforgery.GetAndStoreTokens(context.HttpContext);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
// We shouldn't have saved the cookie because it already existed.
|
||||||
|
context.TokenStore.Verify(
|
||||||
|
t => t.SaveCookieToken(It.IsAny<HttpContext>(), It.IsAny<string>()),
|
||||||
|
Times.Never);
|
||||||
|
|
||||||
|
Assert.Null(tokenSet.CookieToken);
|
||||||
|
Assert.Equal(context.TestTokenSet.FormTokenString, tokenSet.RequestToken);
|
||||||
|
|
||||||
|
Assert.NotNull(antiforgeryFeature);
|
||||||
|
Assert.Equal(context.TestTokenSet.OldCookieToken, antiforgeryFeature.CookieToken);
|
||||||
|
Assert.Equal("no-cache", context.HttpContext.Response.Headers[HeaderNames.CacheControl]);
|
||||||
|
Assert.Equal("no-cache", context.HttpContext.Response.Headers[HeaderNames.Pragma]);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void GetAndStoreTokens_NoExistingCookieToken_Saved()
|
public void GetAndStoreTokens_NoExistingCookieToken_Saved()
|
||||||
{
|
{
|
||||||
|
|
@ -309,6 +377,36 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
|
||||||
Assert.True(antiforgeryFeature.HaveStoredNewCookieToken);
|
Assert.True(antiforgeryFeature.HaveStoredNewCookieToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetAndStoreTokens_NoExistingCookieToken_Saved_AndSetsDoNotCacheHeaders()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var antiforgeryFeature = new AntiforgeryFeature();
|
||||||
|
var context = CreateMockContext(
|
||||||
|
new AntiforgeryOptions(),
|
||||||
|
useOldCookie: false,
|
||||||
|
isOldCookieValid: false,
|
||||||
|
antiforgeryFeature: antiforgeryFeature);
|
||||||
|
var antiforgery = GetAntiforgery(context);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var tokenSet = antiforgery.GetAndStoreTokens(context.HttpContext);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
context.TokenStore.Verify(
|
||||||
|
t => t.SaveCookieToken(It.IsAny<HttpContext>(), context.TestTokenSet.NewCookieTokenString),
|
||||||
|
Times.Once);
|
||||||
|
|
||||||
|
Assert.Equal(context.TestTokenSet.NewCookieTokenString, tokenSet.CookieToken);
|
||||||
|
Assert.Equal(context.TestTokenSet.FormTokenString, tokenSet.RequestToken);
|
||||||
|
|
||||||
|
Assert.NotNull(antiforgeryFeature);
|
||||||
|
Assert.True(antiforgeryFeature.HaveDeserializedCookieToken);
|
||||||
|
Assert.Equal(context.TestTokenSet.OldCookieToken, antiforgeryFeature.CookieToken);
|
||||||
|
Assert.Equal("no-cache", context.HttpContext.Response.Headers[HeaderNames.CacheControl]);
|
||||||
|
Assert.Equal("no-cache", context.HttpContext.Response.Headers[HeaderNames.Pragma]);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void GetAndStoreTokens_DoesNotSerializeTwice()
|
public void GetAndStoreTokens_DoesNotSerializeTwice()
|
||||||
{
|
{
|
||||||
|
|
@ -808,6 +906,76 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
|
||||||
Assert.Equal(expectedHeaderValue, xFrameOptions);
|
Assert.Equal(expectedHeaderValue, xFrameOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void SetCookieTokenAndHeader_NewCookieToken_SetsDoNotCacheHeaders()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var options = new AntiforgeryOptions();
|
||||||
|
var antiforgeryFeature = new AntiforgeryFeature();
|
||||||
|
|
||||||
|
// Generate a new cookie.
|
||||||
|
var context = CreateMockContext(
|
||||||
|
options,
|
||||||
|
useOldCookie: false,
|
||||||
|
isOldCookieValid: false,
|
||||||
|
antiforgeryFeature: antiforgeryFeature);
|
||||||
|
var antiforgery = GetAntiforgery(context);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
antiforgery.SetCookieTokenAndHeader(context.HttpContext);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal("no-cache", context.HttpContext.Response.Headers["Cache-Control"]);
|
||||||
|
Assert.Equal("no-cache", context.HttpContext.Response.Headers["Pragma"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void SetCookieTokenAndHeader_ValidOldCookieToken_SetsDoNotCacheHeaders()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var options = new AntiforgeryOptions();
|
||||||
|
var antiforgeryFeature = new AntiforgeryFeature();
|
||||||
|
|
||||||
|
// Generate a new cookie.
|
||||||
|
var context = CreateMockContext(
|
||||||
|
options,
|
||||||
|
useOldCookie: true,
|
||||||
|
isOldCookieValid: true,
|
||||||
|
antiforgeryFeature: antiforgeryFeature);
|
||||||
|
var antiforgery = GetAntiforgery(context);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
antiforgery.SetCookieTokenAndHeader(context.HttpContext);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal("no-cache", context.HttpContext.Response.Headers["Cache-Control"]);
|
||||||
|
Assert.Equal("no-cache", context.HttpContext.Response.Headers["Pragma"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void SetCookieTokenAndHeader_OverridesExistingCachingHeaders()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var options = new AntiforgeryOptions();
|
||||||
|
var antiforgeryFeature = new AntiforgeryFeature();
|
||||||
|
|
||||||
|
// Generate a new cookie.
|
||||||
|
var context = CreateMockContext(
|
||||||
|
options,
|
||||||
|
useOldCookie: true,
|
||||||
|
isOldCookieValid: true,
|
||||||
|
antiforgeryFeature: antiforgeryFeature);
|
||||||
|
var antiforgery = GetAntiforgery(context);
|
||||||
|
context.HttpContext.Response.Headers["Cache-Control"] = "public";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
antiforgery.SetCookieTokenAndHeader(context.HttpContext);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal("no-cache", context.HttpContext.Response.Headers["Cache-Control"]);
|
||||||
|
Assert.Equal("no-cache", context.HttpContext.Response.Headers["Pragma"]);
|
||||||
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[InlineData(false, "SAMEORIGIN")]
|
[InlineData(false, "SAMEORIGIN")]
|
||||||
[InlineData(true, null)]
|
[InlineData(true, null)]
|
||||||
|
|
@ -965,6 +1133,110 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal
|
||||||
context.TokenSerializer.Verify(s => s.Deserialize(null), Times.Never);
|
context.TokenSerializer.Verify(s => s.Deserialize(null), Times.Never);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetAndStoreTokens_DoesNotLogWarning_IfNoExistingCacheHeadersPresent()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var testSink = new TestSink();
|
||||||
|
var loggerFactory = new Mock<ILoggerFactory>();
|
||||||
|
loggerFactory
|
||||||
|
.Setup(lf => lf.CreateLogger(typeof(DefaultAntiforgery).FullName))
|
||||||
|
.Returns(new TestLogger("test logger", testSink, enabled: true));
|
||||||
|
var services = new ServiceCollection();
|
||||||
|
services.AddSingleton(loggerFactory.Object);
|
||||||
|
var antiforgeryFeature = new AntiforgeryFeature();
|
||||||
|
var context = CreateMockContext(
|
||||||
|
new AntiforgeryOptions(),
|
||||||
|
useOldCookie: false,
|
||||||
|
isOldCookieValid: false,
|
||||||
|
antiforgeryFeature: antiforgeryFeature);
|
||||||
|
context.HttpContext.RequestServices = services.BuildServiceProvider();
|
||||||
|
var antiforgery = GetAntiforgery(context);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var tokenSet = antiforgery.GetAndStoreTokens(context.HttpContext);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var hasWarningMessage = testSink.Writes
|
||||||
|
.Where(wc => wc.LogLevel == LogLevel.Warning)
|
||||||
|
.Select(wc => wc.State?.ToString())
|
||||||
|
.Contains(ResponseCacheHeadersOverrideWarningMessage);
|
||||||
|
Assert.False(hasWarningMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("Cache-Control", "Public")]
|
||||||
|
[InlineData("Cache-Control", "PuBlic")]
|
||||||
|
[InlineData("Cache-Control", "Private")]
|
||||||
|
[InlineData("Cache-Control", "PriVate")]
|
||||||
|
[InlineData("Cache-Control", "No-Store")]
|
||||||
|
[InlineData("Cache-Control", "No-store")]
|
||||||
|
[InlineData("Pragma", "Foo")]
|
||||||
|
public void GetAndStoreTokens_LogsWarning_NonNoCacheHeadersAlreadyPresent(string headerName, string headerValue)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var testSink = new TestSink();
|
||||||
|
var loggerFactory = new Mock<ILoggerFactory>();
|
||||||
|
loggerFactory
|
||||||
|
.Setup(lf => lf.CreateLogger(typeof(DefaultAntiforgery).FullName))
|
||||||
|
.Returns(new TestLogger("test logger", testSink, enabled: true));
|
||||||
|
var services = new ServiceCollection();
|
||||||
|
services.AddSingleton(loggerFactory.Object);
|
||||||
|
var antiforgeryFeature = new AntiforgeryFeature();
|
||||||
|
var context = CreateMockContext(
|
||||||
|
new AntiforgeryOptions(),
|
||||||
|
useOldCookie: false,
|
||||||
|
isOldCookieValid: false,
|
||||||
|
antiforgeryFeature: antiforgeryFeature);
|
||||||
|
context.HttpContext.RequestServices = services.BuildServiceProvider();
|
||||||
|
var antiforgery = GetAntiforgery(context);
|
||||||
|
context.HttpContext.Response.Headers[headerName] = headerValue;
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var tokenSet = antiforgery.GetAndStoreTokens(context.HttpContext);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var hasWarningMessage = testSink.Writes
|
||||||
|
.Where(wc => wc.LogLevel == LogLevel.Warning)
|
||||||
|
.Select(wc => wc.State?.ToString())
|
||||||
|
.Contains(ResponseCacheHeadersOverrideWarningMessage);
|
||||||
|
Assert.True(hasWarningMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("Cache-Control", "no-cache")]
|
||||||
|
[InlineData("Pragma", "no-cache")]
|
||||||
|
public void GetAndStoreTokens_DoesNotLogsWarning_ForNoCacheHeaders_AlreadyPresent(string headerName, string headerValue)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var testSink = new TestSink();
|
||||||
|
var loggerFactory = new Mock<ILoggerFactory>();
|
||||||
|
loggerFactory
|
||||||
|
.Setup(lf => lf.CreateLogger(typeof(DefaultAntiforgery).FullName))
|
||||||
|
.Returns(new TestLogger("test logger", testSink, enabled: true));
|
||||||
|
var services = new ServiceCollection();
|
||||||
|
services.AddSingleton(loggerFactory.Object);
|
||||||
|
var antiforgeryFeature = new AntiforgeryFeature();
|
||||||
|
var context = CreateMockContext(
|
||||||
|
new AntiforgeryOptions(),
|
||||||
|
useOldCookie: false,
|
||||||
|
isOldCookieValid: false,
|
||||||
|
antiforgeryFeature: antiforgeryFeature);
|
||||||
|
context.HttpContext.RequestServices = services.BuildServiceProvider();
|
||||||
|
var antiforgery = GetAntiforgery(context);
|
||||||
|
context.HttpContext.Response.Headers[headerName] = headerValue;
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var tokenSet = antiforgery.GetAndStoreTokens(context.HttpContext);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var hasWarningMessage = testSink.Writes
|
||||||
|
.Where(wc => wc.LogLevel == LogLevel.Warning)
|
||||||
|
.Select(wc => wc.State?.ToString())
|
||||||
|
.Contains(ResponseCacheHeadersOverrideWarningMessage);
|
||||||
|
Assert.False(hasWarningMessage);
|
||||||
|
}
|
||||||
|
|
||||||
private DefaultAntiforgery GetAntiforgery(
|
private DefaultAntiforgery GetAntiforgery(
|
||||||
HttpContext httpContext,
|
HttpContext httpContext,
|
||||||
AntiforgeryOptions options = null,
|
AntiforgeryOptions options = null,
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@
|
||||||
"Microsoft.AspNetCore.Testing": "1.1.0-*",
|
"Microsoft.AspNetCore.Testing": "1.1.0-*",
|
||||||
"Microsoft.Extensions.DependencyInjection": "1.1.0-*",
|
"Microsoft.Extensions.DependencyInjection": "1.1.0-*",
|
||||||
"Microsoft.Extensions.Logging": "1.1.0-*",
|
"Microsoft.Extensions.Logging": "1.1.0-*",
|
||||||
|
"Microsoft.Extensions.Logging.Testing": "1.1.0-*",
|
||||||
"Microsoft.Extensions.WebEncoders": "1.1.0-*",
|
"Microsoft.Extensions.WebEncoders": "1.1.0-*",
|
||||||
"Moq": "4.6.36-*",
|
"Moq": "4.6.36-*",
|
||||||
"xunit": "2.2.0-*"
|
"xunit": "2.2.0-*"
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue