Authorizing Access via Attributes in ASP.NET MVC Without Magic Strings
Recently I developed a strategy which I think works well for authorizing access to user groups (Roles) without using the string names of those groups.
The problem I am trying to avoid is doing something like [Authorize(Roles=”AdminRole”)] on a controller or action since I know the role names can change & one typo can mess everything up.
Role Names
So first of all I usually have a static class which has the names & aliases for all roles in case they change:
public static class RoleNames
{
public static readonly string Supervisor = "Supervisor";
public static readonly string Admin = "StateOffice";
public static readonly string ProjectAdmin = "ProjectAdmin";
public static readonly string DelegateSupervisor = "Delegate";
}
This is pretty standard for me, but unfortunately I can’t just do [Authorize(Roles=RolesNames.Admin)] because attributes requires constant expressions. So as a solution I came up with the idea of creating a custom attribute which will tightly control access based on specific role criteria.
Creating a Custom Authorize Attribute
When creating the custom authorize attribute I inherit from AuthorizeAttribute since it already contains most of the logic I need. All I need to do is set the Roles property in the constructor to a comma delimited list of the authorized roles, and the authorize attribute base class will take care of the rest.
For example – to restrict access to just the admin role:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class AdminOnlyAttribute : AuthorizeAttribute
{
public AdminOnlyAttribute()
{
Roles = RoleNames.Admin;
}
}
Or if you want to include the project admins as well:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class AdminOnlyAttribute : AuthorizeAttribute
{
public AdminOnlyAttribute()
{
var authorizedRoles = new[] {RoleNames.Admin, RoleNames.ProjectAdmin};
Roles = string.Join(",", authorizedRoles);
}
}
Usage
Then on your controller you restrict access like this
[AdminOnly]
public class AdminController : Controller{}
And it also works on an action
public class AdminController : Controller
{
[AdminOnly]
public ActionResult AdminOnlyAction()
{
return View();
}
}
Enjoy!