diff --git a/src/Microsoft.AspNetCore.Mvc.Cors/CorsAuthorizationFilter.cs b/src/Microsoft.AspNetCore.Mvc.Cors/CorsAuthorizationFilter.cs index 4e2cc209c1..aa0bd66cec 100644 --- a/src/Microsoft.AspNetCore.Mvc.Cors/CorsAuthorizationFilter.cs +++ b/src/Microsoft.AspNetCore.Mvc.Cors/CorsAuthorizationFilter.cs @@ -83,7 +83,7 @@ namespace Microsoft.AspNetCore.Mvc.Cors if (string.Equals( request.Method, CorsConstants.PreflightHttpMethod, - StringComparison.Ordinal) && + StringComparison.OrdinalIgnoreCase) && !StringValues.IsNullOrEmpty(accessControlRequestMethod)) { // If this was a preflight, there is no need to run anything else. diff --git a/src/Microsoft.AspNetCore.Mvc.Cors/Internal/DisableCorsAuthorizationFilter.cs b/src/Microsoft.AspNetCore.Mvc.Cors/Internal/DisableCorsAuthorizationFilter.cs index be643ec367..732fd530d8 100644 --- a/src/Microsoft.AspNetCore.Mvc.Cors/Internal/DisableCorsAuthorizationFilter.cs +++ b/src/Microsoft.AspNetCore.Mvc.Cors/Internal/DisableCorsAuthorizationFilter.cs @@ -40,7 +40,7 @@ namespace Microsoft.AspNetCore.Mvc.Cors.Internal if (string.Equals( context.HttpContext.Request.Method, CorsConstants.PreflightHttpMethod, - StringComparison.Ordinal) && + StringComparison.OrdinalIgnoreCase) && !StringValues.IsNullOrEmpty(accessControlRequestMethod)) { // Short circuit if the request is preflight as that should not result in action execution. diff --git a/test/Microsoft.AspNetCore.Mvc.Cors.Test/CorsAuthorizationFilterTest.cs b/test/Microsoft.AspNetCore.Mvc.Cors.Test/CorsAuthorizationFilterTest.cs index 1382c613b0..1880630832 100644 --- a/test/Microsoft.AspNetCore.Mvc.Cors.Test/CorsAuthorizationFilterTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.Cors.Test/CorsAuthorizationFilterTest.cs @@ -20,8 +20,11 @@ namespace Microsoft.AspNetCore.Mvc.Cors { public class CorsAuthorizationFilterTest { - [Fact] - public async Task PreFlightRequest_SuccessfulMatch_WritesHeaders() + [Theory] + [InlineData("options")] + [InlineData("Options")] + [InlineData("OPTIONS")] + public async Task CaseInsensitive_PreFlightRequest_SuccessfulMatch_WritesHeaders(string preflightRequestMethod) { // Arrange var mockEngine = GetPassingEngine(supportsCredentials:true); @@ -31,6 +34,7 @@ namespace Microsoft.AspNetCore.Mvc.Cors new[] { new FilterDescriptor(filter, FilterScope.Action) }, GetRequestHeaders(true), isPreflight: true); + authorizationContext.HttpContext.Request.Method = preflightRequestMethod; // Act await filter.OnAuthorizationAsync(authorizationContext); diff --git a/test/Microsoft.AspNetCore.Mvc.Cors.Test/DisableCorsAuthorizationFilterTest.cs b/test/Microsoft.AspNetCore.Mvc.Cors.Test/DisableCorsAuthorizationFilterTest.cs new file mode 100644 index 0000000000..5584abad3b --- /dev/null +++ b/test/Microsoft.AspNetCore.Mvc.Cors.Test/DisableCorsAuthorizationFilterTest.cs @@ -0,0 +1,80 @@ +// 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.Collections.Generic; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Cors.Infrastructure; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc.Abstractions; +using Microsoft.AspNetCore.Mvc.Cors.Internal; +using Microsoft.AspNetCore.Mvc.Filters; +using Microsoft.AspNetCore.Routing; +using Xunit; + +namespace Microsoft.AspNetCore.Mvc.Cors +{ + public class DisableCorsAuthorizationFilterTest + { + [Fact] + public async Task DisableCors_DoesNotShortCircuitsRequest_IfNotAPreflightRequest() + { + // Arrange + var filter = new DisableCorsAuthorizationFilter(); + var httpContext = new DefaultHttpContext(); + httpContext.Request.Method = "GET"; + httpContext.Request.Headers.Add(CorsConstants.Origin, "http://localhost:5000/"); + httpContext.Request.Headers.Add(CorsConstants.AccessControlRequestMethod, "PUT"); + var authorizationFilterContext = new AuthorizationFilterContext( + new ActionContext(httpContext, new RouteData(), new ActionDescriptor()), + new List()); + + // Act + await filter.OnAuthorizationAsync(authorizationFilterContext); + + // Assert + Assert.Null(authorizationFilterContext.Result); + } + + [Fact] + public async Task DisableCors_DoesNotShortCircuitsRequest_IfNoAccessControlRequestMethodFound() + { + // Arrange + var filter = new DisableCorsAuthorizationFilter(); + var httpContext = new DefaultHttpContext(); + httpContext.Request.Method = "OPTIONS"; + httpContext.Request.Headers.Add(CorsConstants.Origin, "http://localhost:5000/"); + var authorizationFilterContext = new AuthorizationFilterContext( + new ActionContext(httpContext, new RouteData(), new ActionDescriptor()), + new List()); + + // Act + await filter.OnAuthorizationAsync(authorizationFilterContext); + + // Assert + Assert.Null(authorizationFilterContext.Result); + } + + [Theory] + [InlineData("OpTions")] + [InlineData("OPTIONS")] + public async Task DisableCors_CaseInsensitivePreflightMethod_ShortCircuitsRequest(string preflightMethod) + { + // Arrange + var filter = new DisableCorsAuthorizationFilter(); + var httpContext = new DefaultHttpContext(); + httpContext.Request.Method = preflightMethod; + httpContext.Request.Headers.Add(CorsConstants.Origin, "http://localhost:5000/"); + httpContext.Request.Headers.Add(CorsConstants.AccessControlRequestMethod, "PUT"); + var authorizationFilterContext = new AuthorizationFilterContext( + new ActionContext(httpContext, new RouteData(), new ActionDescriptor()), + new List()); + + // Act + await filter.OnAuthorizationAsync(authorizationFilterContext); + + // Assert + var statusCodeResult = Assert.IsType(authorizationFilterContext.Result); + Assert.Equal(StatusCodes.Status200OK, statusCodeResult.StatusCode); + } + } +}