Hierarchical roles in ASP.NET

January 8th, 2008

The concept of hierarchical roles can simplify the application's authorization logic. If we have some resource that only administrators and moderators can access, then instead of testing if the user is in the role A, B, or C, we can just ask for role moderator, and if the user is an administrator, he'll also be able to access the resource.

To accomplish this hierarchical role relation we can use a XML mapping file just like this:

XML:
  1. <roles>
  2.     <role name="admin">
  3.         <role name="moderator">
  4.             <role name="user">
  5.         </role>
  6.     </role>
  7.     <role name="guest">
  8. </role>

With this file we specify that the administrator is also a moderator and that a moderator is also an user. We also specify that we have a guest role with no relation to the other roles.

We can now create a RoleManager.IsInRole method that can interpret this XML structure:

C#:
  1. public static class RoleManager {
  2.  
  3.     ...
  4.  
  5.     public static bool IsInRole(
  6.         IPrincipal principal, string name )
  7.     {
  8.         XPathNavigator nav;
  9.         XPathNodeIterator iter;
  10.         string xpath;
  11.  
  12.         xpath = string.Format("//role[@name='{0}']",name);
  13.         nav = DocumentForRoleFile.CreateNavigator();
  14.         iter = nav.Select(xpath);
  15.  
  16.         foreach( XPathNavigator navigator in iter ) {
  17.             if( CheckParents(navigator,principal) ) {
  18.                 return true;
  19.             }
  20.         }
  21.  
  22.         return false;
  23.     }
  24.  
  25.     private static bool CheckParents(
  26.         XPathNavigator navigator, Principal principal )
  27.     {
  28.         string role = navigator.GetAttribute( "name", "" );
  29.         if( principal.HasRole(role) ) {
  30.             return true;
  31.         }
  32.  
  33.         if( navigator.MoveToParent() ) {
  34.         return CheckParents( navigator, principal );
  35.         }
  36.  
  37.         return false;
  38.     }
  39. }

To make this functionalities available for the ASP.NET framework we'll need to create a Principal class that'll implement IPrincipal and that'll make use of the previous RoleManager class:

C#:
  1. public class Principal :
  2.     System.Security.Principal.IPrincipal  {
  3.  
  4.     ...
  5.  
  6.     public bool IsInRole( string role )
  7.     {
  8.         if( string.IsNullOrEmpty( role ) ) {
  9.             return false;
  10.         }
  11.  
  12.         foreach( string role r in UserRoles ) {
  13.             if( RoleManager.IsInRole(this, role) ) {
  14.                 return true;
  15.             }
  16.         }
  17.  
  18.         return false;
  19.     }
  20. };