web 2.0

Using Custom Security Attributes in ASP.NET MVC

In ASP.NET MVC it is common practice to sprinkle attributes all over your controller methods to enforce security. For example if I have an administrative section of my website that I want to restrict to users in the Admin Role I could write the following snippet of code

[Authorize( Roles = "BUILTIN\\Administrators" )]
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult LoginHistory( string userID ) 
{}

This style of securing your controller methods is convenient but it can become a nightmare to maintain. Especially if you later determine that the admin functionality in your application now should be available to the members of several Active Directory groups and a couple of random users. Due to this change, your code would end up looking something like this:

[Authorize( Roles = "MYDOMAIN\\Admins,MYDOMAIN\\Developers,MYDOMAIN\\Managers", Users = "MYDOMAIN\\User1,MYDOMAIN\\User2" )]
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult LoginHistory( string userID ) 
{}

Oh boy, this is ugly! Especially if there are methods in various controllers who all require this change. So how can we avoid this problem? By creating custom attributes! To make a custom attribute,  just create a new class in Visual Studio with the following code:

using System;
using System.Web.Mvc;

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class AdminsAttribute : AuthorizeAttribute
{
    public AdminsAttribute()
    {
        this.Roles = "BUILTIN\\Administrators,MYDOMAIN\\Developers,MYDOMAIN\\Managers";
        this.Users = "MYDOMAIN\\User1,MYDOMAIN\\User2";
    }
}

This code will create a new attribute which is called [Admins]. This is much better because we have now centralized the list of users and roles assigned to our controller code. However, this is still slightly unpleasant because we have hard coded strings in our code. Therefore I would advise you to consider loading the values dynamically from a configuration file or some other location. In any case, this new attribute will significantly clean up my controller code. My new code looks like this:

[Admins]
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult LoginHistory( string userID ) 
{}

On the flipside if you are toggling the visibility of links and controls on your View pages you can also write extension methods which will help you avoid hard coding strings in your pages. For example, I commonly find myself writing this chunk of code in my view to toggle the visibility of a link:

<%= User.IsInRole( "MYDOMAIN\\Admins" ) ? Html.ActionLink( "Delete", "Delete", new { id = Model.ID } ) : String.Empty %>

Instead of writing this code with magic strings, we can create a method on the System.Security.Principal.IPrincipal interface:

using System;
using System.Security;

public static class SecurityExtensions
{
    public static bool IsAdmin( System.Security.Principal.IPrincipal user )
    {
        return user.IsInRole( "MYDOMAIN\\Admins" );
    }
}

Now the code can be written this way instead:

<%= User.IsAdmin() ? Html.ActionLink( "Delete", "Delete", new { id = Model.ID } ) : String.Empty %>

In conclusion, by using extension methods and custom attributes you can avoid using "magic strings" in your Views and Controllers. Overall, this leads to a more maintainable and flexible design.

blog comments powered by Disqus