PR comments incorporated. Working on making SampleDestination dynamic- define the policy within the sample through a UI.
This commit is contained in:
parent
5dcf47ef12
commit
b3bdba1559
4
CORS.sln
4
CORS.sln
|
|
@ -21,9 +21,9 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "CorsMiddlewareWebSite", "te
|
||||||
EndProject
|
EndProject
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{960E0703-A8A5-44DF-AA87-B7C614683B3C}"
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{960E0703-A8A5-44DF-AA87-B7C614683B3C}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "SampleDestination", "SampleDestination\SampleDestination.xproj", "{F6675DC1-AA21-453B-89B6-DA425FB9C3A5}"
|
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "SampleDestination", "samples\SampleDestination\SampleDestination.xproj", "{F6675DC1-AA21-453B-89B6-DA425FB9C3A5}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "SampleOrigin", "SampleOrigin\SampleOrigin.xproj", "{99460370-AE5D-4DC9-8DBF-04DF66D6B21D}"
|
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "SampleOrigin", "samples\SampleOrigin\SampleOrigin.xproj", "{99460370-AE5D-4DC9-8DBF-04DF66D6B21D}"
|
||||||
EndProject
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
|
|
||||||
|
|
@ -19,9 +19,15 @@ Save the file and close it. Then clear your browser history.
|
||||||
Run the sample
|
Run the sample
|
||||||
*In a command prompt window, open the directory where you cloned the repository, and open the SampleDestination directory. Run the command: dotnet run
|
*In a command prompt window, open the directory where you cloned the repository, and open the SampleDestination directory. Run the command: dotnet run
|
||||||
*Repeat the above step in the SampleOrigin directory.
|
*Repeat the above step in the SampleOrigin directory.
|
||||||
*Open a browser window and go to http://origin.example.com
|
*Open a browser window and go to http://origin.example.com:5001
|
||||||
*Click the button to see CORS in action.
|
*Click the button to see CORS in action.
|
||||||
|
|
||||||
|
The SampleOrigin application will use port 5001, and SampleDestination will use 5000. Please ensure there are no other processes using those ports before running the CORS sample.
|
||||||
|
|
||||||
|
As an example, apart from GET, HEAD and POST requests, PUT requests are allowed in the CORS policy on SampleDestination. Any others, like DELETE, OPTIONS etc. are not allowed and throw an error.
|
||||||
|
Content-Length has been added as an allowed header to the sample. Any other headers are not allowed and throw an error.
|
||||||
|
To edit the policy, please see app.UseCors() method in the Startup.cs file of SampleDestination.
|
||||||
|
|
||||||
If using Visual Studio to launch the request origin:
|
If using Visual Studio to launch the request origin:
|
||||||
Open Visual Studio and in the launchSettings.json file for the SampleOrigin project, change the launchUrl under SampleOrigin to
|
Open Visual Studio and in the launchSettings.json file for the SampleOrigin project, change the launchUrl under SampleOrigin to
|
||||||
http://origin.example.com:8080. Using the dropdown near the Start button, choose SampleOrigin before pressing Start to ensure that it uses Kestrel
|
http://origin.example.com:8080. Using the dropdown near the Start button, choose SampleOrigin before pressing Start to ensure that it uses Kestrel
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,6 @@ namespace SampleDestination
|
||||||
.UseKestrel()
|
.UseKestrel()
|
||||||
.UseUrls("http://*:5000")
|
.UseUrls("http://*:5000")
|
||||||
.UseContentRoot(Directory.GetCurrentDirectory())
|
.UseContentRoot(Directory.GetCurrentDirectory())
|
||||||
.UseIISIntegration()
|
|
||||||
.UseStartup<Startup>()
|
.UseStartup<Startup>()
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
using Microsoft.AspNetCore.Cors.Infrastructure;
|
||||||
using Microsoft.AspNetCore.Hosting;
|
using Microsoft.AspNetCore.Hosting;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
@ -19,10 +20,21 @@ namespace SampleDestination
|
||||||
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
|
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
|
||||||
{
|
{
|
||||||
loggerFactory.AddConsole();
|
loggerFactory.AddConsole();
|
||||||
app.UseCors(policy => policy.WithOrigins("http://origin.example.com:8080"));
|
|
||||||
|
app.UseCors(policy => policy
|
||||||
|
.WithOrigins("http://origin.example.com:5001")
|
||||||
|
.WithMethods("PUT")
|
||||||
|
.WithHeaders("Content-Length"));
|
||||||
|
|
||||||
app.Run(async context =>
|
app.Run(async context =>
|
||||||
{
|
{
|
||||||
await context.Response.WriteAsync("Status code of your request: " + context.Response.StatusCode.ToString());
|
var responseHeaders = context.Response.Headers;
|
||||||
|
foreach (var responseHeader in responseHeaders)
|
||||||
|
{
|
||||||
|
await context.Response.WriteAsync("\n"+responseHeader.Key+": "+responseHeader.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
await context.Response.WriteAsync("\nStatus code of your request: " + context.Response.StatusCode.ToString());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,16 +4,11 @@
|
||||||
"version": "1.1.0-*",
|
"version": "1.1.0-*",
|
||||||
"type": "platform"
|
"type": "platform"
|
||||||
},
|
},
|
||||||
"Microsoft.AspNetCore.Server.IISIntegration": "1.2.0-*",
|
|
||||||
"Microsoft.AspNetCore.Server.Kestrel": "1.2.0-*",
|
"Microsoft.AspNetCore.Server.Kestrel": "1.2.0-*",
|
||||||
"Microsoft.Extensions.Logging.Console": "1.2.0-*",
|
"Microsoft.Extensions.Logging.Console": "1.2.0-*",
|
||||||
"Microsoft.AspNetCore.Cors": "1.2.0-*"
|
"Microsoft.AspNetCore.Cors": "1.2.0-*"
|
||||||
},
|
},
|
||||||
|
|
||||||
"tools": {
|
|
||||||
"Microsoft.AspNetCore.Server.IISIntegration.Tools": "1.0.0-*"
|
|
||||||
},
|
|
||||||
|
|
||||||
"frameworks": {
|
"frameworks": {
|
||||||
"netcoreapp1.0": {
|
"netcoreapp1.0": {
|
||||||
"imports": [
|
"imports": [
|
||||||
|
|
@ -36,12 +31,7 @@
|
||||||
|
|
||||||
"publishOptions": {
|
"publishOptions": {
|
||||||
"include": [
|
"include": [
|
||||||
"wwwroot",
|
"wwwroot"
|
||||||
"web.config"
|
|
||||||
]
|
]
|
||||||
},
|
|
||||||
|
|
||||||
"scripts": {
|
|
||||||
"postpublish": [ "dotnet publish-iis --publish-folder %publish:OutputPath% --framework %publish:FullTargetFramework%" ]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,14 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<configuration>
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Configure your application settings in appsettings.json. Learn more at http://go.microsoft.com/fwlink/?LinkId=786380
|
|
||||||
-->
|
|
||||||
|
|
||||||
<system.webServer>
|
|
||||||
<handlers>
|
|
||||||
<add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModule" resourceType="Unspecified"/>
|
|
||||||
</handlers>
|
|
||||||
<aspNetCore processPath="%LAUNCHER_PATH%" arguments="%LAUNCHER_ARGS%" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" forwardWindowsAuthToken="false"/>
|
|
||||||
</system.webServer>
|
|
||||||
</configuration>
|
|
||||||
|
|
@ -12,9 +12,8 @@ namespace SampleOrigin
|
||||||
{
|
{
|
||||||
var host = new WebHostBuilder()
|
var host = new WebHostBuilder()
|
||||||
.UseKestrel()
|
.UseKestrel()
|
||||||
.UseUrls("http://*:8080")
|
.UseUrls("http://*:5001")
|
||||||
.UseContentRoot(Directory.GetCurrentDirectory())
|
.UseContentRoot(Directory.GetCurrentDirectory())
|
||||||
.UseIISIntegration()
|
|
||||||
.UseStartup<Startup>()
|
.UseStartup<Startup>()
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,15 +4,10 @@
|
||||||
"version": "1.1.0-*",
|
"version": "1.1.0-*",
|
||||||
"type": "platform"
|
"type": "platform"
|
||||||
},
|
},
|
||||||
"Microsoft.AspNetCore.Server.IISIntegration": "1.2.0-*",
|
|
||||||
"Microsoft.AspNetCore.Server.Kestrel": "1.2.0-*",
|
"Microsoft.AspNetCore.Server.Kestrel": "1.2.0-*",
|
||||||
"Microsoft.Extensions.Logging.Console": "1.2.0-*"
|
"Microsoft.Extensions.Logging.Console": "1.2.0-*"
|
||||||
},
|
},
|
||||||
|
|
||||||
"tools": {
|
|
||||||
"Microsoft.AspNetCore.Server.IISIntegration.Tools": "1.0.0-*"
|
|
||||||
},
|
|
||||||
|
|
||||||
"frameworks": {
|
"frameworks": {
|
||||||
"netcoreapp1.0": {
|
"netcoreapp1.0": {
|
||||||
"imports": [
|
"imports": [
|
||||||
|
|
@ -35,12 +30,7 @@
|
||||||
|
|
||||||
"publishOptions": {
|
"publishOptions": {
|
||||||
"include": [
|
"include": [
|
||||||
"wwwroot",
|
"wwwroot"
|
||||||
"web.config"
|
|
||||||
]
|
]
|
||||||
},
|
|
||||||
|
|
||||||
"scripts": {
|
|
||||||
"postpublish": [ "dotnet publish-iis --publish-folder %publish:OutputPath% --framework %publish:FullTargetFramework%" ]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,9 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<configuration>
|
<configuration>
|
||||||
|
|
||||||
<!--
|
|
||||||
Configure your application settings in appsettings.json. Learn more at http://go.microsoft.com/fwlink/?LinkId=786380
|
|
||||||
-->
|
|
||||||
|
|
||||||
<system.webServer>
|
<system.webServer>
|
||||||
<handlers>
|
<handlers>
|
||||||
<add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModule" resourceType="Unspecified"/>
|
<add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModule" resourceType="Unspecified" />
|
||||||
</handlers>
|
</handlers>
|
||||||
<aspNetCore processPath="%LAUNCHER_PATH%" arguments="%LAUNCHER_ARGS%" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" forwardWindowsAuthToken="false"/>
|
<aspNetCore processPath="%LAUNCHER_PATH%" arguments="%LAUNCHER_ARGS%" forwardWindowsAuthToken="false" stdoutLogEnabled="false" />
|
||||||
</system.webServer>
|
</system.webServer>
|
||||||
</configuration>
|
</configuration>
|
||||||
|
|
@ -2,30 +2,56 @@
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<script type="text/javascript">
|
<style>
|
||||||
|
p {
|
||||||
function createCORSRequest(method, url) {
|
font-size: 20px;
|
||||||
var request = new XMLHttpRequest();
|
|
||||||
request.open(method, url, true);
|
|
||||||
return request;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make the CORS request.
|
.button {
|
||||||
function makeCORSRequest(method) {
|
border: none;
|
||||||
|
color: white;
|
||||||
|
padding: 10px 15px;
|
||||||
|
text-align: center;
|
||||||
|
text-decoration: none;
|
||||||
|
display: inline-block;
|
||||||
|
font-size: 14px;
|
||||||
|
margin: 5px 5px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.green {
|
||||||
|
background-color: #4CAF50;
|
||||||
|
}
|
||||||
|
|
||||||
|
.red {
|
||||||
|
background-color: indianred;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gray {
|
||||||
|
background-color: gray;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<title>CORS Sample</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<script type="text/javascript">
|
||||||
|
// Make the CORS request.
|
||||||
|
function makeCORSRequest(method, headerName, headerValue) {
|
||||||
// Destination server with CORS enabled.
|
// Destination server with CORS enabled.
|
||||||
var url = 'http://destination.example.com:5000/api';
|
var url = 'http://destination.example.com:5000/';
|
||||||
var request = createCORSRequest(method , url);
|
var request = new XMLHttpRequest();
|
||||||
|
request.open(method, url, true);
|
||||||
|
request.setRequestHeader(headerName, headerValue);
|
||||||
|
|
||||||
if (!request) {
|
if (!request) {
|
||||||
alert('CORS not supported');
|
alert('CORS not supported');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Response handlers.
|
// Response handlers.
|
||||||
request.onload = function () {
|
request.onload = function () {
|
||||||
var text = request.responseText;
|
var text = request.responseText;
|
||||||
alert('Response from CORS '+method+' request to ' + url + ': ' + text);
|
alert('Response from CORS ' + method + ' request to ' + url + ': ' + text);
|
||||||
};
|
};
|
||||||
|
|
||||||
request.onerror = function () {
|
request.onerror = function () {
|
||||||
|
|
@ -34,16 +60,40 @@
|
||||||
|
|
||||||
request.send();
|
request.send();
|
||||||
}
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
<title></title>
|
<p>CORS Sample</p>
|
||||||
</head>
|
Method: <input type="text" id="methodName" /><br /><br />
|
||||||
<body>
|
Header Name: <input type="text" id="headerName" value="Content-Length" /> Header Value: <input type="text" id="headerValue" value="10" /><br /><br />
|
||||||
<form method="post">
|
<script>
|
||||||
<input id="clickMe" type="button" value="CORS Request" onclick="makeCORSRequest('GET');" />
|
document.getElementById('methodName')
|
||||||
</form>
|
.addEventListener("keyup", function (event) {
|
||||||
<form method="post">
|
event.preventDefault();
|
||||||
<input id="clickMe" type="button" value="Invalid CORS Request" onclick="makeCORSRequest('DELETE');" />
|
if (event.keyCode == 13) {
|
||||||
</form>
|
document.getElementById("headerName").focus();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
document.getElementById('headerName')
|
||||||
|
.addEventListener("keyup", function (event) {
|
||||||
|
event.preventDefault();
|
||||||
|
if (event.keyCode == 13) {
|
||||||
|
document.getElementById("headerValue").focus();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
document.getElementById('headerValue')
|
||||||
|
.addEventListener("keyup", function (event) {
|
||||||
|
event.preventDefault();
|
||||||
|
if (event.keyCode == 13) {
|
||||||
|
document.getElementById("CORS").click();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<button class="button gray" id="CORS" type="submit" onclick="makeCORSRequest(document.getElementById('methodName').value, document.getElementById('headerName').value, document.getElementById('headerValue').value);">Make a CORS Request</button><br /><br /><br /><br />
|
||||||
|
|
||||||
|
Method DELETE is not allowed:<button class="button red" id="InvalidMethodCORS" type="submit" onclick="makeCORSRequest('DELETE', 'Content-Length', '10');">Invalid Method CORS Request</button>
|
||||||
|
Method PUT is allowed:<button class="button green" id="InvalidMethodCORS" type="submit" onclick="makeCORSRequest('PUT', 'Content-Length', '10');">Valid Method CORS Request</button><br /><br />
|
||||||
|
|
||||||
|
Header 'Max-Forwards' not supported:<button class="button red" id="InvalidHeaderCORS" type="submit" onclick="makeCORSRequest('PUT', 'Max-Forwards', 'x');">Invalid Header CORS Request</button>
|
||||||
|
Header 'Content-Length' is supported:<button class="button green" id="InvalidHeaderCORS" type="submit" onclick="makeCORSRequest('PUT', 'Content-Length', 'x');">Valid Header CORS Request</button><br /><br />
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
@ -26,7 +26,7 @@ namespace Microsoft.AspNetCore.Cors.Infrastructure
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="options">The option model representing <see cref="CorsOptions"/>.</param>
|
/// <param name="options">The option model representing <see cref="CorsOptions"/>.</param>
|
||||||
public CorsService(IOptions<CorsOptions> options)
|
public CorsService(IOptions<CorsOptions> options)
|
||||||
:this(options, loggerFactory: null)
|
: this(options, loggerFactory: null)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -103,14 +103,14 @@ namespace Microsoft.AspNetCore.Cors.Infrastructure
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_logger?.RequestHasOriginHeader(origin);
|
||||||
if (!policy.AllowAnyOrigin && !policy.Origins.Contains(origin))
|
if (!policy.AllowAnyOrigin && !policy.Origins.Contains(origin))
|
||||||
{
|
{
|
||||||
_logger?.RequestHasOriginHeader();
|
_logger?.PolicyFailure();
|
||||||
_logger?.PolicyFailure($"Request origin {origin} does not have permission to access the resource.");
|
_logger?.OriginNotAllowed(origin);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_logger?.RequestHasOriginHeader();
|
|
||||||
AddOriginToResult(origin, policy, result);
|
AddOriginToResult(origin, policy, result);
|
||||||
result.SupportsCredentials = policy.SupportsCredentials;
|
result.SupportsCredentials = policy.SupportsCredentials;
|
||||||
AddHeaderValues(result.AllowedExposedHeaders, policy.ExposedHeaders);
|
AddHeaderValues(result.AllowedExposedHeaders, policy.ExposedHeaders);
|
||||||
|
|
@ -126,14 +126,14 @@ namespace Microsoft.AspNetCore.Cors.Infrastructure
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_logger?.RequestHasOriginHeader(origin);
|
||||||
if (!policy.AllowAnyOrigin && !policy.Origins.Contains(origin))
|
if (!policy.AllowAnyOrigin && !policy.Origins.Contains(origin))
|
||||||
{
|
{
|
||||||
_logger?.RequestHasOriginHeader();
|
_logger?.PolicyFailure();
|
||||||
_logger?.PolicyFailure($"Request origin {origin} does not have permission to access the resource.");
|
_logger?.OriginNotAllowed(origin);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_logger?.RequestHasOriginHeader();
|
|
||||||
var accessControlRequestMethod = context.Request.Headers[CorsConstants.AccessControlRequestMethod];
|
var accessControlRequestMethod = context.Request.Headers[CorsConstants.AccessControlRequestMethod];
|
||||||
if (StringValues.IsNullOrEmpty(accessControlRequestMethod))
|
if (StringValues.IsNullOrEmpty(accessControlRequestMethod))
|
||||||
{
|
{
|
||||||
|
|
@ -158,18 +158,25 @@ namespace Microsoft.AspNetCore.Cors.Infrastructure
|
||||||
|
|
||||||
if (!found)
|
if (!found)
|
||||||
{
|
{
|
||||||
_logger?.PolicyFailure($"Request method {accessControlRequestMethod} not allowed in CORS policy.");
|
_logger?.PolicyFailure();
|
||||||
return;
|
_logger?.AccessControlMethodNotAllowed(accessControlRequestMethod);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!policy.AllowAnyHeader &&
|
if (!policy.AllowAnyHeader &&
|
||||||
requestHeaders != null &&
|
requestHeaders != null)
|
||||||
!requestHeaders.All(header => CorsConstants.SimpleRequestHeaders.Contains(header, StringComparer.OrdinalIgnoreCase) ||
|
|
||||||
policy.Headers.Contains(header, StringComparer.OrdinalIgnoreCase)))
|
|
||||||
{
|
{
|
||||||
_logger?.PolicyFailure($"One or more request header(s) not allowed in CORS policy.");
|
foreach (var requestHeader in requestHeaders)
|
||||||
return;
|
{
|
||||||
|
if (!CorsConstants.SimpleRequestHeaders.Contains(requestHeader, StringComparer.OrdinalIgnoreCase) &&
|
||||||
|
!policy.Headers.Contains(requestHeader, StringComparer.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
_logger?.PolicyFailure();
|
||||||
|
_logger?.RequestHeaderNotAllowed(requestHeader);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AddOriginToResult(origin, policy, result);
|
AddOriginToResult(origin, policy, result);
|
||||||
|
|
|
||||||
|
|
@ -9,22 +9,25 @@ namespace Microsoft.AspNetCore.Cors.Internal
|
||||||
internal static class CORSLoggerExtensions
|
internal static class CORSLoggerExtensions
|
||||||
{
|
{
|
||||||
private static readonly Action<ILogger, Exception> _isPreflightRequest;
|
private static readonly Action<ILogger, Exception> _isPreflightRequest;
|
||||||
private static readonly Action<ILogger, Exception> _requestHasOriginHeader;
|
private static readonly Action<ILogger, string, Exception> _requestHasOriginHeader;
|
||||||
private static readonly Action<ILogger, Exception> _requestDoesNotHaveOriginHeader;
|
private static readonly Action<ILogger, Exception> _requestDoesNotHaveOriginHeader;
|
||||||
private static readonly Action<ILogger, Exception> _policySuccess;
|
private static readonly Action<ILogger, Exception> _policySuccess;
|
||||||
private static readonly Action<ILogger, string, Exception> _policyFailure;
|
private static readonly Action<ILogger, Exception> _policyFailure;
|
||||||
|
private static readonly Action<ILogger, string, Exception> _originNotAllowed;
|
||||||
|
private static readonly Action<ILogger, string, Exception> _accessControlMethodNotAllowed;
|
||||||
|
private static readonly Action<ILogger, string, Exception> _requestHeaderNotAllowed;
|
||||||
|
|
||||||
static CORSLoggerExtensions()
|
static CORSLoggerExtensions()
|
||||||
{
|
{
|
||||||
_isPreflightRequest = LoggerMessage.Define(
|
_isPreflightRequest = LoggerMessage.Define(
|
||||||
LogLevel.Debug,
|
LogLevel.Debug,
|
||||||
1,
|
1,
|
||||||
"This is a preflight request.");
|
"The request is a preflight request.");
|
||||||
|
|
||||||
_requestHasOriginHeader = LoggerMessage.Define(
|
_requestHasOriginHeader = LoggerMessage.Define<string>(
|
||||||
LogLevel.Debug,
|
LogLevel.Debug,
|
||||||
2,
|
2,
|
||||||
"The request has an origin header.");
|
"The request has an origin header: '{origin}'.");
|
||||||
|
|
||||||
_requestDoesNotHaveOriginHeader = LoggerMessage.Define(
|
_requestDoesNotHaveOriginHeader = LoggerMessage.Define(
|
||||||
LogLevel.Debug,
|
LogLevel.Debug,
|
||||||
|
|
@ -36,10 +39,25 @@ namespace Microsoft.AspNetCore.Cors.Internal
|
||||||
4,
|
4,
|
||||||
"Policy execution successful.");
|
"Policy execution successful.");
|
||||||
|
|
||||||
_policyFailure = LoggerMessage.Define<string>(
|
_policyFailure = LoggerMessage.Define(
|
||||||
LogLevel.Information,
|
LogLevel.Information,
|
||||||
5,
|
5,
|
||||||
"Policy execution failed. {FailureReason}");
|
"Policy execution failed.");
|
||||||
|
|
||||||
|
_originNotAllowed = LoggerMessage.Define<string>(
|
||||||
|
LogLevel.Information,
|
||||||
|
6,
|
||||||
|
"Request origin {origin} does not have permission to access the resource.");
|
||||||
|
|
||||||
|
_accessControlMethodNotAllowed = LoggerMessage.Define<string>(
|
||||||
|
LogLevel.Information,
|
||||||
|
7,
|
||||||
|
"Request method {accessControlRequestMethod} not allowed in CORS policy.");
|
||||||
|
|
||||||
|
_requestHeaderNotAllowed = LoggerMessage.Define<string>(
|
||||||
|
LogLevel.Information,
|
||||||
|
8,
|
||||||
|
"Request header '{requestHeader}' not allowed in CORS policy.");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void IsPreflightRequest(this ILogger logger)
|
public static void IsPreflightRequest(this ILogger logger)
|
||||||
|
|
@ -47,9 +65,9 @@ namespace Microsoft.AspNetCore.Cors.Internal
|
||||||
_isPreflightRequest(logger, null);
|
_isPreflightRequest(logger, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void RequestHasOriginHeader(this ILogger logger)
|
public static void RequestHasOriginHeader(this ILogger logger, string origin)
|
||||||
{
|
{
|
||||||
_requestHasOriginHeader(logger, null);
|
_requestHasOriginHeader(logger, origin, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void RequestDoesNotHaveOriginHeader(this ILogger logger)
|
public static void RequestDoesNotHaveOriginHeader(this ILogger logger)
|
||||||
|
|
@ -62,9 +80,24 @@ namespace Microsoft.AspNetCore.Cors.Internal
|
||||||
_policySuccess(logger, null);
|
_policySuccess(logger, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void PolicyFailure(this ILogger logger, string failureReason)
|
public static void PolicyFailure(this ILogger logger)
|
||||||
{
|
{
|
||||||
_policyFailure(logger, failureReason, null);
|
_policyFailure(logger, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void OriginNotAllowed(this ILogger logger, string origin)
|
||||||
|
{
|
||||||
|
_originNotAllowed(logger, origin, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void AccessControlMethodNotAllowed(this ILogger logger, string accessControlMethod)
|
||||||
|
{
|
||||||
|
_accessControlMethodNotAllowed(logger, accessControlMethod, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void RequestHeaderNotAllowed(this ILogger logger, string requestHeader)
|
||||||
|
{
|
||||||
|
_requestHeaderNotAllowed(logger, requestHeader, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -228,39 +228,41 @@ namespace Microsoft.AspNetCore.Cors.Infrastructure
|
||||||
Assert.Contains("PUT", result.AllowedMethods);
|
Assert.Contains("PUT", result.AllowedMethods);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static TheoryData<string, string, string[], string, string> PreflightRequests_LoggingData
|
public static TheoryData<LogData> PreflightRequests_LoggingData
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return new TheoryData<string, string, string[], string, string>
|
return new TheoryData<LogData>
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
"http://example.com",
|
new LogData {
|
||||||
"PUT",
|
origin = "http://example.com",
|
||||||
null,
|
method = "PUT",
|
||||||
"The request has an origin header.",
|
headers = null,
|
||||||
"Policy execution failed. Request origin http://example.com does not have permission to access the resource."
|
originLogMessage = "The request has an origin header: 'http://example.com'.",
|
||||||
|
policyLogMessage = "Policy execution failed.",
|
||||||
|
failureReason = "Request origin http://example.com does not have permission to access the resource."
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"http://allowed.example.com",
|
new LogData {
|
||||||
"DELETE",
|
origin = "http://allowed.example.com",
|
||||||
null,
|
method = "DELETE",
|
||||||
"The request has an origin header.",
|
headers = null,
|
||||||
"Policy execution failed. Request method DELETE not allowed in CORS policy."
|
originLogMessage = "The request has an origin header: 'http://allowed.example.com'.",
|
||||||
|
policyLogMessage = "Policy execution failed.",
|
||||||
|
failureReason = "Request method DELETE not allowed in CORS policy."
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"http://allowed.example.com",
|
new LogData {
|
||||||
"PUT",
|
origin = "http://allowed.example.com",
|
||||||
new[] { "test" },
|
method = "PUT",
|
||||||
"The request has an origin header.",
|
headers = new[] { "test" },
|
||||||
"Policy execution failed. One or more request header(s) not allowed in CORS policy."
|
originLogMessage = "The request has an origin header: 'http://allowed.example.com'.",
|
||||||
},
|
policyLogMessage = "Policy execution failed.",
|
||||||
{
|
failureReason = "Request header 'test' not allowed in CORS policy."
|
||||||
"http://allowed.example.com",
|
}
|
||||||
"PUT",
|
|
||||||
null,
|
|
||||||
"The request has an origin header.",
|
|
||||||
"Policy execution successful."
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -268,13 +270,13 @@ namespace Microsoft.AspNetCore.Cors.Infrastructure
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[MemberData(nameof(PreflightRequests_LoggingData))]
|
[MemberData(nameof(PreflightRequests_LoggingData))]
|
||||||
public void EvaluatePolicy_LoggingForPreflightRequests_HasOriginHeader(string origin, string method, string[] headers, string originLogMessage, string policyLogMessage)
|
public void EvaluatePolicy_LoggingForPreflightRequests_HasOriginHeader_PolicyFailed(LogData logData)
|
||||||
{
|
{
|
||||||
var sink = new TestSink();
|
var sink = new TestSink();
|
||||||
var loggerFactory = new TestLoggerFactory(sink, enabled: true);
|
var loggerFactory = new TestLoggerFactory(sink, enabled: true);
|
||||||
|
|
||||||
var corsService = new CorsService(new TestCorsOptions(), loggerFactory);
|
var corsService = new CorsService(new TestCorsOptions(), loggerFactory);
|
||||||
var requestContext = GetHttpContext(method: "OPTIONS", origin: origin, accessControlRequestMethod: method, accessControlRequestHeaders: headers);
|
var requestContext = GetHttpContext(method: "OPTIONS", origin: logData.origin, accessControlRequestMethod: logData.method, accessControlRequestHeaders: logData.headers);
|
||||||
var policy = new CorsPolicy();
|
var policy = new CorsPolicy();
|
||||||
policy.Origins.Add("http://allowed.example.com");
|
policy.Origins.Add("http://allowed.example.com");
|
||||||
policy.Methods.Add("PUT");
|
policy.Methods.Add("PUT");
|
||||||
|
|
@ -282,9 +284,30 @@ namespace Microsoft.AspNetCore.Cors.Infrastructure
|
||||||
// Act
|
// Act
|
||||||
var result = corsService.EvaluatePolicy(requestContext, policy);
|
var result = corsService.EvaluatePolicy(requestContext, policy);
|
||||||
|
|
||||||
Assert.Equal("This is a preflight request.", sink.Writes[0].State.ToString());
|
Assert.Equal("The request is a preflight request.", sink.Writes[0].State.ToString());
|
||||||
Assert.Equal(originLogMessage, sink.Writes[1].State.ToString());
|
Assert.Equal(logData.originLogMessage, sink.Writes[1].State.ToString());
|
||||||
Assert.Equal(policyLogMessage, sink.Writes[2].State.ToString());
|
Assert.Equal(logData.policyLogMessage, sink.Writes[2].State.ToString());
|
||||||
|
Assert.Equal(logData.failureReason, sink.Writes[3].State.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void EvaluatePolicy_LoggingForPreflightRequests_HasOriginHeader_PolicySucceeded()
|
||||||
|
{
|
||||||
|
var sink = new TestSink();
|
||||||
|
var loggerFactory = new TestLoggerFactory(sink, enabled: true);
|
||||||
|
|
||||||
|
var corsService = new CorsService(new TestCorsOptions(), loggerFactory);
|
||||||
|
var requestContext = GetHttpContext(method: "OPTIONS", origin: "http://allowed.example.com", accessControlRequestMethod: "PUT");
|
||||||
|
var policy = new CorsPolicy();
|
||||||
|
policy.Origins.Add("http://allowed.example.com");
|
||||||
|
policy.Methods.Add("PUT");
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = corsService.EvaluatePolicy(requestContext, policy);
|
||||||
|
|
||||||
|
Assert.Equal("The request is a preflight request.", sink.Writes[0].State.ToString());
|
||||||
|
Assert.Equal("The request has an origin header: 'http://allowed.example.com'.", sink.Writes[1].State.ToString());
|
||||||
|
Assert.Equal("Policy execution successful.", sink.Writes[2].State.ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -302,47 +325,45 @@ namespace Microsoft.AspNetCore.Cors.Infrastructure
|
||||||
// Act
|
// Act
|
||||||
var result = corsService.EvaluatePolicy(requestContext, policy);
|
var result = corsService.EvaluatePolicy(requestContext, policy);
|
||||||
|
|
||||||
Assert.Equal("This is a preflight request.", sink.Writes[0].State.ToString());
|
Assert.Equal("The request is a preflight request.", sink.Writes[0].State.ToString());
|
||||||
Assert.Equal("The request does not have an origin header.", sink.Writes[1].State.ToString());
|
Assert.Equal("The request does not have an origin header.", sink.Writes[1].State.ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static TheoryData<string, string, string> NonPreflightRequests_LoggingData
|
[Fact]
|
||||||
{
|
public void EvaluatePolicy_LoggingForNonPreflightRequests_HasOriginHeader_PolicyFailed()
|
||||||
get
|
|
||||||
{
|
|
||||||
return new TheoryData<string, string, string>
|
|
||||||
{
|
|
||||||
{
|
|
||||||
"http://example.com",
|
|
||||||
"The request has an origin header.",
|
|
||||||
"Policy execution failed. Request origin http://example.com does not have permission to access the resource."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"http://allowed.example.com",
|
|
||||||
"The request has an origin header.",
|
|
||||||
"Policy execution successful."
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory]
|
|
||||||
[MemberData(nameof(NonPreflightRequests_LoggingData))]
|
|
||||||
public void EvaluatePolicy_LoggingForNonPreflightRequests_HasOriginHeader(string origin, string originlogMessage, string policyLogMessage)
|
|
||||||
{
|
{
|
||||||
var sink = new TestSink();
|
var sink = new TestSink();
|
||||||
var loggerFactory = new TestLoggerFactory(sink, enabled: true);
|
var loggerFactory = new TestLoggerFactory(sink, enabled: true);
|
||||||
|
|
||||||
var corsService = new CorsService(new TestCorsOptions(), loggerFactory);
|
var corsService = new CorsService(new TestCorsOptions(), loggerFactory);
|
||||||
var requestContext = GetHttpContext(origin: origin);
|
var requestContext = GetHttpContext(origin: "http://example.com");
|
||||||
var policy = new CorsPolicy();
|
var policy = new CorsPolicy();
|
||||||
policy.Origins.Add("http://allowed.example.com");
|
policy.Origins.Add("http://allowed.example.com");
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = corsService.EvaluatePolicy(requestContext, policy);
|
var result = corsService.EvaluatePolicy(requestContext, policy);
|
||||||
|
|
||||||
Assert.Equal(originlogMessage, sink.Writes[0].State.ToString());
|
Assert.Equal("The request has an origin header: 'http://example.com'.", sink.Writes[0].State.ToString());
|
||||||
Assert.Equal(policyLogMessage, sink.Writes[1].State.ToString());
|
Assert.Equal("Policy execution failed.", sink.Writes[1].State.ToString());
|
||||||
|
Assert.Equal("Request origin http://example.com does not have permission to access the resource.", sink.Writes[2].State.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void EvaluatePolicy_LoggingForNonPreflightRequests_HasOriginHeader_PolicySucceeded()
|
||||||
|
{
|
||||||
|
var sink = new TestSink();
|
||||||
|
var loggerFactory = new TestLoggerFactory(sink, enabled: true);
|
||||||
|
|
||||||
|
var corsService = new CorsService(new TestCorsOptions(), loggerFactory);
|
||||||
|
var requestContext = GetHttpContext(origin: "http://allowed.example.com");
|
||||||
|
var policy = new CorsPolicy();
|
||||||
|
policy.Origins.Add("http://allowed.example.com");
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = corsService.EvaluatePolicy(requestContext, policy);
|
||||||
|
|
||||||
|
Assert.Equal("The request has an origin header: 'http://allowed.example.com'.", sink.Writes[0].State.ToString());
|
||||||
|
Assert.Equal("Policy execution successful.", sink.Writes[1].State.ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -1049,5 +1070,15 @@ namespace Microsoft.AspNetCore.Cors.Infrastructure
|
||||||
|
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public struct LogData
|
||||||
|
{
|
||||||
|
public string origin { get; set; }
|
||||||
|
public string method { get; set; }
|
||||||
|
public string[] headers { get; set; }
|
||||||
|
public string originLogMessage { get; set; }
|
||||||
|
public string policyLogMessage { get; set; }
|
||||||
|
public string failureReason { get; set; }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Loading…
Reference in New Issue