Skip to content
This repository has been archived by the owner on Dec 14, 2018. It is now read-only.

Commit

Permalink
[Fixes #4112 #4093] Adding support for custom SSL port
Browse files Browse the repository at this point in the history
New optional MvcOptions.SslPort. If not defined the redirection uses an empty port (default),
otherwise the custom port is used.
  • Loading branch information
sebastienros committed Feb 23, 2016
1 parent 5b805bb commit c259f82
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 2 deletions.
6 changes: 6 additions & 0 deletions src/Microsoft.AspNetCore.Mvc.Core/MvcOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -127,5 +127,11 @@ public int MaxModelValidationErrors
/// Gets a list of <see cref="IValueProviderFactory"/> used by this application.
/// </summary>
public IList<IValueProviderFactory> ValueProviderFactories { get; }

/// <summary>
/// Gets or sets the SSL port that is used by this application when <see cref="RequireHttpsAttribute"/>
/// is used. If not set the port won't be specified in the secured URL e.g. https://localhost/path.
/// </summary>
public int? SslPort { get; set; }
}
}
19 changes: 18 additions & 1 deletion src/Microsoft.AspNetCore.Mvc.Core/RequireHttpsAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
using System;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;

namespace Microsoft.AspNetCore.Mvc
{
Expand Down Expand Up @@ -35,10 +37,25 @@ protected virtual void HandleNonHttpsRequest(AuthorizationFilterContext filterCo
}
else
{
var optionsAccessor = filterContext.HttpContext.RequestServices.GetRequiredService<IOptions<MvcOptions>>();

var request = filterContext.HttpContext.Request;

var host = request.Host;
if (optionsAccessor.Value.SslPort.HasValue && optionsAccessor.Value.SslPort > 0)
{
// a specific SSL port is specified
host = new HostString(host.Host, optionsAccessor.Value.SslPort.Value);
}
else
{
// clear the port
host = new HostString(host.Host);
}

var newUrl = string.Concat(
"https://",
request.Host.ToUriComponent(),
host.ToUriComponent(),
request.PathBase.ToUriComponent(),
request.Path.ToUriComponent(),
request.QueryString.ToUriComponent());
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
// 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.Http;
using Microsoft.AspNetCore.Http.Internal;
using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Xunit;

namespace Microsoft.AspNetCore.Mvc
Expand Down Expand Up @@ -37,7 +40,7 @@ public static TheoryData<string, string, string, string, string> RedirectToHttpE
return new TheoryData<string, string, string, string, string>
{
{ "localhost", null, null, null, "https://localhost" },
{ "localhost:5000", null, null, null, "https://localhost:5000" },
{ "localhost:5000", null, null, null, "https://localhost" },
{ "localhost", "/pathbase", null, null, "https://localhost/pathbase" },
{ "localhost", "/pathbase", "/path", null, "https://localhost/pathbase/path" },
{ "localhost", "/pathbase", "/path", "?foo=bar", "https://localhost/pathbase/path?foo=bar" },
Expand Down Expand Up @@ -67,6 +70,7 @@ public void OnAuthorization_RedirectsToHttpsEndpoint_ForNonHttpsGetRequests(
{
// Arrange
var requestContext = new DefaultHttpContext();
requestContext.RequestServices = CreateServices();
requestContext.Request.Scheme = "http";
requestContext.Request.Method = "GET";
requestContext.Request.Host = HostString.FromUriComponent(host);
Expand Down Expand Up @@ -109,6 +113,7 @@ public void OnAuthorization_SignalsBadRequestStatusCode_ForNonHttpsAndNonGetRequ
{
// Arrange
var requestContext = new DefaultHttpContext();
requestContext.RequestServices = CreateServices();
requestContext.Request.Scheme = "http";
requestContext.Request.Method = method;
var authContext = CreateAuthorizationContext(requestContext);
Expand All @@ -128,6 +133,7 @@ public void HandleNonHttpsRequestExtensibility()
{
// Arrange
var requestContext = new DefaultHttpContext();
requestContext.RequestServices = CreateServices();
requestContext.Request.Scheme = "http";

var authContext = CreateAuthorizationContext(requestContext);
Expand All @@ -141,6 +147,51 @@ public void HandleNonHttpsRequestExtensibility()
Assert.Equal(StatusCodes.Status404NotFound, result.StatusCode);
}

[Theory]
[InlineData("http://localhost", null, "https://localhost/")]
[InlineData("http://localhost:5000", null, "https://localhost/")]
[InlineData("http://[2001:db8:a0b:12f0::1]", null, "https://[2001:db8:a0b:12f0::1]/")]
[InlineData("http://[2001:db8:a0b:12f0::1]:5000", null, "https://[2001:db8:a0b:12f0::1]/")]
[InlineData("http://localhost:5000/path", null, "https://localhost/path")]
[InlineData("http://localhost:5000/path?foo=bar", null, "https://localhost/path?foo=bar")]
[InlineData("http://本地主機:5000", null, "https://xn--tiq21tzznx7c/")]
[InlineData("http://localhost", 44380, "https://localhost:44380/")]
[InlineData("http://localhost:5000", 44380, "https://localhost:44380/")]
[InlineData("http://[2001:db8:a0b:12f0::1]", 44380, "https://[2001:db8:a0b:12f0::1]:44380/")]
[InlineData("http://[2001:db8:a0b:12f0::1]:5000", 44380, "https://[2001:db8:a0b:12f0::1]:44380/")]
[InlineData("http://localhost:5000/path", 44380, "https://localhost:44380/path")]
[InlineData("http://localhost:5000/path?foo=bar", 44380, "https://localhost:44380/path?foo=bar")]
[InlineData("http://本地主機:5000", 44380, "https://xn--tiq21tzznx7c:44380/")]
public void OnAuthorization_RedirectsToHttpsEndpoint_ForCustomSslPort(
string url,
int? sslPort,
string expectedUrl)
{
// Arrange
var options = new TestOptionsManager<MvcOptions>();
var uri = new Uri(url);

var requestContext = new DefaultHttpContext();
requestContext.RequestServices = CreateServices(sslPort);
requestContext.Request.Scheme = "http";
requestContext.Request.Method = "GET";
requestContext.Request.Host = HostString.FromUriComponent(uri);
requestContext.Request.Path = PathString.FromUriComponent(uri);
requestContext.Request.QueryString = QueryString.FromUriComponent(uri);

var authContext = CreateAuthorizationContext(requestContext);
var attr = new RequireHttpsAttribute();

// Act
attr.OnAuthorization(authContext);

// Assert
Assert.NotNull(authContext.Result);
var result = Assert.IsType<RedirectResult>(authContext.Result);

Assert.Equal(expectedUrl, result.Url);
}

private class CustomRequireHttpsAttribute : RequireHttpsAttribute
{
protected override void HandleNonHttpsRequest(AuthorizationFilterContext filterContext)
Expand All @@ -154,5 +205,16 @@ private static AuthorizationFilterContext CreateAuthorizationContext(HttpContext
var actionContext = new ActionContext(ctx, new RouteData(), new ActionDescriptor());
return new AuthorizationFilterContext(actionContext, new IFilterMetadata[0]);
}

private static IServiceProvider CreateServices(int? sslPort = null)
{
var options = new TestOptionsManager<MvcOptions>();
options.Value.SslPort = sslPort;

var services = new ServiceCollection();
services.AddSingleton<IOptions<MvcOptions>>(options);

This comment has been minimized.

Copy link
@davidfowl

davidfowl Feb 24, 2016

Member

You can use Options.Create(new MvcOptions { SslPort = sslPort })

This comment has been minimized.

Copy link
@sebastienros

sebastienros Feb 24, 2016

Author Member

Thanks, do you want me to change it, or that ok to leave it there?


return services.BuildServiceProvider();
}
}
}

0 comments on commit c259f82

Please sign in to comment.