Adds Https Redirection and Hsts Middlewares (#264)
This commit is contained in:
parent
83d1396330
commit
758f9fcea5
|
|
@ -57,6 +57,12 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
|
|||
version.xml = version.xml
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.HttpsPolicy", "src\Microsoft.AspNetCore.HttpsPolicy\Microsoft.AspNetCore.HttpsPolicy.csproj", "{4D39C29B-4EC8-497C-B411-922DA494D71B}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HttpsPolicySample", "samples\HttpsPolicySample\HttpsPolicySample.csproj", "{AC424AEE-4883-49C6-945F-2FC916B8CA1C}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.HttpsPolicy.Tests", "test\Microsoft.AspNetCore.HttpsEnforcement.Tests\Microsoft.AspNetCore.HttpsPolicy.Tests.csproj", "{1C67B0F1-6E70-449E-A2F1-98B9D5C576CE}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
|
|
@ -111,6 +117,18 @@ Global
|
|||
{B2A3CE38-51B2-4486-982C-98C380AF140E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{B2A3CE38-51B2-4486-982C-98C380AF140E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{B2A3CE38-51B2-4486-982C-98C380AF140E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{4D39C29B-4EC8-497C-B411-922DA494D71B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{4D39C29B-4EC8-497C-B411-922DA494D71B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{4D39C29B-4EC8-497C-B411-922DA494D71B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{4D39C29B-4EC8-497C-B411-922DA494D71B}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{AC424AEE-4883-49C6-945F-2FC916B8CA1C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{AC424AEE-4883-49C6-945F-2FC916B8CA1C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{AC424AEE-4883-49C6-945F-2FC916B8CA1C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{AC424AEE-4883-49C6-945F-2FC916B8CA1C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{1C67B0F1-6E70-449E-A2F1-98B9D5C576CE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{1C67B0F1-6E70-449E-A2F1-98B9D5C576CE}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{1C67B0F1-6E70-449E-A2F1-98B9D5C576CE}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{1C67B0F1-6E70-449E-A2F1-98B9D5C576CE}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
|
@ -128,6 +146,9 @@ Global
|
|||
{45308A9D-F4C6-46A8-A24F-E73D995CC223} = {A5076D28-FA7E-4606-9410-FEDD0D603527}
|
||||
{3360A5D1-70C0-49EE-9051-04A6A6B836DC} = {8437B0F3-3894-4828-A945-A9187F37631D}
|
||||
{B2A3CE38-51B2-4486-982C-98C380AF140E} = {9587FE9F-5A17-42C4-8021-E87F59CECB98}
|
||||
{4D39C29B-4EC8-497C-B411-922DA494D71B} = {A5076D28-FA7E-4606-9410-FEDD0D603527}
|
||||
{AC424AEE-4883-49C6-945F-2FC916B8CA1C} = {9587FE9F-5A17-42C4-8021-E87F59CECB98}
|
||||
{1C67B0F1-6E70-449E-A2F1-98B9D5C576CE} = {8437B0F3-3894-4828-A945-A9187F37631D}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {4518E9CE-3680-4E05-9259-B64EA7807158}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,17 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net461;netcoreapp2.0</TargetFrameworks>
|
||||
<TargetFrameworks Condition=" '$(OS)' != 'Windows_NT' ">netcoreapp2.0</TargetFrameworks>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel.Https" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.HttpsPolicy\Microsoft.AspNetCore.HttpsPolicy.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"iisSettings": {
|
||||
"windowsAuthentication": false,
|
||||
"anonymousAuthentication": true,
|
||||
"iisExpress": {
|
||||
"applicationUrl": "http://localhost:31894/",
|
||||
"sslPort": 0
|
||||
}
|
||||
},
|
||||
"profiles": {
|
||||
"IIS Express": {
|
||||
"commandName": "IISExpress",
|
||||
"launchBrowser": true,
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
},
|
||||
"HttpsSample": {
|
||||
"commandName": "Project",
|
||||
"launchBrowser": true,
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
},
|
||||
"applicationUrl": "http://localhost:31895/"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.HttpsPolicy;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace HttpsSample
|
||||
{
|
||||
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 https://go.microsoft.com/fwlink/?LinkID=398940
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
services.AddHttpsRedirection(options =>
|
||||
{
|
||||
options.RedirectStatusCode = StatusCodes.Status301MovedPermanently;
|
||||
options.TlsPort = 5001;
|
||||
});
|
||||
|
||||
services.AddHsts(options =>
|
||||
{
|
||||
options.MaxAge = TimeSpan.FromDays(30);
|
||||
options.Preload = true;
|
||||
options.IncludeSubDomains = true;
|
||||
});
|
||||
}
|
||||
|
||||
public void Configure(IApplicationBuilder app, IHostingEnvironment environment)
|
||||
{
|
||||
if (!environment.IsDevelopment())
|
||||
{
|
||||
app.UseHsts();
|
||||
}
|
||||
app.UseHttpsRedirection();
|
||||
|
||||
app.Run(async context =>
|
||||
{
|
||||
await context.Response.WriteAsync("Hello world!");
|
||||
});
|
||||
}
|
||||
|
||||
// Entry point for the application.
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
var host = new WebHostBuilder()
|
||||
.UseKestrel(
|
||||
options =>
|
||||
{
|
||||
options.Listen(new IPEndPoint(IPAddress.Loopback, 5001), listenOptions =>
|
||||
{
|
||||
listenOptions.UseHttps("testCert.pfx", "testPassword");
|
||||
});
|
||||
options.Listen(new IPEndPoint(IPAddress.Loopback, 5000), listenOptions =>
|
||||
{
|
||||
});
|
||||
})
|
||||
.UseContentRoot(Directory.GetCurrentDirectory()) // for the cert file
|
||||
.UseStartup<Startup>()
|
||||
.Build();
|
||||
|
||||
host.Run();
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
|
@ -0,0 +1,30 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.HttpsPolicy;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace Microsoft.AspNetCore.Builder
|
||||
{
|
||||
/// <summary>
|
||||
/// Extension methods for the HSTS middleware.
|
||||
/// </summary>
|
||||
public static class HstsBuilderExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Adds middleware for using HSTS, which adds the Strict-Transport-Security header.
|
||||
/// </summary>
|
||||
/// <param name="app">The <see cref="IApplicationBuilder"/> instance this method extends.</param>
|
||||
public static IApplicationBuilder UseHsts(this IApplicationBuilder app)
|
||||
{
|
||||
if (app == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(app));
|
||||
}
|
||||
|
||||
return app.UseMiddleware<HstsMiddleware>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
using Microsoft.Net.Http.Headers;
|
||||
|
||||
namespace Microsoft.AspNetCore.HttpsPolicy
|
||||
{
|
||||
/// <summary>
|
||||
/// Enables HTTP Strict Transport Security (HSTS)
|
||||
/// See https://tools.ietf.org/html/rfc6797.
|
||||
/// </summary>
|
||||
public class HstsMiddleware
|
||||
{
|
||||
private const string IncludeSubDomains = "; includeSubDomains";
|
||||
private const string Preload = "; preload";
|
||||
|
||||
private readonly RequestDelegate _next;
|
||||
private readonly StringValues _strictTransportSecurityValue;
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the HSTS middleware.
|
||||
/// </summary>
|
||||
/// <param name="next"></param>
|
||||
/// <param name="options"></param>
|
||||
public HstsMiddleware(RequestDelegate next, IOptions<HstsOptions> options)
|
||||
{
|
||||
if (next == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(next));
|
||||
}
|
||||
|
||||
if (options == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(options));
|
||||
}
|
||||
|
||||
_next = next;
|
||||
|
||||
var hstsOptions = options.Value;
|
||||
var maxAge = Convert.ToInt64(Math.Floor(hstsOptions.MaxAge.TotalSeconds))
|
||||
.ToString(CultureInfo.InvariantCulture);
|
||||
var includeSubdomains = hstsOptions.IncludeSubDomains ? IncludeSubDomains : StringSegment.Empty;
|
||||
var preload = hstsOptions.Preload ? Preload : StringSegment.Empty;
|
||||
_strictTransportSecurityValue = new StringValues($"max-age={maxAge}{includeSubdomains}{preload}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invoke the middleware.
|
||||
/// </summary>
|
||||
/// <param name="context">The <see cref="HttpContext"/>.</param>
|
||||
/// <returns></returns>
|
||||
public Task Invoke(HttpContext context)
|
||||
{
|
||||
if (context.Request.IsHttps)
|
||||
{
|
||||
context.Response.Headers[HeaderNames.StrictTransportSecurity] = _strictTransportSecurityValue;
|
||||
}
|
||||
|
||||
return _next(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
|
||||
namespace Microsoft.AspNetCore.HttpsPolicy
|
||||
{
|
||||
/// <summary>
|
||||
/// Options for the Hsts Middleware
|
||||
/// </summary>
|
||||
public class HstsOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Sets the max-age parameter of the Strict-Transport-Security header.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Max-age is required; defaults to 30 days.
|
||||
/// See: https://tools.ietf.org/html/rfc6797#section-6.1.1
|
||||
/// </remarks>
|
||||
public TimeSpan MaxAge { get; set; } = TimeSpan.FromDays(30);
|
||||
|
||||
/// <summary>
|
||||
/// Enables includeSubDomain parameter of the Strict-Transport-Security header.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// See: https://tools.ietf.org/html/rfc6797#section-6.1.2
|
||||
/// </remarks>
|
||||
public bool IncludeSubDomains { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Sets the preload parameter of the Strict-Transport-Security header.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Preload is not part of the RFC specification, but is supported by web browsers
|
||||
/// to preload HSTS sites on fresh install. See https://hstspreload.org/.
|
||||
/// </remarks>
|
||||
public bool Preload { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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 System;
|
||||
using Microsoft.AspNetCore.HttpsPolicy;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Microsoft.AspNetCore.Builder
|
||||
{
|
||||
/// <summary>
|
||||
/// Extension methods for the HSTS middleware.
|
||||
/// </summary>
|
||||
public static class HstsServicesExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Adds HSTS services.
|
||||
/// </summary>
|
||||
/// <param name="services">The <see cref="IServiceCollection"/> for adding services.</param>
|
||||
/// <param name="configureOptions">A delegate to configure the <see cref="HstsOptions"/>.</param>
|
||||
/// <returns></returns>
|
||||
public static IServiceCollection AddHsts(this IServiceCollection services, Action<HstsOptions> configureOptions)
|
||||
{
|
||||
if (services == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(services));
|
||||
}
|
||||
if (configureOptions == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(configureOptions));
|
||||
}
|
||||
|
||||
services.Configure(configureOptions);
|
||||
return services;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using Microsoft.AspNetCore.HttpsPolicy;
|
||||
using Microsoft.AspNetCore.Rewrite;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace Microsoft.AspNetCore.Builder
|
||||
{
|
||||
/// <summary>
|
||||
/// Extension methods for the HttpsRedirection middleware.
|
||||
/// </summary>
|
||||
public static class HttpsPolicyBuilderExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Adds middleware for redirecting HTTP Requests to HTTPS.
|
||||
/// </summary>
|
||||
/// <param name="app">The <see cref="IApplicationBuilder"/> instance this method extends.</param>
|
||||
/// <returns>The <see cref="IApplicationBuilder"/> for HttpsRedirection.</returns>
|
||||
/// <remarks>
|
||||
/// HTTPS Enforcement interanlly uses the UrlRewrite middleware to redirect HTTP requests to HTTPS.
|
||||
/// </remarks>
|
||||
public static IApplicationBuilder UseHttpsRedirection(this IApplicationBuilder app)
|
||||
{
|
||||
if (app == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(app));
|
||||
}
|
||||
|
||||
var options = app.ApplicationServices.GetRequiredService<IOptions<HttpsRedirectionOptions>>().Value;
|
||||
|
||||
var rewriteOptions = new RewriteOptions();
|
||||
rewriteOptions.AddRedirectToHttps(
|
||||
options.RedirectStatusCode,
|
||||
options.TlsPort);
|
||||
|
||||
app.UseRewriter(rewriteOptions);
|
||||
|
||||
return app;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
// 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.Http;
|
||||
|
||||
namespace Microsoft.AspNetCore.HttpsPolicy
|
||||
{
|
||||
/// <summary>
|
||||
/// Options for the HttpsRedirection middleware
|
||||
/// </summary>
|
||||
public class HttpsRedirectionOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// The status code to redirect the response to.
|
||||
/// </summary>
|
||||
public int RedirectStatusCode { get; set; } = StatusCodes.Status301MovedPermanently;
|
||||
|
||||
/// <summary>
|
||||
/// The TLS port to be added to the redirected URL.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Defaults to 443 if not provided.
|
||||
/// </remarks>
|
||||
public int? TlsPort { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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 System;
|
||||
using Microsoft.AspNetCore.HttpsPolicy;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Microsoft.AspNetCore.Builder
|
||||
{
|
||||
/// <summary>
|
||||
/// Extension methods for the HttpsRedirection middleware.
|
||||
/// </summary>
|
||||
public static class HttpsRedirectionServicesExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Adds HTTPS redirection services.
|
||||
/// </summary>
|
||||
/// <param name="services">The <see cref="IServiceCollection"/> for adding services.</param>
|
||||
/// <param name="configureOptions">A delegate to configure the <see cref="HttpsRedirectionOptions"/>.</param>
|
||||
/// <returns></returns>
|
||||
public static IServiceCollection AddHttpsRedirection(this IServiceCollection services, Action<HttpsRedirectionOptions> configureOptions)
|
||||
{
|
||||
if (services == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(services));
|
||||
}
|
||||
if (configureOptions == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(configureOptions));
|
||||
}
|
||||
|
||||
services.Configure(configureOptions);
|
||||
return services;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<Description>
|
||||
ASP.NET Core basic middleware for supporting HTTPS Redirection and HTTP Strict-Transport-Security.
|
||||
</Description>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<NoWarn>$(NoWarn);CS1591</NoWarn>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
<PackageTags>aspnetcore;https;hsts</PackageTags>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.Rewrite\Microsoft.AspNetCore.Rewrite.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
|
@ -0,0 +1,128 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.TestHost;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Net.Http.Headers;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.HttpsPolicy.Tests
|
||||
{
|
||||
public class HstsMiddlewareTests
|
||||
{
|
||||
[Fact]
|
||||
public async Task SetOptionsWithDefault_SetsMaxAgeToCorrectValue()
|
||||
{
|
||||
var builder = new WebHostBuilder()
|
||||
.UseUrls("https://*:5050")
|
||||
.ConfigureServices(services =>
|
||||
{
|
||||
})
|
||||
.Configure(app =>
|
||||
{
|
||||
app.UseHsts();
|
||||
app.Run(context =>
|
||||
{
|
||||
return context.Response.WriteAsync("Hello world");
|
||||
});
|
||||
});
|
||||
|
||||
var server = new TestServer(builder);
|
||||
var client = server.CreateClient();
|
||||
client.BaseAddress = new Uri("https://localhost:5050");
|
||||
|
||||
var request = new HttpRequestMessage(HttpMethod.Get, "");
|
||||
|
||||
var response = await client.SendAsync(request);
|
||||
|
||||
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||
Assert.Equal("max-age=2592000", response.Headers.GetValues(HeaderNames.StrictTransportSecurity).FirstOrDefault());
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(0, false, false, "max-age=0")]
|
||||
[InlineData(-1, false, false, "max-age=-1")]
|
||||
[InlineData(0, true, false, "max-age=0; includeSubDomains")]
|
||||
[InlineData(50000, false, true, "max-age=50000; preload")]
|
||||
[InlineData(0, true, true, "max-age=0; includeSubDomains; preload")]
|
||||
[InlineData(50000, true, true, "max-age=50000; includeSubDomains; preload")]
|
||||
public async Task SetOptionsThroughConfigure_SetsHeaderCorrectly(int maxAge, bool includeSubDomains, bool preload, string expected)
|
||||
{
|
||||
var builder = new WebHostBuilder()
|
||||
.UseUrls("https://*:5050")
|
||||
.ConfigureServices(services =>
|
||||
{
|
||||
services.Configure<HstsOptions>(options => {
|
||||
options.Preload = preload;
|
||||
options.IncludeSubDomains = includeSubDomains;
|
||||
options.MaxAge = TimeSpan.FromSeconds(maxAge);
|
||||
});
|
||||
})
|
||||
.Configure(app =>
|
||||
{
|
||||
app.UseHsts();
|
||||
app.Run(context =>
|
||||
{
|
||||
return context.Response.WriteAsync("Hello world");
|
||||
});
|
||||
});
|
||||
|
||||
var server = new TestServer(builder);
|
||||
var client = server.CreateClient();
|
||||
client.BaseAddress = new Uri("https://localhost:5050");
|
||||
var request = new HttpRequestMessage(HttpMethod.Get, "");
|
||||
|
||||
var response = await client.SendAsync(request);
|
||||
|
||||
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||
Assert.Equal(expected, response.Headers.GetValues(HeaderNames.StrictTransportSecurity).FirstOrDefault());
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(0, false, false, "max-age=0")]
|
||||
[InlineData(-1, false, false, "max-age=-1")]
|
||||
[InlineData(0, true, false, "max-age=0; includeSubDomains")]
|
||||
[InlineData(50000, false, true, "max-age=50000; preload")]
|
||||
[InlineData(0, true, true, "max-age=0; includeSubDomains; preload")]
|
||||
[InlineData(50000, true, true, "max-age=50000; includeSubDomains; preload")]
|
||||
public async Task SetOptionsThroughHelper_SetsHeaderCorrectly(int maxAge, bool includeSubDomains, bool preload, string expected)
|
||||
{
|
||||
var builder = new WebHostBuilder()
|
||||
.UseUrls("https://*:5050")
|
||||
.ConfigureServices(services =>
|
||||
{
|
||||
services.AddHsts(options => {
|
||||
options.Preload = preload;
|
||||
options.IncludeSubDomains = includeSubDomains;
|
||||
options.MaxAge = TimeSpan.FromSeconds(maxAge);
|
||||
});
|
||||
})
|
||||
.Configure(app =>
|
||||
{
|
||||
app.UseHsts();
|
||||
app.Run(context =>
|
||||
{
|
||||
return context.Response.WriteAsync("Hello world");
|
||||
});
|
||||
});
|
||||
|
||||
var server = new TestServer(builder);
|
||||
var client = server.CreateClient();
|
||||
client.BaseAddress = new Uri("https://localhost:5050");
|
||||
var request = new HttpRequestMessage(HttpMethod.Get, "");
|
||||
|
||||
var response = await client.SendAsync(request);
|
||||
|
||||
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||
Assert.Equal(expected, response.Headers.GetValues(HeaderNames.StrictTransportSecurity).FirstOrDefault());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.TestHost;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Net.Http.Headers;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.HttpsPolicy.Tests
|
||||
{
|
||||
public class HttpsPolicyTests
|
||||
{
|
||||
[Theory]
|
||||
[InlineData(302, null, 2592000, false, false, "max-age=2592000", "https://localhost/")]
|
||||
[InlineData(301, 5050, 2592000, false, false, "max-age=2592000", "https://localhost:5050/")]
|
||||
[InlineData(301, 443, 2592000, false, false, "max-age=2592000", "https://localhost/")]
|
||||
[InlineData(301, 443, 2592000, true, false, "max-age=2592000; includeSubDomains", "https://localhost/")]
|
||||
[InlineData(301, 443, 2592000, false, true, "max-age=2592000; preload", "https://localhost/")]
|
||||
[InlineData(301, null, 2592000, true, true, "max-age=2592000; includeSubDomains; preload", "https://localhost/")]
|
||||
[InlineData(302, 5050, 2592000, true, true, "max-age=2592000; includeSubDomains; preload", "https://localhost:5050/")]
|
||||
public async Task SetsBothHstsAndHttpsRedirection_RedirectOnFirstRequest_HstsOnSecondRequest(int statusCode, int? tlsPort, int maxAge, bool includeSubDomains, bool preload, string expectedHstsHeader, string expectedUrl)
|
||||
{
|
||||
|
||||
var builder = new WebHostBuilder()
|
||||
.UseUrls("https://*:5050", "http://*:5050")
|
||||
.ConfigureServices(services =>
|
||||
{
|
||||
services.Configure<HttpsRedirectionOptions>(options =>
|
||||
{
|
||||
options.RedirectStatusCode = statusCode;
|
||||
options.TlsPort = tlsPort;
|
||||
});
|
||||
services.Configure<HstsOptions>(options =>
|
||||
{
|
||||
options.IncludeSubDomains = includeSubDomains;
|
||||
options.MaxAge = TimeSpan.FromSeconds(maxAge);
|
||||
options.Preload = preload;
|
||||
});
|
||||
})
|
||||
.Configure(app =>
|
||||
{
|
||||
app.UseHttpsRedirection();
|
||||
app.UseHsts();
|
||||
app.Run(context =>
|
||||
{
|
||||
return context.Response.WriteAsync("Hello world");
|
||||
});
|
||||
});
|
||||
|
||||
var server = new TestServer(builder);
|
||||
var client = server.CreateClient();
|
||||
|
||||
var request = new HttpRequestMessage(HttpMethod.Get, "");
|
||||
|
||||
var response = await client.SendAsync(request);
|
||||
|
||||
Assert.Equal(statusCode, (int)response.StatusCode);
|
||||
Assert.Equal(expectedUrl, response.Headers.Location.ToString());
|
||||
|
||||
client = server.CreateClient();
|
||||
client.BaseAddress = new Uri(response.Headers.Location.ToString());
|
||||
request = new HttpRequestMessage(HttpMethod.Get, "");
|
||||
response = await client.SendAsync(request);
|
||||
|
||||
Assert.Equal(expectedHstsHeader, response.Headers.GetValues(HeaderNames.StrictTransportSecurity).FirstOrDefault());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,125 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.TestHost;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Net.Http.Headers;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.HttpsPolicy.Tests
|
||||
{
|
||||
public class HttpsRedirectionMiddlewareTests
|
||||
{
|
||||
[Fact]
|
||||
public async Task SetOptions_DefaultsSetCorrectly()
|
||||
{
|
||||
var builder = new WebHostBuilder()
|
||||
.ConfigureServices(services =>
|
||||
{
|
||||
})
|
||||
.Configure(app =>
|
||||
{
|
||||
app.UseHttpsRedirection();
|
||||
app.Run(context =>
|
||||
{
|
||||
return context.Response.WriteAsync("Hello world");
|
||||
});
|
||||
});
|
||||
|
||||
var server = new TestServer(builder);
|
||||
var client = server.CreateClient();
|
||||
|
||||
var request = new HttpRequestMessage(HttpMethod.Get, "");
|
||||
|
||||
var response = await client.SendAsync(request);
|
||||
|
||||
Assert.Equal(HttpStatusCode.MovedPermanently, response.StatusCode);
|
||||
Assert.Equal("https://localhost/", response.Headers.Location.ToString());
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(301, null, "https://localhost/")]
|
||||
[InlineData(302, null, "https://localhost/")]
|
||||
[InlineData(307, null, "https://localhost/")]
|
||||
[InlineData(308, null, "https://localhost/")]
|
||||
[InlineData(301, 5050, "https://localhost:5050/")]
|
||||
[InlineData(301, 443, "https://localhost/")]
|
||||
public async Task SetOptions_SetStatusCodeTlsPort(int statusCode, int? tlsPort, string expected)
|
||||
{
|
||||
|
||||
var builder = new WebHostBuilder()
|
||||
.ConfigureServices(services =>
|
||||
{
|
||||
services.Configure<HttpsRedirectionOptions>(options =>
|
||||
{
|
||||
options.RedirectStatusCode = statusCode;
|
||||
options.TlsPort = tlsPort;
|
||||
});
|
||||
})
|
||||
.Configure(app =>
|
||||
{
|
||||
app.UseHttpsRedirection();
|
||||
app.Run(context =>
|
||||
{
|
||||
return context.Response.WriteAsync("Hello world");
|
||||
});
|
||||
});
|
||||
|
||||
var server = new TestServer(builder);
|
||||
var client = server.CreateClient();
|
||||
|
||||
var request = new HttpRequestMessage(HttpMethod.Get, "");
|
||||
|
||||
var response = await client.SendAsync(request);
|
||||
|
||||
Assert.Equal(statusCode, (int)response.StatusCode);
|
||||
Assert.Equal(expected, response.Headers.Location.ToString());
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(301, null, "https://localhost/")]
|
||||
[InlineData(302, null, "https://localhost/")]
|
||||
[InlineData(307, null, "https://localhost/")]
|
||||
[InlineData(308, null, "https://localhost/")]
|
||||
[InlineData(301, 5050, "https://localhost:5050/")]
|
||||
[InlineData(301, 443, "https://localhost/")]
|
||||
public async Task SetOptionsThroughHelperMethod_SetStatusCodeTlsPort(int statusCode, int? tlsPort, string expectedUrl)
|
||||
{
|
||||
var builder = new WebHostBuilder()
|
||||
.ConfigureServices(services =>
|
||||
{
|
||||
services.AddHttpsRedirection(options =>
|
||||
{
|
||||
options.RedirectStatusCode = statusCode;
|
||||
options.TlsPort = tlsPort;
|
||||
});
|
||||
})
|
||||
.Configure(app =>
|
||||
{
|
||||
app.UseHttpsRedirection();
|
||||
app.Run(context =>
|
||||
{
|
||||
return context.Response.WriteAsync("Hello world");
|
||||
});
|
||||
});
|
||||
|
||||
var server = new TestServer(builder);
|
||||
var client = server.CreateClient();
|
||||
|
||||
var request = new HttpRequestMessage(HttpMethod.Get, "");
|
||||
|
||||
var response = await client.SendAsync(request);
|
||||
|
||||
Assert.Equal(statusCode, (int)response.StatusCode);
|
||||
Assert.Equal(expectedUrl, response.Headers.Location.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.HttpsPolicy\Microsoft.AspNetCore.HttpsPolicy.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
Loading…
Reference in New Issue