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

[Fixes #4112 #4093] Adding support for custom SSL port #4113

Merged
merged 1 commit into from
Feb 23, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Sort usings here too.

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());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does UriBuilder work better with or without the puny-coding that ToUriComponent() adds?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

UriBuilder is removed in favor of the new features in HostString

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I integrated the new feature from HostString, can you confirm it's ok from your side?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

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" },
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Suggest adding an IPv6 example to this theory since the colons might confuse UriBuilder if ToUriComponent() isn't what it wants.
  • Similar suggestion for adding a "本地主機:80" test.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to be testing UriBuilder here?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My concern is the UriBuilder / ToUriComponent() interaction, not either on its own.

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")]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add variation(s) having query string too?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @sebastienros Just FYI...you seem to have squashed your commits. This makes it difficult to find the differences(to see how the feedback was addressed) from earlier commit. You could squash the commits once you get a sign off.

[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);

return services.BuildServiceProvider();
}
}
}