diff --git a/src/Microsoft.AspNetCore.Antiforgery/Internal/AntiforgeryLoggerExtensions.cs b/src/Microsoft.AspNetCore.Antiforgery/Internal/AntiforgeryLoggerExtensions.cs new file mode 100644 index 0000000000..aae0b3176e --- /dev/null +++ b/src/Microsoft.AspNetCore.Antiforgery/Internal/AntiforgeryLoggerExtensions.cs @@ -0,0 +1,77 @@ +// 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 Microsoft.Extensions.Logging; + +namespace Microsoft.AspNetCore.Antiforgery.Internal +{ + internal static class AntiforgeryLoggerExtensions + { + private static readonly Action _validationFailed; + private static readonly Action _validated; + private static readonly Action _missingCookieToken; + private static readonly Action _missingRequestToken; + private static readonly Action _newCookieToken; + private static readonly Action _reusedCookieToken; + + static AntiforgeryLoggerExtensions() + { + _validationFailed = LoggerMessage.Define( + LogLevel.Warning, + 1, + "Antiforgery validation failed with message '{Message}'."); + _validated = LoggerMessage.Define( + LogLevel.Debug, + 2, + "Antiforgery successfully validated a request."); + _missingCookieToken = LoggerMessage.Define( + LogLevel.Warning, + 3, + "The required antiforgery cookie '{CookieName}' is not present."); + _missingRequestToken = LoggerMessage.Define( + LogLevel.Warning, + 4, + "The required antiforgery request token was not provided in either form field '{FormFieldName}' " + + "or header '{HeaderName}'."); + _newCookieToken = LoggerMessage.Define( + LogLevel.Debug, + 5, + "A new antiforgery cookie token was created."); + _reusedCookieToken = LoggerMessage.Define( + LogLevel.Debug, + 6, + "An antiforgery cookie token was reused."); + } + + public static void ValidationFailed(this ILogger logger, string message) + { + _validationFailed(logger, message, null); + } + + public static void ValidatedAntiforgeryToken(this ILogger logger) + { + _validated(logger, null); + } + + public static void MissingCookieToken(this ILogger logger, string cookieName) + { + _missingCookieToken(logger, cookieName, null); + } + + public static void MissingRequestToken(this ILogger logger, string formFieldName, string headerName) + { + _missingRequestToken(logger, formFieldName, headerName, null); + } + + public static void NewCookieToken(this ILogger logger) + { + _newCookieToken(logger, null); + } + + public static void ReusedCookieToken(this ILogger logger) + { + _reusedCookieToken(logger, null); + } + } +} diff --git a/src/Microsoft.AspNetCore.Antiforgery/Internal/DefaultAntiforgery.cs b/src/Microsoft.AspNetCore.Antiforgery/Internal/DefaultAntiforgery.cs index d25909886d..db4d29e62f 100644 --- a/src/Microsoft.AspNetCore.Antiforgery/Internal/DefaultAntiforgery.cs +++ b/src/Microsoft.AspNetCore.Antiforgery/Internal/DefaultAntiforgery.cs @@ -5,6 +5,7 @@ using System; using System.Diagnostics; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; namespace Microsoft.AspNetCore.Antiforgery.Internal @@ -19,17 +20,20 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal private readonly IAntiforgeryTokenGenerator _tokenGenerator; private readonly IAntiforgeryTokenSerializer _tokenSerializer; private readonly IAntiforgeryTokenStore _tokenStore; + private readonly ILogger _logger; public DefaultAntiforgery( IOptions antiforgeryOptionsAccessor, IAntiforgeryTokenGenerator tokenGenerator, IAntiforgeryTokenSerializer tokenSerializer, - IAntiforgeryTokenStore tokenStore) + IAntiforgeryTokenStore tokenStore, + ILoggerFactory loggerFactory) { _options = antiforgeryOptionsAccessor.Value; _tokenGenerator = tokenGenerator; _tokenSerializer = tokenSerializer; _tokenStore = tokenStore; + _logger = loggerFactory.CreateLogger(); } /// @@ -46,6 +50,11 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal if (tokenSet.IsNewCookieToken) { SaveCookieTokenAndHeader(httpContext, tokenSet.CookieToken); + _logger.NewCookieToken(); + } + else + { + _logger.ReusedCookieToken(); } return Serialize(tokenSet); @@ -76,8 +85,14 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal CheckSSLConfig(httpContext); var tokens = await _tokenStore.GetRequestTokensAsync(httpContext); - if (tokens.CookieToken == null || tokens.RequestToken == null) + if (tokens.CookieToken == null) { + _logger.MissingCookieToken(_options.CookieName); + return false; + } + if (tokens.RequestToken == null) + { + _logger.MissingRequestToken(_options.FormFieldName, _options.HeaderName); return false; } @@ -87,11 +102,22 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal // Validate string message; - return _tokenGenerator.TryValidateTokenSet( + var result = _tokenGenerator.TryValidateTokenSet( httpContext, deserializedCookieToken, deserializedRequestToken, out message); + + if (result) + { + _logger.ValidatedAntiforgeryToken(); + } + else + { + _logger.ValidationFailed(message); + } + + return result; } /// @@ -133,6 +159,8 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal } ValidateTokens(httpContext, tokens); + + _logger.ValidatedAntiforgeryToken(); } private void ValidateTokens(HttpContext httpContext, AntiforgeryTokenSet antiforgeryTokenSet) diff --git a/test/Microsoft.AspNetCore.Antiforgery.Test/Internal/DefaultAntiforgeryTest.cs b/test/Microsoft.AspNetCore.Antiforgery.Test/Internal/DefaultAntiforgeryTest.cs index 4651a14d83..8969b05c31 100644 --- a/test/Microsoft.AspNetCore.Antiforgery.Test/Internal/DefaultAntiforgeryTest.cs +++ b/test/Microsoft.AspNetCore.Antiforgery.Test/Internal/DefaultAntiforgeryTest.cs @@ -6,6 +6,9 @@ using System.Security.Claims; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Internal; +using Microsoft.AspNetCore.Testing; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Moq; using Xunit; @@ -18,13 +21,12 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal public async Task ChecksSSL_ValidateRequestAsync_Throws() { // Arrange - var httpContext = new DefaultHttpContext(); + var httpContext = GetHttpContext(); var options = new AntiforgeryOptions() { RequireSsl = true }; - - var antiforgery = GetAntiforgery(options); + var antiforgery = GetAntiforgery(httpContext, options); // Act & Assert var exception = await Assert.ThrowsAsync( @@ -39,13 +41,13 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal public async Task ChecksSSL_IsRequestValidAsync_Throws() { // Arrange - var httpContext = new DefaultHttpContext(); + var httpContext = GetHttpContext(); var options = new AntiforgeryOptions() { RequireSsl = true }; - var antiforgery = GetAntiforgery(options); + var antiforgery = GetAntiforgery(httpContext, options); // Act & Assert var exception = await Assert.ThrowsAsync( @@ -60,13 +62,13 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal public void ChecksSSL_GetAndStoreTokens_Throws() { // Arrange - var httpContext = new DefaultHttpContext(); + var httpContext = GetHttpContext(); var options = new AntiforgeryOptions() { RequireSsl = true }; - var antiforgery = GetAntiforgery(options); + var antiforgery = GetAntiforgery(httpContext, options); // Act & Assert var exception = Assert.Throws( @@ -81,13 +83,13 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal public void ChecksSSL_GetTokens_Throws() { // Arrange - var httpContext = new DefaultHttpContext(); + var httpContext = GetHttpContext(); var options = new AntiforgeryOptions() { RequireSsl = true }; - var antiforgery = GetAntiforgery(options); + var antiforgery = GetAntiforgery(httpContext, options); // Act & Assert var exception = Assert.Throws( @@ -102,13 +104,13 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal public void ChecksSSL_SetCookieTokenAndHeader_Throws() { // Arrange - var httpContext = new DefaultHttpContext(); + var httpContext = GetHttpContext(); var options = new AntiforgeryOptions() { RequireSsl = true }; - var antiforgery = GetAntiforgery(options); + var antiforgery = GetAntiforgery(httpContext, options); // Act & Assert var exception = Assert.Throws( @@ -290,12 +292,7 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal out message)) .Returns(false) .Verifiable(); - - var antiforgery = new DefaultAntiforgery( - new TestOptionsManager(), - context.TokenGenerator.Object, - context.TokenSerializer.Object, - context.TokenStore.Object); + var antiforgery = GetAntiforgery(context); // Act & assert var exception = await Assert.ThrowsAsync( @@ -458,6 +455,7 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal } private DefaultAntiforgery GetAntiforgery( + HttpContext httpContext, AntiforgeryOptions options = null, IAntiforgeryTokenGenerator tokenGenerator = null, IAntiforgeryTokenSerializer tokenSerializer = null, @@ -469,16 +467,29 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal optionsManager.Value = options; } + var loggerFactory = httpContext.RequestServices.GetRequiredService(); return new DefaultAntiforgery( antiforgeryOptionsAccessor: optionsManager, tokenGenerator: tokenGenerator, tokenSerializer: tokenSerializer, - tokenStore: tokenStore); + tokenStore: tokenStore, + loggerFactory: loggerFactory); + } + + private IServiceProvider GetServices() + { + var builder = new ServiceCollection(); + builder.AddSingleton(new LoggerFactory()); + + return builder.BuildServiceProvider(); } private HttpContext GetHttpContext() { var httpContext = new DefaultHttpContext(); + + httpContext.RequestServices = GetServices(); + httpContext.User = new ClaimsPrincipal(new ClaimsIdentity("some-auth")); return httpContext; } @@ -486,6 +497,7 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal private DefaultAntiforgery GetAntiforgery(AntiforgeryMockContext context) { return GetAntiforgery( + context.HttpContext, context.Options, context.TokenGenerator?.Object, context.TokenSerializer?.Object, @@ -630,4 +642,4 @@ namespace Microsoft.AspNetCore.Antiforgery.Internal public AntiforgeryOptions Value { get; set; } = new AntiforgeryOptions(); } } -} \ No newline at end of file +} diff --git a/test/Microsoft.AspNetCore.Antiforgery.Test/project.json b/test/Microsoft.AspNetCore.Antiforgery.Test/project.json index caabfc7fd0..6b60226074 100644 --- a/test/Microsoft.AspNetCore.Antiforgery.Test/project.json +++ b/test/Microsoft.AspNetCore.Antiforgery.Test/project.json @@ -8,6 +8,7 @@ "Microsoft.AspNetCore.Http": "1.0.0-*", "Microsoft.AspNetCore.Testing": "1.0.0-*", "Microsoft.Extensions.DependencyInjection": "1.0.0-*", + "Microsoft.Extensions.Logging": "1.0.0-*", "Microsoft.Extensions.WebEncoders": "1.0.0-*", "Microsoft.NETCore.Platforms": "1.0.1-*", "xunit": "2.1.0-*" @@ -19,7 +20,7 @@ "frameworks": { "dnx451": { "frameworkAssemblies": { - "System.Threading.Tasks": "" + "System.Threading.Tasks": "" }, "dependencies": { "Moq": "4.2.1312.1622",