Site Map Provider Integrated Authorization

The ASP.NET out of the box included site map provider, XmlSiteMapProvider, allows specifying one or more required roles for each node it knows about. Let’s say you have something like this on your Web.sitemap file:

   1: <?xml version="1.0" encoding="utf-8" ?>
   2: <siteMap xmlns="http://schemas.microsoft.com/AspNet/SiteMap-File-1.0" >
   3:     <siteMapNode url="/Default.aspx" title="Home">
   4:         <siteMapNode url="/About.aspx" title="About" />
   5:         <siteMapNode url="/SomePage.aspx" title="Some Page" roles="Administrator" />
   6:     </siteMapNode>
   7: </siteMap>

What it means is:

  • Page /Default.aspx is publicly accessible, since it has no roles attribute specified;
  • Page /About.aspx is also publicly accessible, for the same reason;
  • Page /SomePage.aspx can only be accessed by users with the Administrator role.

Of course, the site map provider doesn’t actually enforce these permissions; what it does is, if, for example, it is bound to a TreeView, it will only display the nodes that the current user is allowed to navigate to. But if someone attempts to navigate to /SomePage.aspx, unless a restriction is defined on the Web.config file, nothing will prevent it from happening.

Knowing this, I wrote a small module that fills in the gap. It strictly enforces what is specified in the site map provider – either XmlSiteMapProvider or any other –, no other restrictions are applied. If a restriction is added through the Web.config file, the access will also fail.

Here is the module:

   1: public class SiteMapAuthorizationModule : IHttpModule
   2: {
   3:     #region IHttpModule Members
   4:  
   5:     public void Dispose()
   6:     {            
   7:     }
   8:  
   9:     public void Init(HttpApplication context)
  10:     {
  11:         context.PostAcquireRequestState += this.OnPostAcquireRequestState;
  12:     }
  13:     
  14:     #endregion
  15:  
  16:     protected void OnPostAcquireRequestState(Object sender, EventArgs e)
  17:     {
  18:         AuthenticationSection section = WebConfigurationManager.OpenWebConfiguration("~").GetSection("system.web/authentication") as AuthenticationSection;
  19:         HttpApplication app = sender as HttpApplication;
  20:         SiteMapProvider provider = SiteMap.Provider;
  21:         SiteMapNode node = provider.CurrentNode;
  22:  
  23:         if (node != null)
  24:         {
  25:             if (node.Roles.Count != 0)
  26:             {
  27:                 if (node.Roles.OfType<String>().All(role => (role != "*") && (app.Context.User.IsInRole(role) == false)))
  28:                 {
  29:                     app.Context.Response.Buffer = true;
  30:                     app.Context.Response.Redirect(String.Format("{0}?ReturnUrl={1}", section.Forms.LoginUrl, HttpUtility.UrlEncode(app.Context.Request.Url.PathAndQuery)), false);
  31:                     app.Context.ApplicationInstance.CompleteRequest();
  32:                 }
  33:             }
  34:         }
  35:     }
  36: }

As you see, it allows specifying multiple roles, separated by commas, or the * symbol, in which case, all users will be granted access, and is effectively equivalent to not specifying a role at all.

You have to register it on Web.config, and it will use whatever site map provider is configured:

   1: <system.web>
   2:     <httpModules>
   3:         <!-- for IIS 6 -->
   4:         <add name="SiteMapAuthorization" type="MyNamespace.SiteMapAuthorizationModule, MyAssembly"/>
   5:     </httpModules>        
   6: </system.web>
   7: <system.webServer>
   8:     <!-- for IIS 7+ -->
   9:     <modules runAllManagedModulesForAllRequests="true">
  10:         <add name="SiteMapAuthorization" type="MyNamespace.SiteMapAuthorizationModule, MyAssembly"/>
  11:     </modules>
  12: </system.webServer>

As always, looking forward to hearing from you!

                             

4 Comments

Add a Comment

As it will appear on the website

Not displayed

Your website