ASP.NET MVC Custom Authorization ActionFilter
Posted by Dave Bouwman | Posted in ASP.NET MVC, Security | Posted on 22-07-2009
3
The out-of-the-box ASP.NET MVC Authorization filter works nicely if you are using the out-of-the-box ASP.NET Membership and Authorization (roles) capabilities. However, we never seem to be able to utilize them directly because our clients have existing user databases, or we need a richer “user” schema.
Of course we could implement custom providers to plug a different database into this framework, as detailed at MSDN, but this rubs me the wrong way. If my application is going to use a repository pattern for data access, and I’m going to have specific controllers and views for creating/editing users, why should membership and authorization be routed through this other framework?
So, we’ve been “rolling out own” so to speak. We still use System.Web.Security.FormsAuthentication to set cookies, but other than that, we’re on our own. One side-effect of this is that we need to create our own Authorization ActionFilter which will allow us to decorate our controller methods to restrict access based on roles (we dropped the option to restrict by user name). For those not working with ActionFilters yet, this is what it looks like in code.
[Auth(Roles = "Role1, Role3")] public ActionResult Index() { //do things... }
By decorating this controller method with [Auth(Roles = "Role1, Role3")] we automatically restrict access to users who are in “Role1″ or “Role3″.
If someone in Role2 tries to call this method (i.e. they have typed in the url, or were sent a link which they try to visit after logging into the system) we want that logged, and we want the user notified that they have reached an invalid page. We don’t want to just bounce them to the login screen which is confusing.
Since we do use session in the application, we want this filter to also check to make sure the session is valid, and if it is not, redirect the user to the login page, with a message that their session has timed out.
Since this is pretty divergent from the System.Web.Mvc.AuthorizeAttribute behavior, I thought it best to re-implement instead of inherit and override. So – I just popped over to CodePlex, and grabbed the source for AuthorizeAttribute, dropped it into my class, and started changing things.
As I mentioned, we use session state, and one thing we store is the user’s “Account”. This is held in an ApplicationState object in the session. So the first thing we need to do is get the session…
/// <summary> /// Gets the session if it exists. /// </summary> /// <returns></returns> private ApplicationState GetSession(HttpContextBase httpContext) { var sessionObject = httpContext.Session[DRCOGApp.SessionIdentifier]; if (sessionObject != null) return sessionObject as ApplicationState; else { return null; } }
If get a null back from this call, we redirect to the login…
//See if the session is active ApplicationState appState = this.GetSession(filterContext.HttpContext); if (appState == null) { //redirect to login w/ message filterContext.Result = new RedirectToRouteResult( new RouteValueDictionary { { "message", "Session Timed Out" }, { "controller", "Login" }, { "action", "index" }, { "ReturnUrl", filterContext.HttpContext.Request.RawUrl } }); }
Assuming we get past that, we check for roles, if they were specified. If roles are specified, then the rolesSplit array will be populated. To make this a little cleaner we added an IsInRole method to our Account class, which allowed cleaner syntax in the if clause.
//Get the user Account user = appState.CurrentUser;
if (_rolesSplit.Length > 0 && !_rolesSplit.Any(user.IsInRole)) { UnauthorizedModel model = new UnauthorizedModel(); model.CurrentUser = user; model.Message = "The resource you attempted to access is restricted. This access attempt has been logged."; ViewDataDictionary viewData = new ViewDataDictionary(model); filterContext.Result = new ViewResult { ViewName = "~/Views/Error/Unauthorized.aspx", ViewData = viewData }; //If the controller is not null, use it's logger! if (filterContext.Controller != null ) { MyApp.Web.Controllers.ControllerBase ctl = filterContext.Controller as MyApp.Web.Controllers.ControllerBase; ctl.Logger.Log.Warn("Unauthorized attempt to access " + filterContext.HttpContext.Request.RawUrl + " by " + user.Login); } }
The real fun happens in this block. If they are not in the role, we want to return a page which notes that they do not have access to the specified resource, and we want to log that.
Setting the filterContext.Result to the correct ViewResult was pretty straight forward, but it took a little longer to sort out the logging.
Basically, all our controllers inherit from a custom ControllerBase, which requires an instance of ILogService on it’s constructor. The injection is handled by Castle Windsor, via mvccontrib so it’s completely unobtrusive. It just took a while to realize that we could access the controller from the filterContext (a instance of AuthorizationContext). Once we had the controller, we use the logger, and we’re done.
Overall, this seems to be working pretty well. Once we’re really sure it’s bomber, and I’ve found time to decouple some aspects of it from this particular project, I’ll post the source code – in the meantime, grab the source from CodePlex and have fun rolling your own AuthorizationFilters!


Nice Post! Really very useful information. Thanks.
[...] to VoteASP.NET MVC Custom Authorization ActionFilter (7/22/2009)Wednesday, July 22, 2009 from blog.davebouwman.comThe out-of-the-box ASP. NET MVC Authorization [...]
ASP.NET MVC Custom Authorization ActionFilter – Dave Bouwman…
Thank you for submitting this cool story – Trackback from DotNetShoutout…