Wednesday, March 31, 2010

Unit Testing Role Based Security w/ ASP.Net MVC

I recently started working on a little project management demo project in ASP.Net MVC. As part of that I wanted to make sure I unit tested as much as possible so that it could act as a good example of how to do things for other people in the future.

I ran into a bit of a snag when trying to verify that the right security roles are required on my actions. I found out from a few blog posts that ASP.Net follows the .Net 2.0 membership and role provider model, and that you can simple decorate a controller or action with [Authorized] attributes to restrict access. Pretty cool and really simple unfortunately not very testable. When you unit test actions in MVC you call the method directly from the test, bypassing the routing engine where these authorization checks are made.

Fortunately I found Paul Brown's excellent post on how to test that you have applied the right roles. I liked what he did but it was a bit to case specific for me so I made it a bit more generic. Here's what I came up with:

I took Paul's code and turned it into a series of three controller extension methods which verify if the entire controller requires authorization, a particular method requires authorization or if a particular method requires a given role (i have not yet written the obvious 4th case - an entire controller requires a given role but it should be trivial.)
public static class ControllerExtensions 
{
     public static bool RequiresAuthorization(this Controller controller)
     {
           MemberInfo memberInfo = controller.GetType();
           var attributes = memberInfo.GetCustomAttributes(typeof(AuthorizeAttribute), true);              
           return attributes != null && attributes.Length == 1;
     }
     
     public static bool ActionRequiresAuthorization<T>(this Controller controller, Expression<Action<T>> expression)
     {
           var member = expression.Body as MethodCallExpression;
           if (member != null)
           {
                 var methodInfo = member.Method;
                 if (methodInfo != null)
                 {
                       var attributes = methodInfo.GetCustomAttributes(typeof(AuthorizeAttribute), true);
                       return attributes != null && attributes.Length == 1;
                 }
           }
           return false;
      }
 
      public static bool ActionRequiresRole<T>(this Controller controller, Expression<Action<T>> expression, string role)
      {
            var member = expression.Body as MethodCallExpression;
            if (member != null)
            {
                 var methodInfo = member.Method;
                 if (methodInfo != null)
                 {
                       var attributes = methodInfo.GetCustomAttributes(typeof(AuthorizeAttribute), true);
                       var authorizeAttribute = (AuthorizeAttribute)attributes[0];
                       return authorizeAttribute.Roles.Contains(role);
                 }
            }
            return false;
       }
}


Here is an example test for Requires Authorization: (using NUnit)
[Test]
public void Should_require_authorized_user_for_all_actions()
{
     var controller = new ProjectController(null);             
     Assert.That(controller.RequiresAuthorization());
}


And a sample for ActionRequiresRole
[Test]
public void Should_require_admin_to_add_a_bug()
{
     var controller = new BugController(null);
     Assert.That(controller.ActionRequiresRole<BugController>(x=>x.Add(), "Admin"));       
}



EDIT: John suggested changing the extension methods into lambda actions so that they would be strongly typed. I agreed so this has been updated accordingly.

3 comments:

  1. Originally from Kevin Berridge -

    Nice!

    ReplyDelete
  2. Originally from .Net Developer -

    Superb nice post! i’m loving this!! Might sure come back after to get more posts of this nature.

    ReplyDelete
  3. Apple Card has laudable features would possibly be} designed to help sensible users handle their money, for example, however you may be sure much more users are unwise about their money. Big profits from Apple Card rely not simply on per-transaction charges, but additionally from a stack of interest from of 먹튀검증커뮤니티 us that carry balances. But the place the road is blurry between precise privacy for users and leveraging privacy for revenue, the road is as clear as a COVID check when it comes to of|in relation to} whether or not Apple profits from playing or not. Maybe it’s simply nostalgia, however seeing Robertson at this value with a house recreation in opposition to a team within the backside three is hard to disregard. He still will get ahead, and there’s a chance for the clean sheet in opposition to Leeds falling quick and struggling on the road. He is yet to tally single-digit factors in any Fantrax game-week.

    ReplyDelete