Authorization in ASP.Net MVC using XML Configuration.

Download Source Code

Doing authorization in a clean way is always tricky, You want a delicate balance between an extreme abstraction and something like embedding roles in-side your compiled code, I have always preferred simple abstraction either using roles and their corresponding mappings in the database or using simple xml file to store action to role mappings.

Asp.net MVC comes with built in Authorization filter attribute that you can use on your Controller and Action to define the role that can access corresponding Controller or Action. This approach will work fine for small application where you have predefined action to role mappings, but when you have bigger application where developers are not going to define role mappings and mappings might change frequently then maintenance of hard quoted roles might become nightmare.

I have created simple application to demonstrate how you can xml based configuration to apply authorization in MVC application. Following are four major pieces of the application.

  • ConfigurationSectionHandler for defining XML Configuration for Controller and Action to role mapping.
  • IMVCAuthorizer Interface and implementation MCVXMLAuthorizer.
  • HttpModule which plugs into AuthorizeRequest event to validate if user are authorized to access Controller and Action.
  • Sample MVC application to test XMLAuthorizer.

Defining XML and Configuration Section.

Following is the XML structure that defines Controller and Action to role mappings, As you can see Controller and Actions can have their own set of roles. Empty Home Controller section means everybody has access to Controller and all Actions.

As you can also see in first controller node Admin, Edit and View roles have access to Admin Controller but access to Edit and Admin action if limited to specific roles only.

< controllers >
  
< controller  name ="Admin">
    
< roles >
      
< role > Admin </ role >
      
< role > Edit </ role >
      
< role > View </ role >
    
</ roles >
    
< actions >
      
< action  name ="Index">
        
< roles >
          
< role > Admin </ role >
          
< role > Edit </ role >
          
< role > View </ role >
        
</ roles >
      
</ action >
      
< action  name ="Edit">
        
< roles >
          
< role > Edit </ role >
          
< role > Admin </ role >
        
</ roles >
      
</ action >
      
< action  name ="Admin">
        
< roles >
          
< role > Admin </ role >
        
</ roles >
      
</ action >
    
</ actions >
  
</ controller >
  
< controller  name ="Home"> </ controller >
</ controllers >  


Configuration Section handler has two important properties 1) Type which defines fully qualified type name of class implements IXMLAuthorizer interface and 2) ConnectionString which in our case is the path of XMLConfiguration file, additionally It also has static GetSettingsMethod which returns current settings from Web.Config file.

public class AuthorizationMappingSection : IConfigurationSectionHandler {

        
public object Create(object parent, object configContext, System.Xml.XmlNode section) {
            
// Create an instance of XmlSerializer based on the RewriterConfiguration type...
            
XmlSerializer ser = new XmlSerializer(typeof(AuthorizationMappingSection));

            
// Return the Deserialized object from the Web.config XML
            
return ser.Deserialize(new XmlNodeReader(section));
        
}

        
public static AuthorizationMappingSection GetSettings() {
            
if (HttpContext.Current.Cache["AuthorizationMappingSection"== null) {
                AuthorizationMappingSection settings
                    
(AuthorizationMappingSection)ConfigurationManager.GetSection("AuthorizationMappingSection");

                
HttpContext.Current.Cache["AuthorizationMappingSection"settings;
            
}

            
return ((AuthorizationMappingSection)HttpContext.Current.Cache["AuthorizationMappingSection"]);
        
}

       [XmlAttribute(
"type")]
        
public string Type { get; set; }

        [XmlAttribute(
"connectionString")]
        
public string ConnectionString { get; set; }
}


IMVCAuthorizer and MCVXMLAuthorizer Implementation.

IMVCAuthorizer interface has two methods, 1) IsAuthorized which takes controllerName and actionName and Principle object. And 2) Initialize methods which takes connection string.


public interface IMVCAuthorizer {
     
       
bool IsAuthorized(string controllerName, string actionName, System.Security.Principal.IPrincipal user);
 
       void 
Initilize(string connectionString);
}


MCVXMLAuthorizer implements IMVCAuthorizer, In Initialize method it reads XML file and de-serialize it into ControllerAuthorizationInfoCollection class, as you can see it puts de-serialize object into cache and puts dependency on physical file so any time some body changes the file cache will be invalidated.

IsAuthorized is the method that contains logic to check authorization against XML configuration you can download the source code if you want to go deeper into logic inside  ControllerAuthorizationInfoCollection class.


public class MCVXMLAuthorizer:IMVCAuthorizer {

       ControllerAuthorizationInfoCollection controllers 
= null;

       public bool 
IsAuthorized(string controllerName ,string actionName, System.Security.Principal.IPrincipal user) {
            
return controllers.CanAccessAction(controllerName,actionName, user);
        
}

        
public void Initilize(string connectionString) {
            
string key "MCVXMLAuthorizerCacheKey";

            if 
(HttpContext.Current.Cache[key] != null) {
                controllers 
(ControllerAuthorizationInfoCollection)HttpContext.Current.Cache[key];
           
else {
                
string path HttpContext.Current.Server.MapPath(connectionString);
                
controllers getControllerAuthorizationInfoCollection(path);
                
HttpContext.Current.Cache.Insert(key, controllers, new System.Web.Caching.CacheDependency(path));
            
}
        }

        
private ControllerAuthorizationInfoCollection getControllerAuthorizationInfoCollection(string path) {
            ControllerAuthorizationInfoCollection rVal 
= null;           
            
XmlSerializer ser = new XmlSerializer(typeof(ControllerAuthorizationInfoCollection));

            using 
(FileStream fs File.OpenRead(path)) {
                rVal 
(ControllerAuthorizationInfoCollection)ser.Deserialize(fs);
            
}
            
return rVal;
        
}
    }

HttpModule

AuthorizationMappingModule is an HttpModule which hooks into AuthorizedRequest event of page life cycle and check is user has access to particular controller and action, if user does not have privilege to access then it will throw Security Exception.

void OnAuthorizeRequest(object sender, EventArgs e) {
            HttpContext context 
((HttpApplication)sender).Context;
            
RouteData routeData RouteTable.Routes.GetRouteData(new HttpContextWrapper(context));

            if 
(routeData != null) {
                
string controller routeData.GetRequiredString("controller");
                string 
action routeData.GetRequiredString("action");
                
IMVCAuthorizer authorizer GetMVCAuthorizer();

                if 
(!authorizer.IsAuthorized(controller, action, context.User)) {
                    
string message = string.Format("User {0} does not have permission to access {1} on {2}"
                        
, context.User.Identity.Name, action, controller);

                    
System.Diagnostics.Trace.TraceInformation(message);
                    throw new 
SecurityException(message);
                
}
            }
   }

Sample MVC Application to test Authorization.

I have created a simple MVC application with two Controllers, Controller that we are going to test is Admin Controller, I have also simple UI in Home Controller which allows you to login as an different user and try to access different links. As an-authenticated user you should not have access to any actions on Admin Controller and as you switch your role by clicking different login links your permission will change. following is the screen shot of application.



You will need to add reference to MVCAuthorization.dll and following configuration section handler and http module settings in your web.config file.
 


Next Steps,

If you are planning to store mappings in database you can easily create your own implementation of MVCSQLAuthorizer and plug into Web.config file.

8 Comments

  • This looks nice. And you also can use the refactor to create a instance of the Page to check the permission as well

  • Helpfull blog...

  • Just had a quick scan though the code and its all very cool. However - The authorization is a check against a "user" (IPrincipal User) and not a "role". Guess it can be changed so you could have the hierarchy of Users in Roles and so Roles have controller/action permissions.

  • This is a very interesting solution.

    Suppose that I wanted to include in my authorization check some contextual information regarding the actual object that a user was attempting to access. For example, in an online banking application, I might want to verify that the user is actually attempting to access his own bank account. How might it be possible to incorporate such contextual information into this authorization scheme?

  • do you have any suggestion to implement this with a database? what tables do you need, etc... ?

  • How does it work when you have areas?

    If I have multiple areas and they have same control names, can it be handled with this implementation?

  • Nice!
    What to do think about working with it using MVC 3.0 Razor?
    Does it requires to update?

    Thanks

  • Can you please tell us What to do to host on IIS?

Comments have been disabled for this content.