Cors Support in MVC.
This commit is contained in:
parent
ee4ffea294
commit
015edefa96
15
Mvc.sln
15
Mvc.sln
|
|
@ -152,6 +152,8 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "TempDataWebSite", "test\Web
|
||||||
EndProject
|
EndProject
|
||||||
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.Mvc.TestCommon", "test\Microsoft.AspNet.Mvc.TestCommon\Microsoft.AspNet.Mvc.TestCommon.xproj", "{F504357E-C2E1-4818-BA5C-9A2EAC25FEE5}"
|
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.Mvc.TestCommon", "test\Microsoft.AspNet.Mvc.TestCommon\Microsoft.AspNet.Mvc.TestCommon.xproj", "{F504357E-C2E1-4818-BA5C-9A2EAC25FEE5}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "CorsWebSite", "test\WebSites\CorsWebSite\CorsWebSite.xproj", "{94BA134D-04B3-48AA-BA55-5A4DB8640F2D}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
|
@ -894,6 +896,18 @@ Global
|
||||||
{F504357E-C2E1-4818-BA5C-9A2EAC25FEE5}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
{F504357E-C2E1-4818-BA5C-9A2EAC25FEE5}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
||||||
{F504357E-C2E1-4818-BA5C-9A2EAC25FEE5}.Release|x86.ActiveCfg = Release|Any CPU
|
{F504357E-C2E1-4818-BA5C-9A2EAC25FEE5}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
{F504357E-C2E1-4818-BA5C-9A2EAC25FEE5}.Release|x86.Build.0 = Release|Any CPU
|
{F504357E-C2E1-4818-BA5C-9A2EAC25FEE5}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{94BA134D-04B3-48AA-BA55-5A4DB8640F2D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{94BA134D-04B3-48AA-BA55-5A4DB8640F2D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{94BA134D-04B3-48AA-BA55-5A4DB8640F2D}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||||
|
{94BA134D-04B3-48AA-BA55-5A4DB8640F2D}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
|
||||||
|
{94BA134D-04B3-48AA-BA55-5A4DB8640F2D}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{94BA134D-04B3-48AA-BA55-5A4DB8640F2D}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{94BA134D-04B3-48AA-BA55-5A4DB8640F2D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{94BA134D-04B3-48AA-BA55-5A4DB8640F2D}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{94BA134D-04B3-48AA-BA55-5A4DB8640F2D}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
|
||||||
|
{94BA134D-04B3-48AA-BA55-5A4DB8640F2D}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
||||||
|
{94BA134D-04B3-48AA-BA55-5A4DB8640F2D}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{94BA134D-04B3-48AA-BA55-5A4DB8640F2D}.Release|x86.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
|
@ -968,5 +982,6 @@ Global
|
||||||
{BCDB13A6-7D6E-485E-8424-A156432B71AC} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C}
|
{BCDB13A6-7D6E-485E-8424-A156432B71AC} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C}
|
||||||
{8AEB631E-AB74-4D2E-83FB-8931EE10D9D3} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C}
|
{8AEB631E-AB74-4D2E-83FB-8931EE10D9D3} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C}
|
||||||
{F504357E-C2E1-4818-BA5C-9A2EAC25FEE5} = {3BA657BF-28B1-42DA-B5B0-1C4601FCF7B1}
|
{F504357E-C2E1-4818-BA5C-9A2EAC25FEE5} = {3BA657BF-28B1-42DA-B5B0-1C4601FCF7B1}
|
||||||
|
{94BA134D-04B3-48AA-BA55-5A4DB8640F2D} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C}
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
EndGlobal
|
EndGlobal
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,8 @@ using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using Microsoft.AspNet.Authorization;
|
using Microsoft.AspNet.Authorization;
|
||||||
|
using Microsoft.AspNet.Cors;
|
||||||
|
using Microsoft.AspNet.Cors.Core;
|
||||||
using Microsoft.AspNet.Mvc.Description;
|
using Microsoft.AspNet.Mvc.Description;
|
||||||
using Microsoft.AspNet.Mvc.ModelBinding;
|
using Microsoft.AspNet.Mvc.ModelBinding;
|
||||||
using Microsoft.AspNet.Mvc.Routing;
|
using Microsoft.AspNet.Mvc.Routing;
|
||||||
|
|
@ -21,9 +23,9 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
|
||||||
{
|
{
|
||||||
private readonly AuthorizationOptions _authorizationOptions;
|
private readonly AuthorizationOptions _authorizationOptions;
|
||||||
|
|
||||||
public DefaultActionModelBuilder(IOptions<AuthorizationOptions> options)
|
public DefaultActionModelBuilder(IOptions<AuthorizationOptions> authorizationOptions)
|
||||||
{
|
{
|
||||||
_authorizationOptions = options?.Options ?? new AuthorizationOptions();
|
_authorizationOptions = authorizationOptions?.Options ?? new AuthorizationOptions();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
|
@ -266,6 +268,18 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
|
||||||
AddRange(actionModel.ActionConstraints, attributes.OfType<IActionConstraintMetadata>());
|
AddRange(actionModel.ActionConstraints, attributes.OfType<IActionConstraintMetadata>());
|
||||||
AddRange(actionModel.Filters, attributes.OfType<IFilter>());
|
AddRange(actionModel.Filters, attributes.OfType<IFilter>());
|
||||||
|
|
||||||
|
var enableCors = attributes.OfType<IEnableCorsAttribute>().SingleOrDefault();
|
||||||
|
if (enableCors != null)
|
||||||
|
{
|
||||||
|
actionModel.Filters.Add(new CorsAuthorizationFilterFactory(enableCors.PolicyName));
|
||||||
|
}
|
||||||
|
|
||||||
|
var disableCors = attributes.OfType<IDisableCorsAttribute>().SingleOrDefault();
|
||||||
|
if (disableCors != null)
|
||||||
|
{
|
||||||
|
actionModel.Filters.Add(new DisableCorsAuthorizationFilter());
|
||||||
|
}
|
||||||
|
|
||||||
var policy = AuthorizationPolicy.Combine(_authorizationOptions, attributes.OfType<AuthorizeAttribute>());
|
var policy = AuthorizationPolicy.Combine(_authorizationOptions, attributes.OfType<AuthorizeAttribute>());
|
||||||
if (policy != null)
|
if (policy != null)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,8 @@ using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using Microsoft.AspNet.Authorization;
|
using Microsoft.AspNet.Authorization;
|
||||||
|
using Microsoft.AspNet.Cors;
|
||||||
|
using Microsoft.AspNet.Cors.Core;
|
||||||
using Microsoft.AspNet.Mvc.Description;
|
using Microsoft.AspNet.Mvc.Description;
|
||||||
using Microsoft.AspNet.Mvc.Filters;
|
using Microsoft.AspNet.Mvc.Filters;
|
||||||
using Microsoft.AspNet.Mvc.Routing;
|
using Microsoft.AspNet.Mvc.Routing;
|
||||||
|
|
@ -31,11 +33,11 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
|
||||||
public DefaultControllerModelBuilder(
|
public DefaultControllerModelBuilder(
|
||||||
IActionModelBuilder actionModelBuilder,
|
IActionModelBuilder actionModelBuilder,
|
||||||
ILoggerFactory loggerFactory,
|
ILoggerFactory loggerFactory,
|
||||||
IOptions<AuthorizationOptions> options)
|
IOptions<AuthorizationOptions> authorizationOptions)
|
||||||
{
|
{
|
||||||
_actionModelBuilder = actionModelBuilder;
|
_actionModelBuilder = actionModelBuilder;
|
||||||
_logger = loggerFactory.CreateLogger<DefaultControllerModelBuilder>();
|
_logger = loggerFactory.CreateLogger<DefaultControllerModelBuilder>();
|
||||||
_authorizationOptions = options?.Options ?? new AuthorizationOptions();
|
_authorizationOptions = authorizationOptions?.Options ?? new AuthorizationOptions();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
|
@ -80,6 +82,18 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
|
||||||
AddRange(controllerModel.Filters, attributes.OfType<IFilter>());
|
AddRange(controllerModel.Filters, attributes.OfType<IFilter>());
|
||||||
AddRange(controllerModel.RouteConstraints, attributes.OfType<IRouteConstraintProvider>());
|
AddRange(controllerModel.RouteConstraints, attributes.OfType<IRouteConstraintProvider>());
|
||||||
|
|
||||||
|
var enableCors = attributes.OfType<IEnableCorsAttribute>().SingleOrDefault();
|
||||||
|
if (enableCors != null)
|
||||||
|
{
|
||||||
|
controllerModel.Filters.Add(new CorsAuthorizationFilterFactory(enableCors.PolicyName));
|
||||||
|
}
|
||||||
|
|
||||||
|
var disableCors = attributes.OfType<IDisableCorsAttribute>().SingleOrDefault();
|
||||||
|
if (disableCors != null)
|
||||||
|
{
|
||||||
|
controllerModel.Filters.Add(new DisableCorsAuthorizationFilter());
|
||||||
|
}
|
||||||
|
|
||||||
var policy = AuthorizationPolicy.Combine(_authorizationOptions, attributes.OfType<AuthorizeAttribute>());
|
var policy = AuthorizationPolicy.Combine(_authorizationOptions, attributes.OfType<AuthorizeAttribute>());
|
||||||
if (policy != null)
|
if (policy != null)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -11,5 +11,11 @@ namespace Microsoft.AspNet.Mvc
|
||||||
/// User code should order at bigger than 0 or smaller than -2000.
|
/// User code should order at bigger than 0 or smaller than -2000.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static readonly int DefaultFrameworkSortOrder = -1000;
|
public static readonly int DefaultFrameworkSortOrder = -1000;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The default order for <see cref="CorsAuthorizationFilter"/>, <see cref="CorsAuthorizationFilterFactory"/>
|
||||||
|
/// and <see cref="DisableCorsAuthorizationFilter"/>.
|
||||||
|
/// </summary>
|
||||||
|
public static readonly int DefaultCorsSortOrder = int.MaxValue - 100;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,45 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNet.Cors.Core;
|
||||||
|
using Microsoft.AspNet.WebUtilities;
|
||||||
|
using Microsoft.Framework.Internal;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Mvc
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// An <see cref="ICorsAuthorizationFilter"/> which ensures that an action does not run for a pre-flight request.
|
||||||
|
/// </summary>
|
||||||
|
public class DisableCorsAuthorizationFilter : ICorsAuthorizationFilter
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public int Order
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return DefaultOrder.DefaultCorsSortOrder;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public Task OnAuthorizationAsync([NotNull] AuthorizationContext context)
|
||||||
|
{
|
||||||
|
var accessControlRequestMethod =
|
||||||
|
context.HttpContext.Request.Headers.Get(CorsConstants.AccessControlRequestMethod);
|
||||||
|
if (string.Equals(
|
||||||
|
context.HttpContext.Request.Method,
|
||||||
|
CorsConstants.PreflightHttpMethod,
|
||||||
|
StringComparison.Ordinal) &&
|
||||||
|
accessControlRequestMethod != null)
|
||||||
|
{
|
||||||
|
// Short circuit if the request is preflight as that should not result in action execution.
|
||||||
|
context.Result = new HttpStatusCodeResult(StatusCodes.Status200OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Let the action be executed.
|
||||||
|
return Task.FromResult(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,94 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNet.Cors.Core;
|
||||||
|
using Microsoft.AspNet.Http;
|
||||||
|
using Microsoft.AspNet.WebUtilities;
|
||||||
|
using Microsoft.Framework.DependencyInjection;
|
||||||
|
using Microsoft.Framework.Internal;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Mvc
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A filter which applies the given <see cref="CorsPolicy"/> and adds appropriate response headers.
|
||||||
|
/// </summary>
|
||||||
|
public class CorsAuthorizationFilter : ICorsAuthorizationFilter
|
||||||
|
{
|
||||||
|
private ICorsService _corsService;
|
||||||
|
private ICorsPolicyProvider _corsPolicyProvider;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instace of <see cref="CorsAuthorizationFilter"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="corsService">The <see cref="ICorsService"/>.</param>
|
||||||
|
/// <param name="policyProvider">The <see cref="ICorsPolicyProvider"/>.</param>
|
||||||
|
public CorsAuthorizationFilter(ICorsService corsService, ICorsPolicyProvider policyProvider)
|
||||||
|
{
|
||||||
|
_corsService = corsService;
|
||||||
|
_corsPolicyProvider = policyProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The policy name used to fetch a <see cref="CorsPolicy"/>.
|
||||||
|
/// </summary>
|
||||||
|
public string PolicyName { get; set; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public int Order
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return DefaultOrder.DefaultCorsSortOrder;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public async Task OnAuthorizationAsync([NotNull] AuthorizationContext context)
|
||||||
|
{
|
||||||
|
// If this filter is not closest to the action, it is not applicable.
|
||||||
|
if (!IsClosestToAction(context.Filters))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var httpContext = context.HttpContext;
|
||||||
|
var request = httpContext.Request;
|
||||||
|
if (request.Headers.ContainsKey(CorsConstants.Origin))
|
||||||
|
{
|
||||||
|
var policy = await _corsPolicyProvider.GetPolicyAsync(httpContext, PolicyName);
|
||||||
|
var result = _corsService.EvaluatePolicy(context.HttpContext, policy);
|
||||||
|
_corsService.ApplyResult(result, context.HttpContext.Response);
|
||||||
|
|
||||||
|
var accessControlRequestMethod =
|
||||||
|
httpContext.Request.Headers.Get(CorsConstants.AccessControlRequestMethod);
|
||||||
|
if (string.Equals(
|
||||||
|
request.Method,
|
||||||
|
CorsConstants.PreflightHttpMethod,
|
||||||
|
StringComparison.Ordinal) &&
|
||||||
|
accessControlRequestMethod != null)
|
||||||
|
{
|
||||||
|
// If this was a preflight, there is no need to run anything else.
|
||||||
|
// Also the response is always 200 so that anyone after mvc can handle the pre flight request.
|
||||||
|
context.Result = new HttpStatusCodeResult(StatusCodes.Status200OK);
|
||||||
|
await Task.FromResult(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Continue with other filters and action.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool IsClosestToAction(IEnumerable<IFilter> filters)
|
||||||
|
{
|
||||||
|
// If there are multiple ICorsAuthorizationFilter which are defined at the class and
|
||||||
|
// at the action level, the one closest to the action overrides the others.
|
||||||
|
// Since filterdescriptor collection is ordered (the last filter is the one closest to the action),
|
||||||
|
// we apply this constraint only if there is no ICorsAuthorizationFilter after this.
|
||||||
|
return filters.Last(filter => filter is ICorsAuthorizationFilter) == this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,48 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNet.Cors.Core;
|
||||||
|
using Microsoft.AspNet.Http;
|
||||||
|
using Microsoft.AspNet.WebUtilities;
|
||||||
|
using Microsoft.Framework.DependencyInjection;
|
||||||
|
using Microsoft.Framework.Internal;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Mvc
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A filter factory which creates a new instance of <see cref="CorsAuthorizationFilter"/>.
|
||||||
|
/// </summary>
|
||||||
|
public class CorsAuthorizationFilterFactory : IFilterFactory, IOrderedFilter
|
||||||
|
{
|
||||||
|
private readonly string _policyName;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new insntace of <see cref="CorsAuthorizationFilterFactory"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="policyName"></param>
|
||||||
|
public CorsAuthorizationFilterFactory(string policyName)
|
||||||
|
{
|
||||||
|
_policyName = policyName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public int Order
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return DefaultOrder.DefaultCorsSortOrder;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public IFilter CreateInstance([NotNull] IServiceProvider serviceProvider)
|
||||||
|
{
|
||||||
|
var filter = serviceProvider.GetRequiredService<CorsAuthorizationFilter>();
|
||||||
|
filter.PolicyName = _policyName;
|
||||||
|
return filter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -5,6 +5,7 @@ using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using Microsoft.AspNet.Cors.Core;
|
||||||
using Microsoft.Framework.Internal;
|
using Microsoft.Framework.Internal;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc
|
namespace Microsoft.AspNet.Mvc
|
||||||
|
|
@ -54,8 +55,22 @@ namespace Microsoft.AspNet.Mvc
|
||||||
}
|
}
|
||||||
|
|
||||||
var request = context.RouteContext.HttpContext.Request;
|
var request = context.RouteContext.HttpContext.Request;
|
||||||
|
var method = request.Method;
|
||||||
|
if (request.Headers.ContainsKey(CorsConstants.Origin))
|
||||||
|
{
|
||||||
|
// Update the http method if it is preflight request.
|
||||||
|
var accessControlRequestMethod = request.Headers.Get(CorsConstants.AccessControlRequestMethod);
|
||||||
|
if (string.Equals(
|
||||||
|
request.Method,
|
||||||
|
CorsConstants.PreflightHttpMethod,
|
||||||
|
StringComparison.Ordinal) &&
|
||||||
|
accessControlRequestMethod != null)
|
||||||
|
{
|
||||||
|
method = accessControlRequestMethod;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (HttpMethods.Any(m => m.Equals(request.Method, StringComparison.Ordinal)));
|
return (HttpMethods.Any(m => m.Equals(method, StringComparison.Ordinal)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Mvc
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A filter which can be used to enable/disable cors support for a resource.
|
||||||
|
/// </summary>
|
||||||
|
public interface ICorsAuthorizationFilter : IAsyncAuthorizationFilter, IOrderedFilter
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -7,6 +7,7 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Microsoft.AspNet.Authentication": "1.0.0-*",
|
"Microsoft.AspNet.Authentication": "1.0.0-*",
|
||||||
"Microsoft.AspNet.Authorization": "1.0.0-*",
|
"Microsoft.AspNet.Authorization": "1.0.0-*",
|
||||||
|
"Microsoft.AspNet.Cors.Core": "1.0.0-*",
|
||||||
"Microsoft.AspNet.DataProtection": "1.0.0-*",
|
"Microsoft.AspNet.DataProtection": "1.0.0-*",
|
||||||
"Microsoft.AspNet.Diagnostics.Interfaces": "1.0.0-*",
|
"Microsoft.AspNet.Diagnostics.Interfaces": "1.0.0-*",
|
||||||
"Microsoft.AspNet.FileProviders": "1.0.0-*",
|
"Microsoft.AspNet.FileProviders": "1.0.0-*",
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
using Microsoft.AspNet.Builder;
|
||||||
using Microsoft.AspNet.Mvc;
|
using Microsoft.AspNet.Mvc;
|
||||||
using Microsoft.AspNet.Routing;
|
using Microsoft.AspNet.Routing;
|
||||||
using Microsoft.Framework.Internal;
|
using Microsoft.Framework.Internal;
|
||||||
|
|
@ -87,6 +88,7 @@ namespace Microsoft.Framework.DependencyInjection
|
||||||
services.AddOptions();
|
services.AddOptions();
|
||||||
services.AddDataProtection();
|
services.AddDataProtection();
|
||||||
services.AddRouting();
|
services.AddRouting();
|
||||||
|
services.AddCors();
|
||||||
services.AddAuthorization();
|
services.AddAuthorization();
|
||||||
services.AddWebEncoders();
|
services.AddWebEncoders();
|
||||||
services.Configure<RouteOptions>(
|
services.Configure<RouteOptions>(
|
||||||
|
|
|
||||||
|
|
@ -75,6 +75,7 @@ namespace Microsoft.AspNet.Mvc
|
||||||
services.AddTransient<IFilterProvider, DefaultFilterProvider>();
|
services.AddTransient<IFilterProvider, DefaultFilterProvider>();
|
||||||
|
|
||||||
services.AddTransient<FormatFilter, FormatFilter>();
|
services.AddTransient<FormatFilter, FormatFilter>();
|
||||||
|
services.AddTransient<CorsAuthorizationFilter, CorsAuthorizationFilter>();
|
||||||
|
|
||||||
// Dataflow - ModelBinding, Validation and Formatting
|
// Dataflow - ModelBinding, Validation and Formatting
|
||||||
//
|
//
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Microsoft.AspNet.Authorization": "1.0.0-*",
|
"Microsoft.AspNet.Authorization": "1.0.0-*",
|
||||||
|
"Microsoft.AspNet.Cors": "1.0.0-*",
|
||||||
"Microsoft.AspNet.Mvc.Common": { "version": "6.0.0-*", "type": "build" },
|
"Microsoft.AspNet.Mvc.Common": { "version": "6.0.0-*", "type": "build" },
|
||||||
"Microsoft.AspNet.Mvc.Razor": "6.0.0-*",
|
"Microsoft.AspNet.Mvc.Razor": "6.0.0-*",
|
||||||
"Microsoft.Framework.Caching.Memory": "1.0.0-*",
|
"Microsoft.Framework.Caching.Memory": "1.0.0-*",
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using Microsoft.AspNet.Authorization;
|
using Microsoft.AspNet.Authorization;
|
||||||
|
using Microsoft.AspNet.Cors.Core;
|
||||||
using Microsoft.Framework.Internal;
|
using Microsoft.Framework.Internal;
|
||||||
using Microsoft.Framework.OptionsModel;
|
using Microsoft.Framework.OptionsModel;
|
||||||
using Moq;
|
using Moq;
|
||||||
|
|
@ -284,6 +285,38 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
|
||||||
Assert.False(isValid);
|
Assert.False(isValid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void BuildActionModel_EnableCorsAttributeAddsCorsAuthorizationFilterFactory()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var builder = new DefaultActionModelBuilder(authorizationOptions: null);
|
||||||
|
var typeInfo = typeof(EnableCorsController).GetTypeInfo();
|
||||||
|
var method = typeInfo.GetMethod("Action");
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var actions = builder.BuildActionModels(typeInfo, method);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var action = Assert.Single(actions);
|
||||||
|
Assert.Single(action.Filters, f => f is CorsAuthorizationFilterFactory);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void BuildActionModel_DisableCorsAttributeAddsDisableCorsAuthorizationFilter()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var builder = new DefaultActionModelBuilder(authorizationOptions: null);
|
||||||
|
var typeInfo = typeof(DisableCorsController).GetTypeInfo();
|
||||||
|
var method = typeInfo.GetMethod("Action");
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var actions = builder.BuildActionModels(typeInfo, method);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var action = Assert.Single(actions);
|
||||||
|
Assert.True(action.Filters.Any(f => f is DisableCorsAuthorizationFilter));
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void GetActions_ConventionallyRoutedAction_WithoutHttpConstraints()
|
public void GetActions_ConventionallyRoutedAction_WithoutHttpConstraints()
|
||||||
{
|
{
|
||||||
|
|
@ -927,6 +960,22 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
|
||||||
public void Invalid() { }
|
public void Invalid() { }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class EnableCorsController
|
||||||
|
{
|
||||||
|
[EnableCors("policy")]
|
||||||
|
public void Action()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class DisableCorsController
|
||||||
|
{
|
||||||
|
[DisableCors]
|
||||||
|
public void Action()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Here the constraints on the methods are acting as an IActionHttpMethodProvider and
|
// Here the constraints on the methods are acting as an IActionHttpMethodProvider and
|
||||||
// not as an IRouteTemplateProvider given that there is no RouteAttribute
|
// not as an IRouteTemplateProvider given that there is no RouteAttribute
|
||||||
// on the controller and the template for all the constraints on a method is null.
|
// on the controller and the template for all the constraints on a method is null.
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,11 @@ using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNet.Authorization;
|
using Microsoft.AspNet.Authorization;
|
||||||
|
using Microsoft.AspNet.Cors.Core;
|
||||||
using Microsoft.AspNet.Mvc.Filters;
|
using Microsoft.AspNet.Mvc.Filters;
|
||||||
using Microsoft.Framework.Internal;
|
using Microsoft.Framework.Internal;
|
||||||
|
using Microsoft.Framework.OptionsModel;
|
||||||
|
using Moq;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc.ApplicationModels
|
namespace Microsoft.AspNet.Mvc.ApplicationModels
|
||||||
|
|
@ -47,6 +50,48 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
|
||||||
Assert.True(model.Filters.Any(f => f is AuthorizeFilter));
|
Assert.True(model.Filters.Any(f => f is AuthorizeFilter));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void BuildControllerModel_EnableCorsAttributeAddsCorsAuthorizationFilterFactory()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var corsOptions = new CorsOptions();
|
||||||
|
corsOptions.AddPolicy("policy", new CorsPolicy());
|
||||||
|
var mockOptions = new Mock<IOptions<CorsOptions>>();
|
||||||
|
mockOptions.SetupGet(o => o.Options)
|
||||||
|
.Returns(corsOptions);
|
||||||
|
var builder = new DefaultControllerModelBuilder(new DefaultActionModelBuilder(null),
|
||||||
|
NullLoggerFactory.Instance,
|
||||||
|
authorizationOptions: null);
|
||||||
|
var typeInfo = typeof(CorsController).GetTypeInfo();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var model = builder.BuildControllerModel(typeInfo);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Single(model.Filters, f => f is CorsAuthorizationFilterFactory);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void BuildControllerModel_DisableCorsAttributeAddsDisableCorsAuthorizationFilter()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var corsOptions = new CorsOptions();
|
||||||
|
corsOptions.AddPolicy("policy", new CorsPolicy());
|
||||||
|
var mockOptions = new Mock<IOptions<CorsOptions>>();
|
||||||
|
mockOptions.SetupGet(o => o.Options)
|
||||||
|
.Returns(corsOptions);
|
||||||
|
var builder = new DefaultControllerModelBuilder(new DefaultActionModelBuilder(null),
|
||||||
|
NullLoggerFactory.Instance,
|
||||||
|
authorizationOptions: null);
|
||||||
|
var typeInfo = typeof(DisableCorsController).GetTypeInfo();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var model = builder.BuildControllerModel(typeInfo);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.True(model.Filters.Any(f => f is DisableCorsAuthorizationFilter));
|
||||||
|
}
|
||||||
|
|
||||||
// This class has a filter attribute, but doesn't implement any filter interfaces,
|
// This class has a filter attribute, but doesn't implement any filter interfaces,
|
||||||
// so ControllerFilter is not present.
|
// so ControllerFilter is not present.
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -113,6 +158,16 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[EnableCors("policy")]
|
||||||
|
public class CorsController
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
[DisableCors]
|
||||||
|
public class DisableCorsController
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
public class SomeFiltersController : IAsyncActionFilter, IResultFilter
|
public class SomeFiltersController : IAsyncActionFilter, IResultFilter
|
||||||
{
|
{
|
||||||
public Task OnActionExecutionAsync(
|
public Task OnActionExecutionAsync(
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ using System.Reflection;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNet.Http;
|
using Microsoft.AspNet.Http;
|
||||||
using Microsoft.AspNet.Http.Core;
|
using Microsoft.AspNet.Http.Core;
|
||||||
|
using Microsoft.AspNet.Http.Core.Collections;
|
||||||
using Microsoft.AspNet.Mvc.ActionConstraints;
|
using Microsoft.AspNet.Mvc.ActionConstraints;
|
||||||
using Microsoft.AspNet.Mvc.ApplicationModels;
|
using Microsoft.AspNet.Mvc.ApplicationModels;
|
||||||
using Microsoft.AspNet.Mvc.Core;
|
using Microsoft.AspNet.Mvc.Core;
|
||||||
|
|
@ -867,7 +868,7 @@ namespace Microsoft.AspNet.Mvc
|
||||||
var request = new Mock<HttpRequest>(MockBehavior.Strict);
|
var request = new Mock<HttpRequest>(MockBehavior.Strict);
|
||||||
request.SetupGet(r => r.Method).Returns(httpMethod);
|
request.SetupGet(r => r.Method).Returns(httpMethod);
|
||||||
request.SetupGet(r => r.Path).Returns(new PathString());
|
request.SetupGet(r => r.Path).Returns(new PathString());
|
||||||
|
request.SetupGet(r => r.Headers).Returns(new HeaderDictionary());
|
||||||
httpContext.SetupGet(c => c.Request).Returns(request.Object);
|
httpContext.SetupGet(c => c.Request).Returns(request.Object);
|
||||||
httpContext.SetupGet(c => c.RequestServices).Returns(serviceContainer);
|
httpContext.SetupGet(c => c.RequestServices).Returns(serviceContainer);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,281 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNet.Cors.Core;
|
||||||
|
using Microsoft.AspNet.Http;
|
||||||
|
using Microsoft.AspNet.Http.Core;
|
||||||
|
using Microsoft.AspNet.Routing;
|
||||||
|
using Microsoft.Framework.DependencyInjection;
|
||||||
|
using Moq;
|
||||||
|
using Newtonsoft.Json.Utilities;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Mvc.Test
|
||||||
|
{
|
||||||
|
public class CorsAuthorizationFilterTest
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public async Task PreFlightRequest_SuccessfulMatch_WritesHeaders()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var mockEngine = GetPassingEngine(supportsCredentials:true);
|
||||||
|
var filter = GetFilter(mockEngine);
|
||||||
|
|
||||||
|
var authorizationContext = GetAuthorizationContext(
|
||||||
|
new[] { new FilterDescriptor(filter, FilterScope.Action) },
|
||||||
|
GetRequestHeaders(true),
|
||||||
|
isPreflight: true);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await filter.OnAuthorizationAsync(authorizationContext);
|
||||||
|
await authorizationContext.Result.ExecuteResultAsync(authorizationContext);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var response = authorizationContext.HttpContext.Response;
|
||||||
|
Assert.Equal(200, response.StatusCode);
|
||||||
|
Assert.Equal("http://example.com", response.Headers[CorsConstants.AccessControlAllowOrigin]);
|
||||||
|
Assert.Equal("header1,header2", response.Headers[CorsConstants.AccessControlAllowHeaders]);
|
||||||
|
|
||||||
|
// Notice: GET header gets filtered because it is a simple header.
|
||||||
|
Assert.Equal("PUT", response.Headers[CorsConstants.AccessControlAllowMethods]);
|
||||||
|
Assert.Equal("exposed1,exposed2", response.Headers[CorsConstants.AccessControlExposeHeaders]);
|
||||||
|
Assert.Equal("123", response.Headers[CorsConstants.AccessControlMaxAge]);
|
||||||
|
Assert.Equal("true", response.Headers[CorsConstants.AccessControlAllowCredentials]);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task PreFlight_FailedMatch_Writes200()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var mockEngine = GetFailingEngine();
|
||||||
|
var filter = GetFilter(mockEngine);
|
||||||
|
|
||||||
|
var authorizationContext = GetAuthorizationContext(
|
||||||
|
new[] { new FilterDescriptor(filter, FilterScope.Action) },
|
||||||
|
GetRequestHeaders(),
|
||||||
|
isPreflight: true);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await filter.OnAuthorizationAsync(authorizationContext);
|
||||||
|
await authorizationContext.Result.ExecuteResultAsync(authorizationContext);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(200, authorizationContext.HttpContext.Response.StatusCode);
|
||||||
|
Assert.Empty(authorizationContext.HttpContext.Response.Headers);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task CorsRequest_SuccessfulMatch_WritesHeaders()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var mockEngine = GetPassingEngine(supportsCredentials: true);
|
||||||
|
var filter = GetFilter(mockEngine);
|
||||||
|
|
||||||
|
var authorizationContext = GetAuthorizationContext(
|
||||||
|
new[] { new FilterDescriptor(filter, FilterScope.Action) },
|
||||||
|
GetRequestHeaders(true),
|
||||||
|
isPreflight: true);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await filter.OnAuthorizationAsync(authorizationContext);
|
||||||
|
await authorizationContext.Result.ExecuteResultAsync(authorizationContext);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var response = authorizationContext.HttpContext.Response;
|
||||||
|
Assert.Equal(200, response.StatusCode);
|
||||||
|
Assert.Equal("http://example.com", response.Headers[CorsConstants.AccessControlAllowOrigin]);
|
||||||
|
Assert.Equal("exposed1,exposed2", response.Headers[CorsConstants.AccessControlExposeHeaders]);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task CorsRequest_FailedMatch_Writes200()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var mockEngine = GetFailingEngine();
|
||||||
|
var filter = GetFilter(mockEngine);
|
||||||
|
|
||||||
|
var authorizationContext = GetAuthorizationContext(
|
||||||
|
new[] { new FilterDescriptor(filter, FilterScope.Action) },
|
||||||
|
GetRequestHeaders(),
|
||||||
|
isPreflight: false);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await filter.OnAuthorizationAsync(authorizationContext);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(200, authorizationContext.HttpContext.Response.StatusCode);
|
||||||
|
Assert.Empty(authorizationContext.HttpContext.Response.Headers);
|
||||||
|
}
|
||||||
|
|
||||||
|
private CorsAuthorizationFilter GetFilter(ICorsService corsService)
|
||||||
|
{
|
||||||
|
var policyProvider = new Mock<ICorsPolicyProvider>();
|
||||||
|
policyProvider
|
||||||
|
.Setup(o => o.GetPolicyAsync(It.IsAny<HttpContext>(), It.IsAny<string>()))
|
||||||
|
.Returns(Task.FromResult(new CorsPolicy()));
|
||||||
|
|
||||||
|
return new CorsAuthorizationFilter(corsService, policyProvider.Object)
|
||||||
|
{
|
||||||
|
PolicyName = string.Empty
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private AuthorizationContext GetAuthorizationContext(
|
||||||
|
FilterDescriptor[] filterDescriptors,
|
||||||
|
RequestHeaders headers = null,
|
||||||
|
bool isPreflight = false)
|
||||||
|
{
|
||||||
|
|
||||||
|
// HttpContext
|
||||||
|
var httpContext = new DefaultHttpContext();
|
||||||
|
if (headers != null)
|
||||||
|
{
|
||||||
|
httpContext.Request.Headers.Add(CorsConstants.AccessControlRequestHeaders, headers.Headers.Split(','));
|
||||||
|
httpContext.Request.Headers.Add(CorsConstants.AccessControlRequestMethod, new[] { headers.Method });
|
||||||
|
httpContext.Request.Headers.Add(CorsConstants.AccessControlExposeHeaders, headers.ExposedHeaders.Split(','));
|
||||||
|
httpContext.Request.Headers.Add(CorsConstants.Origin, new[] { headers.Origin });
|
||||||
|
}
|
||||||
|
|
||||||
|
var method = isPreflight ? CorsConstants.PreflightHttpMethod : "GET";
|
||||||
|
httpContext.Request.Method = method;
|
||||||
|
|
||||||
|
// AuthorizationContext
|
||||||
|
var actionContext = new ActionContext(
|
||||||
|
httpContext: httpContext,
|
||||||
|
routeData: new RouteData(),
|
||||||
|
actionDescriptor: new ActionDescriptor() { FilterDescriptors = filterDescriptors });
|
||||||
|
|
||||||
|
var authorizationContext = new AuthorizationContext(
|
||||||
|
actionContext,
|
||||||
|
filterDescriptors.Select(filter => filter.Filter).ToList()
|
||||||
|
);
|
||||||
|
|
||||||
|
return authorizationContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ICorsService GetFailingEngine()
|
||||||
|
{
|
||||||
|
var mockEngine = new Mock<ICorsService>();
|
||||||
|
var result = GetCorsResult("http://example.com");
|
||||||
|
|
||||||
|
mockEngine
|
||||||
|
.Setup(o => o.EvaluatePolicy(It.IsAny<HttpContext>(), It.IsAny<CorsPolicy>()))
|
||||||
|
.Returns(result);
|
||||||
|
return mockEngine.Object;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ICorsService GetPassingEngine(bool supportsCredentials = false)
|
||||||
|
{
|
||||||
|
var mockEngine = new Mock<ICorsService>();
|
||||||
|
var result = GetCorsResult(
|
||||||
|
"http://example.com",
|
||||||
|
new List<string> { "header1", "header2" },
|
||||||
|
new List<string> { "PUT" },
|
||||||
|
new List<string> { "exposed1", "exposed2" },
|
||||||
|
123,
|
||||||
|
supportsCredentials);
|
||||||
|
|
||||||
|
mockEngine
|
||||||
|
.Setup(o => o.EvaluatePolicy(It.IsAny<HttpContext>(), It.IsAny<CorsPolicy>()))
|
||||||
|
.Returns(result);
|
||||||
|
|
||||||
|
mockEngine
|
||||||
|
.Setup(o => o.ApplyResult(It.IsAny<CorsResult>(), It.IsAny<HttpResponse>()))
|
||||||
|
.Callback<CorsResult, HttpResponse>((result1, response1) =>
|
||||||
|
{
|
||||||
|
var headers = response1.Headers;
|
||||||
|
headers.Set(
|
||||||
|
CorsConstants.AccessControlMaxAge,
|
||||||
|
result1.PreflightMaxAge.Value.TotalSeconds.ToString());
|
||||||
|
headers.Add(CorsConstants.AccessControlAllowOrigin, new[] { result1.AllowedOrigin });
|
||||||
|
if (result1.SupportsCredentials)
|
||||||
|
{
|
||||||
|
headers.Add(CorsConstants.AccessControlAllowCredentials, new[] { "true" });
|
||||||
|
}
|
||||||
|
|
||||||
|
headers.Add(CorsConstants.AccessControlAllowHeaders, result1.AllowedHeaders.ToArray());
|
||||||
|
headers.Add(CorsConstants.AccessControlAllowMethods, result1.AllowedMethods.ToArray());
|
||||||
|
headers.Add(CorsConstants.AccessControlExposeHeaders, result1.AllowedExposedHeaders.ToArray());
|
||||||
|
});
|
||||||
|
|
||||||
|
return mockEngine.Object;
|
||||||
|
}
|
||||||
|
|
||||||
|
private RequestHeaders GetRequestHeaders(bool supportsCredentials = false)
|
||||||
|
{
|
||||||
|
return new RequestHeaders
|
||||||
|
{
|
||||||
|
Origin = "http://example.com",
|
||||||
|
Headers = "header1,header2",
|
||||||
|
Method = "GET",
|
||||||
|
ExposedHeaders = "exposed1,exposed2",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private CorsResult GetCorsResult(
|
||||||
|
string origin = null,
|
||||||
|
IList<string> headers = null,
|
||||||
|
IList<string> methods = null,
|
||||||
|
IList<string> exposedHeaders = null,
|
||||||
|
long? preFlightMaxAge = null,
|
||||||
|
bool? supportsCredentials = null)
|
||||||
|
{
|
||||||
|
var result = new CorsResult();
|
||||||
|
|
||||||
|
if (origin != null)
|
||||||
|
{
|
||||||
|
result.AllowedOrigin = origin;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (headers != null)
|
||||||
|
{
|
||||||
|
AddRange(result.AllowedHeaders, headers);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (methods != null)
|
||||||
|
{
|
||||||
|
AddRange(result.AllowedMethods, methods);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (exposedHeaders != null)
|
||||||
|
{
|
||||||
|
AddRange(result.AllowedExposedHeaders, exposedHeaders);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (preFlightMaxAge != null)
|
||||||
|
{
|
||||||
|
result.PreflightMaxAge = TimeSpan.FromSeconds(preFlightMaxAge.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (supportsCredentials != null)
|
||||||
|
{
|
||||||
|
result.SupportsCredentials = supportsCredentials.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddRange(IList<string> target, IList<string> source)
|
||||||
|
{
|
||||||
|
foreach (var item in source)
|
||||||
|
{
|
||||||
|
target.Add(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class RequestHeaders
|
||||||
|
{
|
||||||
|
public string Origin { get; set; }
|
||||||
|
|
||||||
|
public string Headers { get; set; }
|
||||||
|
|
||||||
|
public string ExposedHeaders { get; set; }
|
||||||
|
|
||||||
|
public string Method { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,104 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNet.Builder;
|
||||||
|
using Microsoft.AspNet.Cors.Core;
|
||||||
|
using Microsoft.AspNet.Http;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Mvc.FunctionalTests
|
||||||
|
{
|
||||||
|
public class CorsMiddlewareTests
|
||||||
|
{
|
||||||
|
private const string SiteName = nameof(CorsMiddlewareWebSite);
|
||||||
|
private readonly Action<IApplicationBuilder> _app = new CorsMiddlewareWebSite.Startup().Configure;
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("GET")]
|
||||||
|
[InlineData("HEAD")]
|
||||||
|
[InlineData("POST")]
|
||||||
|
public async Task ResourceWithSimpleRequestPolicy_Allows_SimpleRequests(string method)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var server = TestHelper.CreateServer(_app, SiteName);
|
||||||
|
var client = server.CreateClient();
|
||||||
|
var origin = "http://example.com";
|
||||||
|
|
||||||
|
var requestBuilder = server
|
||||||
|
.CreateRequest("http://localhost/CorsMiddleware/GetExclusiveContent")
|
||||||
|
.AddHeader(CorsConstants.Origin, origin);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var response = await requestBuilder.SendAsync(method);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||||
|
var content = await response.Content.ReadAsStringAsync();
|
||||||
|
Assert.Equal("exclusive", content);
|
||||||
|
var responseHeaders = response.Headers;
|
||||||
|
var header = Assert.Single(response.Headers);
|
||||||
|
Assert.Equal(CorsConstants.AccessControlAllowOrigin, header.Key);
|
||||||
|
Assert.Equal(new[] { "http://example.com" }, header.Value.ToArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("GET")]
|
||||||
|
[InlineData("HEAD")]
|
||||||
|
[InlineData("POST")]
|
||||||
|
[InlineData("PUT")]
|
||||||
|
public async Task PolicyFailed_Disallows_PreFlightRequest(string method)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var server = TestHelper.CreateServer(_app, SiteName);
|
||||||
|
var client = server.CreateClient();
|
||||||
|
|
||||||
|
// Adding a custom header makes it a non simple request.
|
||||||
|
var requestBuilder = server
|
||||||
|
.CreateRequest("http://localhost/CorsMiddleware/GetExclusiveContent")
|
||||||
|
.AddHeader(CorsConstants.Origin, "http://example.com")
|
||||||
|
.AddHeader(CorsConstants.AccessControlRequestMethod, method)
|
||||||
|
.AddHeader(CorsConstants.AccessControlRequestHeaders, "Custom");
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var response = await requestBuilder.SendAsync(CorsConstants.PreflightHttpMethod);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
// Middleware applied the policy and since that did not pass, there were no access control headers.
|
||||||
|
Assert.Equal(HttpStatusCode.NoContent, response.StatusCode);
|
||||||
|
Assert.Empty(response.Headers);
|
||||||
|
|
||||||
|
// It should short circuit and hence no result.
|
||||||
|
var content = await response.Content.ReadAsStringAsync();
|
||||||
|
Assert.Equal(string.Empty, content);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task PolicyFailed_Allows_ActualRequest_WithMissingResponseHeaders()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var server = TestHelper.CreateServer(_app, SiteName);
|
||||||
|
var client = server.CreateClient();
|
||||||
|
|
||||||
|
// Adding a custom header makes it a non simple request.
|
||||||
|
var requestBuilder = server
|
||||||
|
.CreateRequest("http://localhost/CorsMiddleware/GetExclusiveContent")
|
||||||
|
.AddHeader(CorsConstants.Origin, "http://example2.com");
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var response = await requestBuilder.SendAsync("PUT");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
// Middleware applied the policy and since that did not pass, there were no access control headers.
|
||||||
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||||
|
Assert.Empty(response.Headers);
|
||||||
|
|
||||||
|
// It still has executed the action.
|
||||||
|
var content = await response.Content.ReadAsStringAsync();
|
||||||
|
Assert.Equal("exclusive", content);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,231 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNet.Builder;
|
||||||
|
using Microsoft.AspNet.Cors.Core;
|
||||||
|
using Microsoft.AspNet.Http;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Mvc.FunctionalTests
|
||||||
|
{
|
||||||
|
public class CorsTests
|
||||||
|
{
|
||||||
|
private const string SiteName = nameof(CorsWebSite);
|
||||||
|
private readonly Action<IApplicationBuilder> _app = new CorsWebSite.Startup().Configure;
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("GET")]
|
||||||
|
[InlineData("HEAD")]
|
||||||
|
[InlineData("POST")]
|
||||||
|
public async Task ResourceWithSimpleRequestPolicy_Allows_SimpleRequests(string method)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var server = TestHelper.CreateServer(_app, SiteName);
|
||||||
|
var client = server.CreateClient();
|
||||||
|
var origin = "http://example.com";
|
||||||
|
|
||||||
|
var requestBuilder = server
|
||||||
|
.CreateRequest("http://localhost/Cors/GetBlogComments")
|
||||||
|
.AddHeader(CorsConstants.Origin, origin);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var response = await requestBuilder.SendAsync(method);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||||
|
var content = await response.Content.ReadAsStringAsync();
|
||||||
|
Assert.Equal("[\"comment1\",\"comment2\",\"comment3\"]", content);
|
||||||
|
var responseHeaders = response.Headers;
|
||||||
|
var header = Assert.Single(response.Headers);
|
||||||
|
Assert.Equal(CorsConstants.AccessControlAllowOrigin, header.Key);
|
||||||
|
Assert.Equal(new[] { "*" }, header.Value.ToArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("GET")]
|
||||||
|
[InlineData("HEAD")]
|
||||||
|
[InlineData("POST")]
|
||||||
|
[InlineData("PUT")]
|
||||||
|
public async Task PolicyFailed_Disallows_PreFlightRequest(string method)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var server = TestHelper.CreateServer(_app, SiteName);
|
||||||
|
var client = server.CreateClient();
|
||||||
|
|
||||||
|
// Adding a custom header makes it a non simple request.
|
||||||
|
var requestBuilder = server
|
||||||
|
.CreateRequest("http://localhost/Cors/GetBlogComments")
|
||||||
|
.AddHeader(CorsConstants.Origin, "http://example.com")
|
||||||
|
.AddHeader(CorsConstants.AccessControlRequestMethod, method)
|
||||||
|
.AddHeader(CorsConstants.AccessControlRequestHeaders, "Custom");
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var response = await requestBuilder.SendAsync(CorsConstants.PreflightHttpMethod);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
// MVC applied the policy and since that did not pass, there were no access control headers.
|
||||||
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||||
|
Assert.Empty(response.Headers);
|
||||||
|
|
||||||
|
// It should short circuit and hence no result.
|
||||||
|
var content = await response.Content.ReadAsStringAsync();
|
||||||
|
Assert.Equal(string.Empty, content);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task SuccessfulCorsRequest_AllowsCredentials_IfThePolicyAllowsCredentials()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var server = TestHelper.CreateServer(_app, SiteName);
|
||||||
|
var client = server.CreateClient();
|
||||||
|
|
||||||
|
// Adding a custom header makes it a non simple request.
|
||||||
|
var requestBuilder = server
|
||||||
|
.CreateRequest("http://localhost/Cors/EditUserComment?userComment=abcd")
|
||||||
|
.AddHeader(CorsConstants.Origin, "http://example.com")
|
||||||
|
.AddHeader(CorsConstants.AccessControlExposeHeaders, "exposed1,exposed2");
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var response = await requestBuilder.SendAsync("PUT");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||||
|
var responseHeaders = response.Headers;
|
||||||
|
Assert.Equal(
|
||||||
|
new[] { "http://example.com" },
|
||||||
|
responseHeaders.GetValues(CorsConstants.AccessControlAllowOrigin).ToArray());
|
||||||
|
Assert.Equal(
|
||||||
|
new[] { "true" },
|
||||||
|
responseHeaders.GetValues(CorsConstants.AccessControlAllowCredentials).ToArray());
|
||||||
|
Assert.Equal(
|
||||||
|
new[] { "exposed1", "exposed2" },
|
||||||
|
responseHeaders.GetValues(CorsConstants.AccessControlExposeHeaders).ToArray());
|
||||||
|
|
||||||
|
var content = await response.Content.ReadAsStringAsync();
|
||||||
|
Assert.Equal("abcd", content);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task SuccessfulPreflightRequest_AllowsCredentials_IfThePolicyAllowsCredentials()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var server = TestHelper.CreateServer(_app, SiteName);
|
||||||
|
var client = server.CreateClient();
|
||||||
|
|
||||||
|
// Adding a custom header makes it a non simple request.
|
||||||
|
var requestBuilder = server
|
||||||
|
.CreateRequest("http://localhost/Cors/EditUserComment?userComment=abcd")
|
||||||
|
.AddHeader(CorsConstants.Origin, "http://example.com")
|
||||||
|
.AddHeader(CorsConstants.AccessControlRequestMethod, "PUT")
|
||||||
|
.AddHeader(CorsConstants.AccessControlRequestHeaders, "header1,header2");
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var response = await requestBuilder.SendAsync(CorsConstants.PreflightHttpMethod);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||||
|
var responseHeaders = response.Headers;
|
||||||
|
Assert.Equal(
|
||||||
|
new[] { "http://example.com" },
|
||||||
|
responseHeaders.GetValues(CorsConstants.AccessControlAllowOrigin).ToArray());
|
||||||
|
Assert.Equal(
|
||||||
|
new[] { "true" },
|
||||||
|
responseHeaders.GetValues(CorsConstants.AccessControlAllowCredentials).ToArray());
|
||||||
|
Assert.Equal(
|
||||||
|
new[] { "header1", "header2" },
|
||||||
|
responseHeaders.GetValues(CorsConstants.AccessControlAllowHeaders).ToArray());
|
||||||
|
Assert.Equal(
|
||||||
|
new[] { "PUT" },
|
||||||
|
responseHeaders.GetValues(CorsConstants.AccessControlAllowMethods).ToArray());
|
||||||
|
|
||||||
|
var content = await response.Content.ReadAsStringAsync();
|
||||||
|
Assert.Empty(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task PolicyFailed_Allows_ActualRequest_WithMissingResponseHeaders()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var server = TestHelper.CreateServer(_app, SiteName);
|
||||||
|
var client = server.CreateClient();
|
||||||
|
|
||||||
|
// Adding a custom header makes it a non simple request.
|
||||||
|
var requestBuilder = server
|
||||||
|
.CreateRequest("http://localhost/Cors/GetUserComments")
|
||||||
|
.AddHeader(CorsConstants.Origin, "http://example2.com");
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var response = await requestBuilder.SendAsync("PUT");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
// MVC applied the policy and since that did not pass, there were no access control headers.
|
||||||
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||||
|
Assert.Empty(response.Headers);
|
||||||
|
|
||||||
|
// It still have executed the action.
|
||||||
|
var content = await response.Content.ReadAsStringAsync();
|
||||||
|
Assert.Equal("[\"usercomment1\",\"usercomment2\",\"usercomment3\"]", content);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("GET")]
|
||||||
|
[InlineData("HEAD")]
|
||||||
|
[InlineData("POST")]
|
||||||
|
public async Task DisableCors_ActionsCanOverride_ControllerLevel(string method)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var server = TestHelper.CreateServer(_app, SiteName);
|
||||||
|
var client = server.CreateClient();
|
||||||
|
|
||||||
|
// Exclusive content is not available on other sites.
|
||||||
|
var requestBuilder = server
|
||||||
|
.CreateRequest("http://localhost/Cors/GetExclusiveContent")
|
||||||
|
.AddHeader(CorsConstants.Origin, "http://example.com");
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var response = await requestBuilder.SendAsync(method);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||||
|
|
||||||
|
// Since there are no response headers, the client should step in to block the content.
|
||||||
|
Assert.Empty(response.Headers);
|
||||||
|
var content = await response.Content.ReadAsStringAsync();
|
||||||
|
Assert.Equal("exclusive", content);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("GET")]
|
||||||
|
[InlineData("HEAD")]
|
||||||
|
[InlineData("POST")]
|
||||||
|
public async Task DisableCors_PreFlight_ActionsCanOverride_ControllerLevel(string method)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var server = TestHelper.CreateServer(_app, SiteName);
|
||||||
|
var client = server.CreateClient();
|
||||||
|
|
||||||
|
// Exclusive content is not available on other sites.
|
||||||
|
var requestBuilder = server
|
||||||
|
.CreateRequest("http://localhost/Cors/GetExclusiveContent")
|
||||||
|
.AddHeader(CorsConstants.Origin, "http://example.com")
|
||||||
|
.AddHeader(CorsConstants.AccessControlRequestMethod, method)
|
||||||
|
.AddHeader(CorsConstants.AccessControlRequestHeaders, "Custom");
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var response = await requestBuilder.SendAsync(CorsConstants.PreflightHttpMethod);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
// Since there are no response headers, the client should step in to block the content.
|
||||||
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||||
|
Assert.Empty(response.Headers);
|
||||||
|
|
||||||
|
// Nothing gets executed for a pre-flight request.
|
||||||
|
var content = await response.Content.ReadAsStringAsync();
|
||||||
|
Assert.Empty(content);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -20,6 +20,8 @@
|
||||||
"ContentNegotiationWebSite": "1.0.0",
|
"ContentNegotiationWebSite": "1.0.0",
|
||||||
"ControllerDiscoveryConventionsWebSite": "1.0.0",
|
"ControllerDiscoveryConventionsWebSite": "1.0.0",
|
||||||
"ControllersFromServicesWebSite": "1.0.0",
|
"ControllersFromServicesWebSite": "1.0.0",
|
||||||
|
"CorsWebSite": "1.0.0",
|
||||||
|
"CorsMiddlewareWebSite": "1.0.0",
|
||||||
"CustomRouteWebSite": "1.0.0",
|
"CustomRouteWebSite": "1.0.0",
|
||||||
"ErrorPageMiddlewareWebSite": "1.0.0",
|
"ErrorPageMiddlewareWebSite": "1.0.0",
|
||||||
"FilesWebSite": "1.0.0",
|
"FilesWebSite": "1.0.0",
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using Microsoft.AspNet.Mvc;
|
||||||
|
|
||||||
|
namespace CorsMiddlewareWebSite
|
||||||
|
{
|
||||||
|
[Route("CorsMiddleWare/[action]")]
|
||||||
|
public class BlogController : Controller
|
||||||
|
{
|
||||||
|
public string GetExclusiveContent()
|
||||||
|
{
|
||||||
|
return "exclusive";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<PropertyGroup>
|
||||||
|
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
|
||||||
|
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.Props" Condition="'$(VSToolsPath)' != ''" />
|
||||||
|
<PropertyGroup Label="Globals">
|
||||||
|
<ProjectGuid>94ba134d-04b3-48aa-ba55-5a4db8640f2d</ProjectGuid>
|
||||||
|
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">..\..\..\artifacts\obj\$(MSBuildProjectName)</BaseIntermediateOutputPath>
|
||||||
|
<OutputPath Condition="'$(OutputPath)'=='' ">..\..\..\artifacts\bin\$(MSBuildProjectName)\</OutputPath>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup>
|
||||||
|
<SchemaVersion>2.0</SchemaVersion>
|
||||||
|
<DevelopmentServerPort>41642</DevelopmentServerPort>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.targets" Condition="'$(VSToolsPath)' != ''" />
|
||||||
|
</Project>
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using Microsoft.AspNet.Builder;
|
||||||
|
using Microsoft.AspNet.Cors;
|
||||||
|
using Microsoft.AspNet.Mvc;
|
||||||
|
using Microsoft.Framework.DependencyInjection;
|
||||||
|
|
||||||
|
namespace CorsMiddlewareWebSite
|
||||||
|
{
|
||||||
|
public class Startup
|
||||||
|
{
|
||||||
|
public void Configure(IApplicationBuilder app)
|
||||||
|
{
|
||||||
|
var configuration = app.GetTestConfiguration();
|
||||||
|
|
||||||
|
app.UseServices(services =>
|
||||||
|
{
|
||||||
|
services.AddMvc();
|
||||||
|
});
|
||||||
|
|
||||||
|
app.UseCors(policy => policy.WithOrigins("http://example.com"));
|
||||||
|
app.UseMvc();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
{
|
||||||
|
"commands": {
|
||||||
|
"web": "Microsoft.AspNet.Hosting server=Microsoft.AspNet.Server.WebListener server.urls=http://localhost:5001",
|
||||||
|
"kestrel": "Microsoft.AspNet.Hosting --server Kestrel --server.urls http://localhost:5000"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"Kestrel": "1.0.0-*",
|
||||||
|
"Microsoft.AspNet.Cors": "1.0.0-*",
|
||||||
|
"Microsoft.AspNet.Cors.Core": "1.0.0-*",
|
||||||
|
"Microsoft.AspNet.Mvc": "6.0.0-*",
|
||||||
|
"Microsoft.AspNet.Mvc.Xml": "6.0.0-*",
|
||||||
|
"Microsoft.AspNet.Mvc.TestConfiguration": "1.0.0",
|
||||||
|
"Microsoft.AspNet.Server.IIS": "1.0.0-*",
|
||||||
|
"Microsoft.AspNet.Server.WebListener": "1.0.0-*",
|
||||||
|
"Microsoft.AspNet.StaticFiles": "1.0.0-*"
|
||||||
|
},
|
||||||
|
"frameworks": {
|
||||||
|
"dnx451": { },
|
||||||
|
"dnx50": { }
|
||||||
|
},
|
||||||
|
"webroot": "wwwroot"
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
CorsMiddlewareWebSite
|
||||||
|
===
|
||||||
|
|
||||||
|
This web site illustrates how to use CorsMiddleware to apply a policy for entire application.
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
HelloWorld
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. 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 Microsoft.AspNet.Cors.Core;
|
||||||
|
using Microsoft.AspNet.Mvc;
|
||||||
|
|
||||||
|
namespace CorsWebSite
|
||||||
|
{
|
||||||
|
[Route("Cors/[action]")]
|
||||||
|
[EnableCors("AllowAnySimpleRequest")]
|
||||||
|
public class BlogController : Controller
|
||||||
|
{
|
||||||
|
public IEnumerable<string> GetBlogComments(int id)
|
||||||
|
{
|
||||||
|
return new[] { "comment1", "comment2", "comment3" };
|
||||||
|
}
|
||||||
|
|
||||||
|
[EnableCors("AllowSpecificOrigin")]
|
||||||
|
public IEnumerable<string> GetUserComments(int id)
|
||||||
|
{
|
||||||
|
return new[] { "usercomment1", "usercomment2", "usercomment3" };
|
||||||
|
}
|
||||||
|
|
||||||
|
[DisableCors]
|
||||||
|
[AcceptVerbs("HEAD", "GET", "POST")]
|
||||||
|
public string GetExclusiveContent()
|
||||||
|
{
|
||||||
|
return "exclusive";
|
||||||
|
}
|
||||||
|
|
||||||
|
[EnableCors("WithCredentialsAnyOrigin")]
|
||||||
|
public string EditUserComment(int id, string userComment)
|
||||||
|
{
|
||||||
|
return userComment;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<PropertyGroup>
|
||||||
|
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
|
||||||
|
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.Props" Condition="'$(VSToolsPath)' != ''" />
|
||||||
|
<PropertyGroup Label="Globals">
|
||||||
|
<ProjectGuid>94ba134d-04b3-48aa-ba55-5a4db8640f2d</ProjectGuid>
|
||||||
|
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">..\..\..\artifacts\obj\$(MSBuildProjectName)</BaseIntermediateOutputPath>
|
||||||
|
<OutputPath Condition="'$(OutputPath)'=='' ">..\..\..\artifacts\bin\$(MSBuildProjectName)\</OutputPath>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup>
|
||||||
|
<SchemaVersion>2.0</SchemaVersion>
|
||||||
|
<DevelopmentServerPort>41642</DevelopmentServerPort>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.targets" Condition="'$(VSToolsPath)' != ''" />
|
||||||
|
</Project>
|
||||||
|
|
@ -0,0 +1,60 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using Microsoft.AspNet.Builder;
|
||||||
|
using Microsoft.AspNet.Mvc;
|
||||||
|
using Microsoft.Framework.DependencyInjection;
|
||||||
|
|
||||||
|
namespace CorsWebSite
|
||||||
|
{
|
||||||
|
public class Startup
|
||||||
|
{
|
||||||
|
public void Configure(IApplicationBuilder app)
|
||||||
|
{
|
||||||
|
var configuration = app.GetTestConfiguration();
|
||||||
|
|
||||||
|
app.UseServices(services =>
|
||||||
|
{
|
||||||
|
services.AddMvc();
|
||||||
|
services.ConfigureCors(options =>
|
||||||
|
{
|
||||||
|
options.AddPolicy(
|
||||||
|
"AllowAnySimpleRequest",
|
||||||
|
builder =>
|
||||||
|
{
|
||||||
|
builder.AllowAnyOrigin()
|
||||||
|
.WithMethods("GET", "POST", "HEAD");
|
||||||
|
});
|
||||||
|
|
||||||
|
options.AddPolicy(
|
||||||
|
"AllowSpecificOrigin",
|
||||||
|
builder =>
|
||||||
|
{
|
||||||
|
builder.WithOrigins("http://example.com");
|
||||||
|
});
|
||||||
|
|
||||||
|
options.AddPolicy(
|
||||||
|
"WithCredentials",
|
||||||
|
builder =>
|
||||||
|
{
|
||||||
|
builder.AllowCredentials()
|
||||||
|
.WithOrigins("http://example.com");
|
||||||
|
});
|
||||||
|
|
||||||
|
options.AddPolicy(
|
||||||
|
"WithCredentialsAnyOrigin",
|
||||||
|
builder =>
|
||||||
|
{
|
||||||
|
builder.AllowCredentials()
|
||||||
|
.AllowAnyOrigin()
|
||||||
|
.AllowAnyHeader()
|
||||||
|
.WithMethods("PUT", "POST")
|
||||||
|
.WithExposedHeaders("exposed1", "exposed2");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
app.UseMvc();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
{
|
||||||
|
"commands": {
|
||||||
|
"web": "Microsoft.AspNet.Hosting server=Microsoft.AspNet.Server.WebListener server.urls=http://localhost:5001",
|
||||||
|
"kestrel": "Microsoft.AspNet.Hosting --server Kestrel --server.urls http://localhost:5000"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"Kestrel": "1.0.0-*",
|
||||||
|
"Microsoft.AspNet.Cors": "1.0.0-*",
|
||||||
|
"Microsoft.AspNet.Cors.Core": "1.0.0-*",
|
||||||
|
"Microsoft.AspNet.Mvc": "6.0.0-*",
|
||||||
|
"Microsoft.AspNet.Mvc.Xml": "6.0.0-*",
|
||||||
|
"Microsoft.AspNet.Mvc.TestConfiguration": "1.0.0",
|
||||||
|
"Microsoft.AspNet.Server.IIS": "1.0.0-*",
|
||||||
|
"Microsoft.AspNet.Server.WebListener": "1.0.0-*",
|
||||||
|
"Microsoft.AspNet.StaticFiles": "1.0.0-*"
|
||||||
|
},
|
||||||
|
"frameworks": {
|
||||||
|
"dnx451": { },
|
||||||
|
"dnx50": { }
|
||||||
|
},
|
||||||
|
"webroot": "wwwroot"
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
CorsWebSite
|
||||||
|
===
|
||||||
|
|
||||||
|
This web site illustrates how to configure actions to allow/disallow cross origin requests.
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
HelloWorld
|
||||||
Loading…
Reference in New Issue