In our last cliff-hanger episode, I introduced the ImpostorHttpModule. I’m going to show how you can use it to implement and test a sitemap and navigation menu in ASP.NET. We’ll use the new ASP.NET 2.0 Master Pages feature because it’s the easiest way to ensure that the same menu ends up on every page. We’ll start from the previous solution with the ImpostorHttpModule registered in the Web.config. I’ve created three pages, ~/Default.aspx, ~/Reports/Default.aspx, and ~/Admin/Default.aspx. The Web.config files are set up as follows:

Path Allowed Roles
~/Default.aspx User, Manager, Administrator
~/Reports/Defaults.aspx Manager, Administrator
~/Admin/Default.aspx Administrator

We’ll be using the security trimming feature of sitemaps, which removes nodes from the sitemap that are not accessible by the current user. Note that this is simply a UI nicety and not actual security – without the appropriate Web.config files and <authorization> sections, users could still access those areas by navigating directly to them. So let’s start by implementing the menu and then we’ll enable security trimming. We’ll see how ImpostorHttpModule can help us in testing these features.

Our first step is to add a sitemap, which we’ll leave with the default name of Web.sitemap. We’ll add three nodes – Home, Reports and Administration. One of the odd things about the sitemap XML schema is that the child of <siteMap> must be a single <siteMapNode>. So if you want to have multiple nodes, you need to create an empty <siteMapNode> with further child nodes like this:

<?xml version=”1.0″ encoding=”utf-8″ ?>
<siteMap xmlns=”http://schemas.microsoft.com/AspNet/SiteMap-File-1.0” >
    <siteMapNode>
        <siteMapNode url=”~/Default.aspx” title=”Home”  description=”Return to the Home page” />
        <siteMapNode url=”~/Reports/Default.aspx” title=”Reports” description=”Go to reports” />
        <siteMapNode url=”~/Admin/Default.aspx” title=”Administration”  description=”Go to site administration” />
    </siteMapNode>
</siteMap>

One quick note is the use of ~/ which allows us to create links that are relative to the application’s virtual directory. This is a great ASP.NET feature, which works in most places links are found in server-side controls, as it allows you to deploy the site at http://servername/ or http://servername/someVdir/ or anywhere else.

Now that we have our sitemap set up, we can create a Menu that is driven by the sitemap. We’ll create a master page called Main.master and add the Menu to it in design mode. We’ll create a new SiteMapDataSource and point the Menu control at it. Since we have an empty starting node, we’ll set SiteMapDataSource.ShowStartingNode = false. (I also changed the menu to use the Classic style and horizontal orientation.) We should now see three nodes: Home, Reports, and Administration in the Menu. If we try to browse the site, we’ll get a 401 – Access Denied because our user account is not in any of the appropriate roles. At least the Web.config files are doing their jobs. Let’s see how to use the ImpostorHttpModule to give ourselves access to these directories without having to create a local or domain group or add our user account to it. (In a deployment situation, you would likely have a domain group that a domain admin would add users to. We’re trying to avoid all the overhead of calling up your friendly neighbourhood domain admin every time you want to test a different security context when accessing your app. Believe me – your domain admin will thank you.)

So let’s add our user, DOMAIN\Foo, to the User, Manager, and Administrator role in the ~/App_Data/Impostors.xml file. (N.B. If you’re logged on using a local account, you’ll need to specify MACHINE\Bar. If you logged in using the alternate user name syntax, foo@example.com, you’ll need to use this in the name.)

<?xml version=”1.0″ encoding=”utf-8″ ?>
<impostors>
    <impostor name=”DOMAIN\Foo” roles=”User, Manager, Administrator”/>
</impostors>

Clicking on the Menu links should allow you to see the Reports and Administration sections. All is good in the world. Now let’s implement security trimming! Our next stop is Web.config where we’ll have to add a new sitemap provider as the default one declared in Machine.config does not have security trimming enabled. The key attribute here is securityTrimmingEnabled=”true”.

<siteMap defaultProvider=”XmlSiteMapProvider” enabled=”true”>
   <providers>
      <add name=”XmlSiteMapProvider” description=”Default SiteMap provider.” type=”System.Web.XmlSiteMapProvider” siteMapFile=”Web.sitemap” securityTrimmingEnabled=”true”/>
   </providers>
</siteMap>

With this change, the menu disappears entirely! We haven’t defined any roles that can see the nodes. So they are all trimmed off. Let’s update the Web.sitemap to add the roles:

<?xml version=”1.0″ encoding=”utf-8″ ?>
<siteMap xmlns=”http://schemas.microsoft.com/AspNet/SiteMap-File-1.0” >
    <siteMapNode roles=”User, Manager, Administrator”>
        <siteMapNode url=”~/Default.aspx” title=”Home”  description=”Return to the Home page” roles=”User, Manager, Administrator” />
        <siteMapNode url=”~/Reports/Default.aspx” title=”Reports” description=”Go to reports” roles=”Manager, Administrator” />
        <siteMapNode url=”~/Admin/Default.aspx” title=”Administration”  description=”Go to site administration” roles=”Administrator” />
    </siteMapNode>
</siteMap>

A quick note is that you must specify all the roles on the empty <siteMapNode>. If you do not, it gets trimmed and none of the child nodes get displayed either. (Took me awhile to realize this the first time I used sitemaps and security trimming.)

Now that we have everything in order, try removing your user account from the Administrator role in ~/App_Data/Impostors.xml and browse to ~/Default.aspx. The Administration link disappears. You can now happily add and remove roles from your user account to test what different kinds of users would see when they browse the site.

In summary, the ImpostorHttpModule allows us to easily test different security configurations for our site without having to change your local or domain group memberships. Since we’re replacing the list of roles that the incoming user is a member of, this technique not only works for testing sitemaps and website security using <authorization>, but it also works for declarative and imperative security demands in code. Full source code is available here. In our next episode, we’ll look at how the ImpostorHttpModule works under the covers.