PR comments incorporated

This commit is contained in:
Jass Bagga 2016-11-17 17:36:18 -08:00
parent 517dc3c860
commit 5dcf47ef12
20 changed files with 199 additions and 166 deletions

View File

@ -1,36 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
namespace SampleDestination
{
public class Startup
{
// This method gets called by the runtime. Use this method to add services to the container.
// For more information on how to configure your application, visit http://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
{
services.AddCors();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseCors(policy => policy.WithOrigins("http://origin.sample.com:8080").WithMethods("GET"));
app.UseMiddleware<StatusMiddleware>();
}
}
}

View File

@ -1,35 +0,0 @@
using Microsoft.AspNetCore.Http;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace SampleDestination
{
public class StatusMiddleware
{
/// <summary>
/// Instantiates a new <see cref="StatusMiddleware"/>.
/// </summary>
/// <param name="next">The next middleware in the pipeline.</param>
public StatusMiddleware(RequestDelegate next)
{
}
/// <summary>
/// Writes the status of the request sent in response. Does not invoke later middleware in the pipeline.
/// </summary>
/// <param name="context">The <see cref="HttpContext"/> of the current request.</param>
/// <returns>A <see cref="Task"/> that completes when writing to the response is done.</returns>
public Task Invoke(HttpContext context)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
return context.Response.WriteAsync(context.Response.StatusCode.ToString());
}
}
}

View File

@ -1,3 +0,0 @@
{
"server.urls": "http://destination.sample.com:80"
}

View File

@ -1,35 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using System.IO;
namespace SampleOrigin
{
public class Startup
{
// This method gets called by the runtime. Use this method to add services to the container.
// For more information on how to configure your application, visit http://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
{
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseDefaultFiles();
app.UseStaticFiles();
}
}
}

View File

@ -1,3 +0,0 @@
{
"server.urls": "http://origin.sample.com:8080"
}

28
samples/README.md Normal file
View File

@ -0,0 +1,28 @@
CORS Sample
===
This sample consists of a request origin (SampleOrigin) and a request destination (SampleDestination).
Both have different domain names, to simulate a CORS request.
Modify Hosts File
Windows:
Run a text editor (e.g. Notepad) as an Administrator. Open the hosts file on the path: "C:\Windows\System32\drivers\etc\hosts".
Linux:
On a Terminal window, type "sudo nano /etc/hosts" and enter your admin password when prompted.
In the hosts file, add the following to the bottom of the file:
127.0.0.1 destination.example.com
127.0.0.1 origin.example.com
Save the file and close it. Then clear your browser history.
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
*Repeat the above step in the SampleOrigin directory.
*Open a browser window and go to http://origin.example.com
*Click the button to see CORS in action.
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
http://origin.example.com:8080. Using the dropdown near the Start button, choose SampleOrigin before pressing Start to ensure that it uses Kestrel
and not IIS Express.

View File

@ -1,10 +1,8 @@
using System; // Copyright (c) .NET Foundation. All rights reserved.
using System.Collections.Generic; // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.IO; using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
namespace SampleDestination namespace SampleDestination
{ {
@ -12,14 +10,9 @@ namespace SampleDestination
{ {
public static void Main(string[] args) public static void Main(string[] args)
{ {
var config = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("hosting.json", optional: true)
.Build();
var host = new WebHostBuilder() var host = new WebHostBuilder()
.UseKestrel() .UseKestrel()
.UseConfiguration(config) .UseUrls("http://*:5000")
.UseContentRoot(Directory.GetCurrentDirectory()) .UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration() .UseIISIntegration()
.UseStartup<Startup>() .UseStartup<Startup>()

View File

@ -0,0 +1,29 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
namespace SampleDestination
{
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddCors();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole();
app.UseCors(policy => policy.WithOrigins("http://origin.example.com:8080"));
app.Run(async context =>
{
await context.Response.WriteAsync("Status code of your request: " + context.Response.StatusCode.ToString());
});
}
}
}

View File

@ -4,13 +4,10 @@
"version": "1.1.0-*", "version": "1.1.0-*",
"type": "platform" "type": "platform"
}, },
"Microsoft.AspNetCore.Diagnostics": "1.2.0-*",
"Microsoft.AspNetCore.Server.IISIntegration": "1.2.0-*", "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.StaticFiles": "1.2.0-*", "Microsoft.AspNetCore.Cors": "1.2.0-*"
"Microsoft.Extensions.Configuration.FileExtensions": "1.2.0-*",
"Microsoft.Extensions.Configuration.Json": "1.2.0-*"
}, },
"tools": { "tools": {

View File

@ -1,10 +1,8 @@
using System; // Copyright (c) .NET Foundation. All rights reserved.
using System.Collections.Generic; // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.IO; using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
namespace SampleOrigin namespace SampleOrigin
{ {
@ -12,14 +10,9 @@ namespace SampleOrigin
{ {
public static void Main(string[] args) public static void Main(string[] args)
{ {
var config = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("hosting.json", optional: true)
.Build();
var host = new WebHostBuilder() var host = new WebHostBuilder()
.UseKestrel() .UseKestrel()
.UseConfiguration(config) .UseUrls("http://*:8080")
.UseContentRoot(Directory.GetCurrentDirectory()) .UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration() .UseIISIntegration()
.UseStartup<Startup>() .UseStartup<Startup>()

View File

@ -0,0 +1,36 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
namespace SampleOrigin
{
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole();
app.Run( context =>
{
var fileInfoProvider = env.WebRootFileProvider;
var fileInfo = fileInfoProvider.GetFileInfo("/Index.html");
context.Response.Headers.Add("Content-Type", "text/html; charset=utf-8");
return context.Response.SendFileAsync(fileInfo);
});
app.Run(async context =>
{
await context.Response.WriteAsync("Status code of your request: " + context.Response.StatusCode.ToString());
});
}
}
}

View File

@ -4,13 +4,9 @@
"version": "1.1.0-*", "version": "1.1.0-*",
"type": "platform" "type": "platform"
}, },
"Microsoft.AspNetCore.Diagnostics": "1.2.0-*",
"Microsoft.AspNetCore.Server.IISIntegration": "1.2.0-*", "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.Extensions.Configuration.FileExtensions": "1.2.0-*",
"Microsoft.Extensions.Configuration.Json": "1.2.0-preview1-*"
}, },
"tools": { "tools": {

View File

@ -14,7 +14,7 @@
function makeCORSRequest(method) { function makeCORSRequest(method) {
// Destination server with CORS enabled. // Destination server with CORS enabled.
var url = 'http://destination.sample.com/api'; var url = 'http://destination.example.com:5000/api';
var request = createCORSRequest(method , url); var request = createCORSRequest(method , url);
if (!request) { if (!request) {

View File

@ -99,13 +99,14 @@ namespace Microsoft.AspNetCore.Cors.Infrastructure
var origin = context.Request.Headers[CorsConstants.Origin]; var origin = context.Request.Headers[CorsConstants.Origin];
if (StringValues.IsNullOrEmpty(origin)) if (StringValues.IsNullOrEmpty(origin))
{ {
_logger?.RequestDoesNotHaveOriginHeader();
return; return;
} }
if (!policy.AllowAnyOrigin && !policy.Origins.Contains(origin)) if (!policy.AllowAnyOrigin && !policy.Origins.Contains(origin))
{ {
_logger?.RequestHasOriginHeader(); _logger?.RequestHasOriginHeader();
_logger.PolicyFailure($"Request origin {origin} does not have permission to access the resource."); _logger?.PolicyFailure($"Request origin {origin} does not have permission to access the resource.");
return; return;
} }
@ -121,13 +122,14 @@ namespace Microsoft.AspNetCore.Cors.Infrastructure
var origin = context.Request.Headers[CorsConstants.Origin]; var origin = context.Request.Headers[CorsConstants.Origin];
if (StringValues.IsNullOrEmpty(origin)) if (StringValues.IsNullOrEmpty(origin))
{ {
_logger?.RequestDoesNotHaveOriginHeader();
return; return;
} }
if (!policy.AllowAnyOrigin && !policy.Origins.Contains(origin)) if (!policy.AllowAnyOrigin && !policy.Origins.Contains(origin))
{ {
_logger?.RequestHasOriginHeader(); _logger?.RequestHasOriginHeader();
_logger.PolicyFailure($"Request origin {origin} does not have permission to access the resource."); _logger?.PolicyFailure($"Request origin {origin} does not have permission to access the resource.");
return; return;
} }

View File

@ -10,6 +10,7 @@ namespace Microsoft.AspNetCore.Cors.Internal
{ {
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, Exception> _requestHasOriginHeader;
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, string, Exception> _policyFailure;
@ -18,21 +19,26 @@ namespace Microsoft.AspNetCore.Cors.Internal
_isPreflightRequest = LoggerMessage.Define( _isPreflightRequest = LoggerMessage.Define(
LogLevel.Debug, LogLevel.Debug,
1, 1,
"The request is preflight."); "This is a preflight request.");
_requestHasOriginHeader = LoggerMessage.Define( _requestHasOriginHeader = LoggerMessage.Define(
LogLevel.Debug, LogLevel.Debug,
2, 2,
"The request has an origin header."); "The request has an origin header.");
_requestDoesNotHaveOriginHeader = LoggerMessage.Define(
LogLevel.Debug,
3,
"The request does not have an origin header.");
_policySuccess = LoggerMessage.Define( _policySuccess = LoggerMessage.Define(
LogLevel.Information, LogLevel.Information,
3, 4,
"Policy execution successful."); "Policy execution successful.");
_policyFailure = LoggerMessage.Define<string>( _policyFailure = LoggerMessage.Define<string>(
LogLevel.Information, LogLevel.Information,
3, 5,
"Policy execution failed. {FailureReason}"); "Policy execution failed. {FailureReason}");
} }
@ -46,6 +52,11 @@ namespace Microsoft.AspNetCore.Cors.Internal
_requestHasOriginHeader(logger, null); _requestHasOriginHeader(logger, null);
} }
public static void RequestDoesNotHaveOriginHeader(this ILogger logger)
{
_requestDoesNotHaveOriginHeader(logger, null);
}
public static void PolicySuccess(this ILogger logger) public static void PolicySuccess(this ILogger logger)
{ {
_policySuccess(logger, null); _policySuccess(logger, null);

View File

@ -228,34 +228,38 @@ namespace Microsoft.AspNetCore.Cors.Infrastructure
Assert.Contains("PUT", result.AllowedMethods); Assert.Contains("PUT", result.AllowedMethods);
} }
public static TheoryData<string, string, string[], string> LoggingData public static TheoryData<string, string, string[], string, string> PreflightRequests_LoggingData
{ {
get get
{ {
return new TheoryData<string, string, string[], string> return new TheoryData<string, string, string[], string, string>
{ {
{ {
"http://example.com", "http://example.com",
"PUT", "PUT",
null, null,
"The request has an origin header.",
"Policy execution failed. Request origin http://example.com does not have permission to access the resource." "Policy execution failed. Request origin http://example.com does not have permission to access the resource."
}, },
{ {
"http://allowedexample.com", "http://allowed.example.com",
"DELETE", "DELETE",
null, null,
"The request has an origin header.",
"Policy execution failed. Request method DELETE not allowed in CORS policy." "Policy execution failed. Request method DELETE not allowed in CORS policy."
}, },
{ {
"http://allowedexample.com", "http://allowed.example.com",
"PUT", "PUT",
new[] { "test" }, new[] { "test" },
"The request has an origin header.",
"Policy execution failed. One or more request header(s) not allowed in CORS policy." "Policy execution failed. One or more request header(s) not allowed in CORS policy."
}, },
{ {
"http://allowedexample.com", "http://allowed.example.com",
"PUT", "PUT",
null, null,
"The request has an origin header.",
"Policy execution successful." "Policy execution successful."
}, },
}; };
@ -263,8 +267,8 @@ namespace Microsoft.AspNetCore.Cors.Infrastructure
} }
[Theory] [Theory]
[MemberData(nameof(LoggingData))] [MemberData(nameof(PreflightRequests_LoggingData))]
public void EvaluatePolicy_LoggingForPreflightRequests(string origin, string method, string[] headers, string logMessage) public void EvaluatePolicy_LoggingForPreflightRequests_HasOriginHeader(string origin, string method, string[] headers, 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);
@ -272,21 +276,59 @@ namespace Microsoft.AspNetCore.Cors.Infrastructure
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: origin, accessControlRequestMethod: method, accessControlRequestHeaders: headers);
var policy = new CorsPolicy(); var policy = new CorsPolicy();
policy.Origins.Add("http://allowedexample.com"); policy.Origins.Add("http://allowed.example.com");
policy.Methods.Add("PUT"); policy.Methods.Add("PUT");
// Act // Act
var result = corsService.EvaluatePolicy(requestContext, policy); var result = corsService.EvaluatePolicy(requestContext, policy);
Assert.Equal("The request is preflight.", sink.Writes[0].State.ToString()); Assert.Equal("This is a preflight request.", sink.Writes[0].State.ToString());
Assert.Equal("The request has an origin header.", sink.Writes[1].State.ToString()); Assert.Equal(originLogMessage, sink.Writes[1].State.ToString());
Assert.Equal(logMessage, sink.Writes[2].State.ToString()); Assert.Equal(policyLogMessage, sink.Writes[2].State.ToString());
}
[Fact]
public void EvaluatePolicy_LoggingForPreflightRequests_DoesNotHaveOriginHeader()
{
var sink = new TestSink();
var loggerFactory = new TestLoggerFactory(sink, enabled: true);
var corsService = new CorsService(new TestCorsOptions(), loggerFactory);
var requestContext = GetHttpContext(method: "OPTIONS", origin: null, 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("This is a preflight request.", sink.Writes[0].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
{
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] [Theory]
[InlineData("http://allowedexample.com", "Policy execution successful.")] [MemberData(nameof(NonPreflightRequests_LoggingData))]
[InlineData("http://example.com", "Policy execution failed. Request origin http://example.com does not have permission to access the resource.")] public void EvaluatePolicy_LoggingForNonPreflightRequests_HasOriginHeader(string origin, string originlogMessage, string policyLogMessage)
public void EvaluatePolicy_LoggingForRequests(string origin, string logMessage)
{ {
var sink = new TestSink(); var sink = new TestSink();
var loggerFactory = new TestLoggerFactory(sink, enabled: true); var loggerFactory = new TestLoggerFactory(sink, enabled: true);
@ -294,13 +336,31 @@ namespace Microsoft.AspNetCore.Cors.Infrastructure
var corsService = new CorsService(new TestCorsOptions(), loggerFactory); var corsService = new CorsService(new TestCorsOptions(), loggerFactory);
var requestContext = GetHttpContext(origin: origin); var requestContext = GetHttpContext(origin: origin);
var policy = new CorsPolicy(); var policy = new CorsPolicy();
policy.Origins.Add("http://allowedexample.com"); policy.Origins.Add("http://allowed.example.com");
// Act // Act
var result = corsService.EvaluatePolicy(requestContext, policy); var result = corsService.EvaluatePolicy(requestContext, policy);
Assert.Equal("The request has an origin header.", sink.Writes[0].State.ToString()); Assert.Equal(originlogMessage, sink.Writes[0].State.ToString());
Assert.Equal(logMessage, sink.Writes[1].State.ToString()); Assert.Equal(policyLogMessage, sink.Writes[1].State.ToString());
}
[Fact]
public void EvaluatePolicy_LoggingForNonPreflightRequests_DoesNotHaveOriginHeader()
{
var sink = new TestSink();
var loggerFactory = new TestLoggerFactory(sink, enabled: true);
var corsService = new CorsService(new TestCorsOptions(), loggerFactory);
var requestContext = GetHttpContext(origin: null);
var policy = new CorsPolicy();
policy.Origins.Add("http://allowed.example.com");
// Act
var result = corsService.EvaluatePolicy(requestContext, policy);
var logMessage = Assert.Single(sink.Writes);
Assert.Equal("The request does not have an origin header.", logMessage.State.ToString());
} }
[Theory] [Theory]