Last active
August 29, 2015 13:58
-
-
Save brainded/9962987 to your computer and use it in GitHub Desktop.
Conditionally Require Https Attribute that allows a controller or controller action to force Https unless certain conditions are met to cancel the forward.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace MvcEssentials | |
{ | |
using System; | |
using System.Collections.Specialized; | |
using System.Configuration; | |
using System.Web.Mvc; | |
/// <summary> | |
/// Conditionally Require Https Attribute | |
/// </summary> | |
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = false)] | |
public class ConditionallyRequireHttps : System.Web.Mvc.RequireHttpsAttribute | |
{ | |
/// <summary> | |
/// ConditionallyRequireHttpsEnabled Configuration | |
/// </summary> | |
/// <remarks>On by default if no configuration is found.</remarks> | |
public static readonly string ConditionallyRequireHttpsEnabled = ConfigurationManager.AppSettings["ConditionallyRequireHttpsEnabled"] ?? "true"; | |
/// <summary> | |
/// Determines whether a request is secured (HTTPS) and, if it is not, calls the <see cref="M:System.Web.Mvc.RequireHttpsAttribute.HandleNonHttpsRequest(System.Web.Mvc.AuthorizationContext)" /> method. | |
/// </summary> | |
/// <param name="filterContext">An object that encapsulates information that is required in order to use the <see cref="T:System.Web.Mvc.RequireHttpsAttribute" /> attribute.</param> | |
public override void OnAuthorization(AuthorizationContext filterContext) | |
{ | |
if (filterContext == null) | |
{ | |
throw new ArgumentNullException("filterContext"); | |
} | |
// if the configuration override is present, disable the attribute | |
bool isRequireHttpsAttributeEnabled = false; | |
if (bool.TryParse(ConditionallyRequireHttpsEnabled, out isRequireHttpsAttributeEnabled)) | |
{ | |
if (!isRequireHttpsAttributeEnabled) | |
{ | |
return; | |
} | |
} | |
// if the connection is already secure, do nothing | |
if (filterContext.HttpContext.Request.IsSecureConnection) | |
{ | |
return; | |
} | |
/* You may not want this, but its here by example */ | |
// if the connection is local, do nothing | |
if (filterContext.HttpContext.Request.IsLocal) | |
{ | |
return; | |
} | |
// if the connection is secure from the originating proxy, do nothing | |
if (IsSecuredByOriginatingProxy(filterContext.HttpContext.Request.Headers)) | |
{ | |
return; | |
} | |
this.HandleNonHttpsRequest(filterContext); | |
} | |
/// <summary> | |
/// Determines whether [is secured by originating proxy] [the specified request]. | |
/// </summary> | |
/// <param name="requestHeaders">The request headers.</param> | |
/// <returns> | |
/// <c>true</c> if [is secured by originating proxy] [the specified request]; otherwise, <c>false</c>. | |
/// </returns> | |
private static bool IsSecuredByOriginatingProxy(NameValueCollection requestHeaders) | |
{ | |
//See http://en.wikipedia.org/wiki/List_of_HTTP_header_fields for header explanation | |
const string OriginatingProtocolHeader = "X-Forwarded-Proto"; | |
//get the header value | |
string originatingProtocol = requestHeaders[OriginatingProtocolHeader]; | |
//header not found | |
if (string.IsNullOrWhiteSpace(originatingProtocol)) return false; | |
//compare if the header is what was expected | |
bool isSecure = string.Equals(originatingProtocol, Uri.UriSchemeHttps, StringComparison.InvariantCultureIgnoreCase); | |
return isSecure; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment