MVC3: How can I route non-SSL requests automatically to the SSL protocol? - asp.net-mvc-3

I have a site that needs to be secured with SSL. How can I set up a route or IIS configuration that will automatically send any request received on the non-SSL protocol over to the SSL protocol? Is this something that can be handled as a routing rule, or would it be best to use the RequireHttps attribute in the primary controller only on the methods rather than on the controller itself and detect the protocol in the Index() method?
As a note: I read this question that makes use of UrlRewriting and IIS7's Application Request Routing, but I am stuck with IIS6 and UrlRewriting is not an option for me.

Something like this will help:
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
filters.Add(new RequireHttpsAttribute());
}

This is what we use. Would love to hear if it can be improved.
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = true, AllowMultiple = false)]
public class EnforceHttpsAttribute : RequireHttpsAttribute
{
private static bool AuthorizeCore(HttpContextBase httpContext)
{
return httpContext.Request.IsSecureConnection;
}
public override void OnAuthorization(AuthorizationContext filterContext)
{
if (!AuthorizeCore(filterContext.HttpContext))
{
HandleNonHttpsRequest(filterContext);
}
else
{
var cache = filterContext.HttpContext.Response.Cache;
cache.SetProxyMaxAge(new TimeSpan(0L));
cache.AddValidationCallback(CacheValidateHandler, null);
}
}
// ReSharper disable RedundantAssignment
private static void CacheValidateHandler(HttpContext context, object data, ref HttpValidationStatus validationStatus)
// ReSharper restore RedundantAssignment
{
validationStatus = OnCacheAuthorization(new HttpContextWrapper(context));
}
private static HttpValidationStatus OnCacheAuthorization(HttpContextBase httpContext)
{
return !httpContext.Request.IsSecureConnection
? HttpValidationStatus.IgnoreThisRequest
: HttpValidationStatus.Valid;
}
}
Response to comment 1
Good question, I'm not sure. HandleNonHttpsRequest comes from the base RequireHttpsAttribute. I just did an inspection in fiddler2, and there was only 1 request sent over http. However the response came back over https.
I just realized we use the above to make RequireHttps work with the output cache. You might be better off just using the RequireHttps attribute.

Related

Capturing and injecting HttpRequestMessage in Web API with Ninject

I've got a class that requires access to the HttpRequestMessage in my Web API service. At the moment, I've got the following code to capture the message in the pipeline and save it for later (based on this and this):
public class ContextCapturingControllerActivator : IHttpControllerActivator
{
private readonly IKernel kernel;
private HttpRequestMessage requestMessage;
public ContextCapturingControllerActivator(IKernel kernel)
{
this.kernel = kernel;
}
public IHttpController Create(HttpRequestMessage requestMessage,
HttpControllerDescriptor controllerDescriptor,
Type controllerType)
{
this.kernel.Rebind<HttpRequestMessage>()
.ToConstant<HttpRequestMessage>(requestMessage);
var controller = (IHttpController)this.kernel.GetService(controllerType);
this.requestMessage = requestMessage;
requestMessage.RegisterForDispose(
new Release(() => this.kernel.Release(controller)));
return controller;
}
private class Release : IDisposable
{
private readonly Action release;
public Release(Action release)
{
this.release = release;
}
public void Dispose()
{
this.release();
}
}
}
In my composition root, I configure the ControllerActivator:
kernel.Bind<IHttpControllerActivator>()
.To<ContextCapturingControllerActivator>();
The end result is that from the perspective of the configuration, the HttpRequestMessage is "magically" injected wherever it is requested since it is done for us inside the ControllerActivator. I have not been able to inject the message from my composition root. I'm also not crazy about the Rebind since it's there to avoid adding a new binding every time the service is called. I suspect it's due to the singleton nature of the Web API stack, but have not been able to sort out how to deal with that properly.
In general, I cannot use the latest unstable Nuget package of Ninject web api due to the error reported (and ignored) here.
Can anyone suggest the proper way to improve my code to make it a bit more clear and make life easier for future maintainers (and let's face it -- that's probably going to be me).
Thanks.
Here is what I did, but I believe it depends on Web API 2.0+.
I created an instance class that wraps the current context's http request:
public class HttpRequestMessageWrapper
{
private readonly HttpRequestMessage m_httpRequestMessage;
public HttpRequestMessageWrapper()
{
m_httpRequestMessage = HttpContext.Current.Items["MS_HttpRequestMessage"] as HttpRequestMessage;
}
public HttpRequestMessage RequestMessage
{
get
{
return m_httpRequestMessage;
}
}
}
Then I bound the HttpRequestMessage to the property with the ToMethod binding in request scope.
container.Bind<HttpRequestMessage>().ToMethod(ctx => new HttpRequestMessageWrapper().RequestMessage).InRequestScope();
I've tried the method that #Mackers proposed which is the cleanest way.... however, in my specific scenario, it didn't work due to a timing issue. For my case, I needed to inject an object into the apicontroller ctor and that object required the HttpRequestMessage. The HttpContext.Current.Items["MS_HttpRequestMessage"]isn't populated until the controller has been constructed and initialized and I couldn't find any other way to access it. So I resorted to creating a custom DelegatingHandler and rebinding the current request message as they come in.
public class CurrentHttpRequestMessageHandler : DelegatingHandler
{
[SecuritySafeCritical]
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
UpdateScopeWithHttpRequestMessage(request);
return base.SendAsync(request, cancellationToken);
}
internal static void UpdateScopeWithHttpRequestMessage(HttpRequestMessage request)
{
NinjectConfig.GetConfiguredKernel().Rebind<HttpRequestMessage>().ToMethod(ctx => { return request; })
.InRequestScope();
}
}
The GetConfiguredKernel is a static method I created to simply return the static Kernel instance already configured.
public class NinjectConfig
{
private static readonly Bootstrapper bootstrapper = new Bootstrapper();
private static StandardKernel _kernel;
public static void Start()
{
DynamicModuleUtility.RegisterModule(typeof(OnePerRequestHttpModule));
DynamicModuleUtility.RegisterModule(typeof(NinjectHttpModule));
bootstrapper.Initialize(CreateKernel);
}
public static IKernel GetConfiguredKernel()
{
if (_kernel != null)
return _kernel;
return CreateKernel();
}
....
Then register the DelegatingHandler with the HttpConfiguration:
config.MessageHandlers.Add(new CurrentHttpRequestMessageHandler());
Building off of Macker's answer, System.Web has an HttpRequestBase class that you can use and simplify unit testing the code. Anywhere in the code that the request is required, specify the HttpRequestBase type as the constructor parameter and register it with the below method:
Ninject example:
Bind<HttpRequestBase>().ToMethod(context => new HttpRequestWrapper(HttpContext.Current.Request));
Unity example:
container.RegisterType<HttpRequestBase>(new InjectionFactory(_ => new HttpRequestWrapper(HttpContext.Current.Request)));

SignalR in SharePoint

My project has a need for realtime user interaction and I think SignalR will solve my need. I'm technically on a SharePoint 2007 project, although I'm exclusively in application pages and thus barely use SharePoint at all. Regardless, I'm stuck in a 2.0 framework app pool in IIS.
My first approach was to try to create a 4.0 application as a sub-site. Unfortunately, that failed miserably. That approach works in a non-SharePoint world, but it appears that SharePoint has hijacked too much of the request pipeline for this approach to work for me.
So now I'm going down the path of creating a separate IIS Site that's 4.0 and using IIS rewrite rules to fake my app into thinking a particular subdirectory (/realtime/) is local and not a separate site so that I don't have to deal with cross domain request issues. The problem is I can't get IIS rewrite rules to rewrite to another http host (e.g. http://www.mySharepoint.com/_layouts/MySite/realtime/Hello.aspx to http://realtime.mySharePoint.com/Hello.aspx).
Any help with approach #1 or approach #2 or any alternative ideas would be greatly appreciated.
Here is what I did... Web App with signalR .net4.0, then your SharePoint Web App .net 2.
Add this to the global.asax in your Signalr project
RouteTable.Routes.MapHttpHandlerRoute("spproxy","spproxy/{*operation}", new SharePointRProxyHandler());
If you want to raise an event from SharePoint you can do a http POST to this new route URL for example
http://localhost:38262/spproxy
It will pass any posted data onto the httphandler below, that will then broadcast it to your clients.
Here is the code for MapHttpHandlerRoute
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace System.Web.Routing
{
public class HttpHandlerRoute : IRouteHandler
{
private String _virtualPath = null;
private IHttpHandler _handler = null;
public HttpHandlerRoute(String virtualPath)
{
_virtualPath = virtualPath;
}
public HttpHandlerRoute(IHttpHandler handler)
{
_handler = handler;
}
public IHttpHandler GetHttpHandler(RequestContext requestContext)
{
IHttpHandler result;
if (_handler == null)
{
result = (IHttpHandler)System.Web.Compilation.BuildManager.CreateInstanceFromVirtualPath(_virtualPath, typeof(IHttpHandler));
}
else
{
result = _handler;
}
return result;
}
}
public static class RoutingExtensions
{
public static void MapHttpHandlerRoute(this RouteCollection routes, string routeName, string routeUrl, string physicalFile, RouteValueDictionary defaults = null, RouteValueDictionary constraints = null)
{
var route = new Route(routeUrl, defaults, constraints, new HttpHandlerRoute(physicalFile));
RouteTable.Routes.Add(routeName, route);
}
public static void MapHttpHandlerRoute(this RouteCollection routes, string routeName, string routeUrl, IHttpHandler handler, RouteValueDictionary defaults = null, RouteValueDictionary constraints = null)
{
var route = new Route(routeUrl, defaults, constraints, new HttpHandlerRoute(handler));
RouteTable.Routes.Add(routeName, route);
}
}
}
Or you could just post directly to a httphandler and get the handler to do a connection.Broadcast
namespace SharePointRProxy
{
/// <summary>
/// Summary description for SharePointRProxyHandler
/// </summary>
public class SharePointRProxyHandler : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
context.Response.ContentType = "text/plain";
IConnectionManager connectonManager = AspNetHost.DependencyResolver.Resolve<IConnectionManager>();
IConnection connection = connectonManager.GetConnection<MyConnection>();
object payload = null; //Add payload here 'context.Request.Params["data"] ?'
JavaScriptSerializer jss = new JavaScriptSerializer();
var payloadJSON = jss.Serialize(payload);
connection.Broadcast(payloadJSON);
}
public bool IsReusable
{
get
{
return false;
}
}
}
}
You could also use either an event handler calling a .net 4.0 web service or an http handler to grab requests from SharePoint and pass them over to a .net 4.0 application running your signalr code.
You can see an example of using an http handler here: http://spmatt.wordpress.com/2012/04/12/harnessing-signalr-in-sharepoint/

Best way of handling timeouts with AsyncController

I have a long time polling controller in my MVC3 project. It has its timeout set to 30 seconds. I have a HandleErrorAttribute implementation that handles logging of all errors.
Since the timout throws a TimeoutException it means these will be presented in the log.
I need to intercept this error before my HandleErrorAttribute class gets it and return a json object instead of the 500 error page. Whats the best approach for this?
I did this and it works
public class HandleTimeout : HandleErrorAttribute
{
public override void OnException(ExceptionContext filterContext)
{
if(filterContext.Exception is TimeoutException)
{
filterContext.Result = new { Timeout = true }.AsJson();
filterContext.ExceptionHandled = true;
filterContext.HttpContext.Response.StatusCode = 200;
}
base.OnException(filterContext);
}
}
Best approach?
I went with this route, the difference from my above code is that I also check if the Controller is Async, because we only want to handle Timeouts in this fashion if we are in a long time polling scenarios.
public class HandleTimeout : HandleErrorAttribute
{
public override void OnException(ExceptionContext filterContext)
{
if(filterContext.Exception is TimeoutException && filterContext.Controller is AsyncController)
{
filterContext.HttpContext.Response.StatusCode = 200;
filterContext.Result = new { Timeout = true }.AsJson();
filterContext.ExceptionHandled = true;
}
base.OnException(filterContext);
}
}
The notion of best is very subjective. I prefer not to talk about it as different people have different definition of it. For me using a custom exception filter is a very good approach to handle this case without polluting your controller with exception handling code.

Is having multiple authentication providers at the same time possible in ASP.NET MVC3 application?

A client application is uploading an audio file in "chunks" to an MVC3 site. A client uses HttpWebRequest POST to do it.
On the server, I have the following controller action:
[Authorize]
[HttpPost]
public JsonResult RecieveChunk(string id, [ModelBinder(typeof(AudioChunkModelBinder))] byte[] audio)
{
//Process chunk
var chunk = new AudioChunk
{
ThoughtId = Guid.Parse(id),
Data = audio
};
//Process chunk by BL
return new JsonResult {Data = "Success"};
}
Currently, a built-in AspNetMemebershipProvider is handling the authorization, so the client app has to first authenticate at the logon page, obtain cookie into a CookieContainer and then make a call to a server to upload a chunk of data.
I want to allow clients to also be able to anonymously upload audio files to the server, without a need to previously register. They the client app code will provide the same guid each time the file is uploaded from the same device.
I want both categories of users to share the same RecieveChunk action to do it. But they must be authrized either anonymously (with just guid), or with the logon/pass combination.
Can I have two different controllers linked to two different authentication providers? The third controller, that has [Authorize] marked action, will allow action if either one provider has given a user a cookie (or some other auth method).
Is it possible in general in ASP.NET MVC3?
As discussed in comments you can create a custom implementation of the FilterAttribute class and implement the IAuthorizationFilter interface. For example here is the ChildActionOnlyAttribute implementation:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public sealed class ChildActionOnlyAttribute : FilterAttribute, IAuthorizationFilter
{
public void OnAuthorization(AuthorizationContext filterContext)
{
if (filterContext == null)
throw new ArgumentNullException("filterContext");
if (!filterContext.IsChildAction)
throw Error.ChildActionOnlyAttribute_MustBeInChildRequest(filterContext.ActionDescriptor);
}
}
And here is the RequireHttpsAttribute implementation:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class RequireHttpsAttribute : FilterAttribute, IAuthorizationFilter
{
public virtual void OnAuthorization(AuthorizationContext filterContext)
{
if (filterContext == null)
throw new ArgumentNullException("filterContext");
if (filterContext.HttpContext.Request.IsSecureConnection)
return;
this.HandleNonHttpsRequest(filterContext);
}
protected virtual void HandleNonHttpsRequest(AuthorizationContext filterContext)
{
if (!string.Equals(filterContext.HttpContext.Request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase))
throw new InvalidOperationException(MvcResources.RequireHttpsAttribute_MustUseSsl);
string url = "https://" + filterContext.HttpContext.Request.Url.Host + filterContext.HttpContext.Request.RawUrl;
filterContext.Result = (ActionResult) new RedirectResult(url);
}
}
So you could do something like:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public sealed class CustomAuthorizeAttribute : FilterAttribute, IAuthorizationFilter
{
public void OnAuthorization(AuthorizationContext filterContext)
{
if (filterContext == null)
throw new ArgumentNullException("filterContext");
var guidPresent = CheckForGuid();
if (!filterContext.HttpContext.User.Identity.IsAuthenticated && !guidPresent)
throw new InvalidOperationException("Must authenticate properly")
}
}

subclassing outputcache issues in mvc3

I am having some issues understanding what is happening when I create a simple subclass of OutputCacheAttribute in MVC3. Here is the code:
public class ExampleOutputCacheAttribute : OutputCacheAttribute
{
public ExampleOutputCacheAttribute()
{
// breakpoint here
}
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
// breakpoint here
base.OnActionExecuting(filterContext);
}
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
// breakpoint here
base.OnActionExecuted(filterContext);
}
public override void OnResultExecuting(ResultExecutingContext filterContext)
{
// breakpoint here
base.OnResultExecuting(filterContext);
}
public override void OnResultExecuted(ResultExecutedContext filterContext)
{
// breakpoint here
base.OnResultExecuted(filterContext);
}
}
The first time a controller action with this attribute is requested, the constructor and all overridden methods are hit, but if I refresh the page, none of the methods or the constructor are hit. It is as if the cache is being read from outside the OutputCacheAttribute, but looking at the MVC source code for OutputCacheAttribute, I can see that in OnActionExecuting, there is code for checking for a cached page and returning the result:
filterContext.Result = new ContentResult() { Content = cachedValue };
Can anyone shed any light on what is happening?
It seems as though the OutputCache filter is more complicated than it originally appears. For page caching, it hooks in to the standard ASP.NET output caching mechanism which uses the OutputCacheModule HttpModule in IIS. Once the filter is hit once and adds the page to the cache, subsequent requests do not hit the filter in any way. The OutputCacheModule intercepts these requests and returns the cached object higher up the pipeline.
For action caching, a separate mechanism is used. This uses a static MemoryCache and the constructor and all overridden methods are hit on every request.

Resources