File: CorsApplicationModelProviderTest.cs
Web Access
Project: src\src\Mvc\Mvc.Cors\test\Microsoft.AspNetCore.Mvc.Cors.Test.csproj (Microsoft.AspNetCore.Mvc.Cors.Test)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Reflection;
using Microsoft.AspNetCore.Cors;
using Microsoft.AspNetCore.Cors.Infrastructure;
using Microsoft.AspNetCore.Mvc.ActionConstraints;
using Microsoft.AspNetCore.Mvc.ApplicationModels;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Moq;
 
namespace Microsoft.AspNetCore.Mvc.Cors;
 
public class CorsApplicationModelProviderTest
{
    private readonly IOptions<MvcOptions> OptionsWithoutEndpointRouting = Options.Create(new MvcOptions { EnableEndpointRouting = false });
 
    [Fact]
    public void OnProvidersExecuting_WithoutGlobalAuthorizationFilter_EnableCorsAttributeAddsCorsAuthorizationFilterFactory()
    {
        // Arrange
        var corsProvider = new CorsApplicationModelProvider(OptionsWithoutEndpointRouting);
        var context = GetProviderContext(typeof(CorsController));
 
        // Act
        corsProvider.OnProvidersExecuting(context);
 
        // Assert
        var model = Assert.Single(context.Result.Controllers);
        Assert.Single(model.Filters, f => f is CorsAuthorizationFilterFactory);
        var action = Assert.Single(model.Actions);
        var selector = Assert.Single(action.Selectors);
        var constraint = Assert.Single(selector.ActionConstraints, c => c is HttpMethodActionConstraint);
        Assert.IsType<CorsHttpMethodActionConstraint>(constraint);
    }
 
    [Fact]
    public void OnProvidersExecuting_WithoutGlobalAuthorizationFilter_DisableCorsAttributeAddsDisableCorsAuthorizationFilter()
    {
        // Arrange
        var corsProvider = new CorsApplicationModelProvider(OptionsWithoutEndpointRouting);
        var context = GetProviderContext(typeof(DisableCorsController));
 
        // Act
        corsProvider.OnProvidersExecuting(context);
 
        // Assert
        var model = Assert.Single(context.Result.Controllers);
        Assert.Single(model.Filters, f => f is DisableCorsAuthorizationFilter);
        var action = Assert.Single(model.Actions);
        var selector = Assert.Single(action.Selectors);
        var constraint = Assert.Single(selector.ActionConstraints, c => c is HttpMethodActionConstraint);
        Assert.IsType<CorsHttpMethodActionConstraint>(constraint);
    }
 
    [Fact]
    public void OnProvidersExecuting_WithoutGlobalAuthorizationFilter_CustomCorsFilter_EnablesCorsPreflight()
    {
        // Arrange
        var corsProvider = new CorsApplicationModelProvider(OptionsWithoutEndpointRouting);
        var context = GetProviderContext(typeof(CustomCorsFilterController));
 
        // Act
        corsProvider.OnProvidersExecuting(context);
 
        // Assert
        var controller = Assert.Single(context.Result.Controllers);
        var action = Assert.Single(controller.Actions);
        var selector = Assert.Single(action.Selectors);
        var constraint = Assert.Single(selector.ActionConstraints, c => c is HttpMethodActionConstraint);
        Assert.IsType<CorsHttpMethodActionConstraint>(constraint);
    }
 
    [Fact]
    public void BuildActionModel_EnableCorsAttributeAddsCorsAuthorizationFilterFactory()
    {
        // Arrange
        var corsProvider = new CorsApplicationModelProvider(OptionsWithoutEndpointRouting);
        var context = GetProviderContext(typeof(EnableCorsController));
 
        // Act
        corsProvider.OnProvidersExecuting(context);
 
        // Assert
        var controller = Assert.Single(context.Result.Controllers);
        var action = Assert.Single(controller.Actions);
        Assert.Single(action.Filters, f => f is CorsAuthorizationFilterFactory);
        var selector = Assert.Single(action.Selectors);
        var constraint = Assert.Single(selector.ActionConstraints, c => c is HttpMethodActionConstraint);
        Assert.IsType<CorsHttpMethodActionConstraint>(constraint);
    }
 
    [Fact]
    public void BuildActionModel_WithoutGlobalAuthorizationFilter_DisableCorsAttributeAddsDisableCorsAuthorizationFilter()
    {
        // Arrange
        var corsProvider = new CorsApplicationModelProvider(OptionsWithoutEndpointRouting);
        var context = GetProviderContext(typeof(DisableCorsActionController));
 
        // Act
        corsProvider.OnProvidersExecuting(context);
 
        // Assert
        var controller = Assert.Single(context.Result.Controllers);
        var action = Assert.Single(controller.Actions);
        Assert.Contains(action.Filters, f => f is DisableCorsAuthorizationFilter);
        var selector = Assert.Single(action.Selectors);
        var constraint = Assert.Single(selector.ActionConstraints, c => c is HttpMethodActionConstraint);
        Assert.IsType<CorsHttpMethodActionConstraint>(constraint);
    }
 
    [Fact]
    public void BuildActionModel_WithoutGlobalAuthorizationFilter_CustomCorsAuthorizationFilterOnAction_EnablesCorsPreflight()
    {
        // Arrange
        var corsProvider = new CorsApplicationModelProvider(OptionsWithoutEndpointRouting);
        var context = GetProviderContext(typeof(CustomCorsFilterOnActionController));
 
        // Act
        corsProvider.OnProvidersExecuting(context);
 
        // Assert
        var controller = Assert.Single(context.Result.Controllers);
        var action = Assert.Single(controller.Actions);
        var selector = Assert.Single(action.Selectors);
        var constraint = Assert.Single(selector.ActionConstraints, c => c is HttpMethodActionConstraint);
        Assert.IsType<CorsHttpMethodActionConstraint>(constraint);
    }
 
    [Fact]
    public void OnProvidersExecuting_WithoutGlobalAuthorizationFilter_EnableCorsGloballyEnablesCorsPreflight()
    {
        // Arrange
        var corsProvider = new CorsApplicationModelProvider(OptionsWithoutEndpointRouting);
        var context = GetProviderContext(typeof(RegularController));
 
        context.Result.Filters.Add(
            new CorsAuthorizationFilter(Mock.Of<ICorsService>(), Mock.Of<ICorsPolicyProvider>(), Mock.Of<ILoggerFactory>()));
 
        // Act
        corsProvider.OnProvidersExecuting(context);
 
        // Assert
        var model = Assert.Single(context.Result.Controllers);
        var action = Assert.Single(model.Actions);
        var selector = Assert.Single(action.Selectors);
        var constraint = Assert.Single(selector.ActionConstraints, c => c is HttpMethodActionConstraint);
        Assert.IsType<CorsHttpMethodActionConstraint>(constraint);
    }
 
    [Fact]
    public void OnProvidersExecuting_WithoutGlobalAuthorizationFilter_DisableCorsGloballyEnablesCorsPreflight()
    {
        // Arrange
        var corsProvider = new CorsApplicationModelProvider(OptionsWithoutEndpointRouting);
        var context = GetProviderContext(typeof(RegularController));
        context.Result.Filters.Add(new DisableCorsAuthorizationFilter());
 
        // Act
        corsProvider.OnProvidersExecuting(context);
 
        // Assert
        var model = Assert.Single(context.Result.Controllers);
        var action = Assert.Single(model.Actions);
        var selector = Assert.Single(action.Selectors);
        var constraint = Assert.Single(selector.ActionConstraints, c => c is HttpMethodActionConstraint);
        Assert.IsType<CorsHttpMethodActionConstraint>(constraint);
    }
 
    [Fact]
    public void OnProvidersExecuting_WithoutGlobalAuthorizationFilter_CustomCorsFilterGloballyEnablesCorsPreflight()
    {
        // Arrange
        var corsProvider = new CorsApplicationModelProvider(OptionsWithoutEndpointRouting);
        var context = GetProviderContext(typeof(RegularController));
        context.Result.Filters.Add(new CustomCorsFilterAttribute());
 
        // Act
        corsProvider.OnProvidersExecuting(context);
 
        // Assert
        var model = Assert.Single(context.Result.Controllers);
        var action = Assert.Single(model.Actions);
        var selector = Assert.Single(action.Selectors);
        var constraint = Assert.Single(selector.ActionConstraints, c => c is HttpMethodActionConstraint);
        Assert.IsType<CorsHttpMethodActionConstraint>(constraint);
    }
 
    [Fact]
    public void OnProvidersExecuting_WithoutGlobalAuthorizationFilter_CorsNotInUseDoesNotOverrideHttpConstraints()
    {
        // Arrange
        var corsProvider = new CorsApplicationModelProvider(OptionsWithoutEndpointRouting);
        var context = GetProviderContext(typeof(RegularController));
 
        // Act
        corsProvider.OnProvidersExecuting(context);
 
        // Assert
        var model = Assert.Single(context.Result.Controllers);
        var action = Assert.Single(model.Actions);
        var selector = Assert.Single(action.Selectors);
        var constraint = Assert.Single(selector.ActionConstraints, c => c is HttpMethodActionConstraint);
        Assert.IsNotType<CorsHttpMethodActionConstraint>(constraint);
    }
 
    private static ApplicationModelProviderContext GetProviderContext(Type controllerType)
    {
        var context = new ApplicationModelProviderContext(new[] { controllerType.GetTypeInfo() });
        var provider = new DefaultApplicationModelProvider(
            Options.Create(new MvcOptions()),
            new EmptyModelMetadataProvider());
        provider.OnProvidersExecuting(context);
 
        return context;
    }
 
    private class EnableCorsController
    {
        [EnableCors("policy")]
        [HttpGet]
        public IActionResult Action()
        {
            return null;
        }
    }
 
    private class DisableCorsActionController
    {
        [DisableCors]
        [HttpGet]
        public void Action()
        {
        }
    }
 
    [EnableCors("policy")]
    public class CorsController
    {
        [HttpGet]
        public IActionResult Action()
        {
            return null;
        }
    }
 
    [DisableCors]
    public class DisableCorsController
    {
        [HttpOptions]
        public IActionResult Action()
        {
            return null;
        }
    }
 
    public class RegularController
    {
        [HttpPost]
        public IActionResult Action()
        {
            return null;
        }
    }
 
    [CustomCorsFilter]
    public class CustomCorsFilterController
    {
        [HttpPost]
        public IActionResult Action()
        {
            return null;
        }
    }
 
    public class CustomCorsFilterOnActionController
    {
        [HttpPost]
        [CustomCorsFilter]
        public IActionResult Action()
        {
            return null;
        }
    }
 
    public class CustomCorsFilterAttribute : Attribute, ICorsAuthorizationFilter
    {
        public int Order { get; } = 1000;
 
        public Task OnAuthorizationAsync(AuthorizationFilterContext context)
        {
            return Task.FromResult(0);
        }
    }
}