ASP.net: Understanding sitemaps

July 17, 2010 by C#   ASP.NET  

One day Pete the web developer decided to build his friends a website using ASP.net.

He started out by creating a new web solution in his Visual Studio (yes the one he pirated from one of his friends) and added a masterpage to his solution, next he decided to create folders defining all their diverse interests.



Pete felt that navigation is paramount, so he added a sitemap to the website and dropped a menu onto his masterpage where he bound the menu to his sitemap.

<?xml version="1.0" encoding="utf-8" ?>
<siteMap xmlns="http://schemas.microsoft.com/AspNet/SiteMap-File-1.0" >
  <siteMapNode>
    <siteMapNode url="~/Music/Default.aspx" title="Music" />
    <siteMapNode url="~/History/Default.aspx" title="History" />
    <siteMapNode title="Sport">
      <siteMapNode url="~/Sport/Cricket/Default.aspx" title="Cricket"></siteMapNode>
      <siteMapNode url="~/Sport/Rugby/Default.aspx" title="Rugby"></siteMapNode>
      <siteMapNode url="~/Sport/Soccer/Default.aspx" title="Soccer"></siteMapNode>
    </siteMapNode>
  </siteMapNode>
</siteMap>

Pete called his friends over (Jack & Paul) to have a look at his creation, immediately Jack noticed the Soccer menu item and cried out "Pete I am straight I don't watch gay sports"

Pete realised that he needed to add roles to his website (Gay, Straight Roles), so he wacked a MembershipProvider & RoleProvider (not discussed in this post) into his web.config and enabled security trimmings for his sitemap

<siteMap defaultProvider="XmlSiteMapProvider" enabled="true">
      <providers>
        <clear />
        <add name="XmlSiteMapProvider"
         type="System.Web.XmlSiteMapProvider"
         siteMapFile="web.sitemap"
         securityTrimmingEnabled="true" />
      </providers>
    </siteMap>
  </system.web>

Next he altered his sitemap to include the needed roles, but for some reason it didn't work - can you spot the mistake he made?

<?xml version="1.0" encoding="utf-8" ?>
<siteMap xmlns="http://schemas.microsoft.com/AspNet/SiteMap-File-1.0" >
  <siteMapNode roles="*">
    <siteMapNode url="~/Music/Default.aspx" title="Music" />
    <siteMapNode url="~/Fairies/Default.aspx" title="Fairies" roles="Gay" />
    <siteMapNode title="Sport" roles="*">
      <siteMapNode url="~/Sport/Cricket/Default.aspx" title="Cricket"></siteMapNode>
      <siteMapNode url="~/Sport/Rugby/Default.aspx" title="Rugby"></siteMapNode>
      <siteMapNode url="~/Sport/Soccer/Default.aspx" title="Soccer" roles="Gay"></siteMapNode>
    </siteMapNode>
  </siteMapNode>
</siteMap>

For some strange reason the Gay nodes were still showing, even though Jack logged in using his Straight role.



After googling himself on Google for a few hours, Pete decided to actually use Google for something useful and had a look all over the web, to find out if other people are experiencing the same issue.

Pete eventually discovered the problem, one can't define roles on a sitemapnode that include an url - it actually retrieves its visibility/accessibility from a location tag in the web.config. (Which makes sense from a security point of view - what's the use of hiding a page from a menu if we still have access to the page regardless)

So Pete fixed his sitemap by removing all the invalid roles (while wondering why Microsoft didn't simply throw an exception if someone made this mistake)

<?xml version="1.0" encoding="utf-8" ?>
<siteMap xmlns="http://schemas.microsoft.com/AspNet/SiteMap-File-1.0" >
  <siteMapNode roles="*">
    <siteMapNode url="~/Music/Default.aspx" title="Music" />
    <siteMapNode url="~/Fairies/Default.aspx" title="Fairies" />
    <siteMapNode title="Sport" roles="*">
      <siteMapNode url="~/Sport/Cricket/Default.aspx" title="Cricket"></siteMapNode>
      <siteMapNode url="~/Sport/Rugby/Default.aspx" title="Rugby"></siteMapNode>
      <siteMapNode url="~/Sport/Soccer/Default.aspx" title="Soccer"></siteMapNode>
    </siteMapNode>
  </siteMapNode>
</siteMap>

And added the Gay locations to his web.config like this:

</system.web>

<location path="Sport/Soccer">
  <system.web>
    <authorization>
      <allow roles="Gay" />
      <deny users="*" />
    </authorization>
  </system.web>
</location>

<location path="Fairies">
  <system.web>
    <authorization>
      <allow roles="Gay" />
      <deny users="*" />
    </authorization>
  </system.web>
</location>

When Jack logs in, he now only sees the items he wants to see


Note:
If security isn't a concern, one can always alternative simply create a custom SiteMapProvider, where one can enforce the roles attribute on a sitemapnode containing an url .

using System;
using System.Linq;
using System.Web;

namespace CSTruter.Web
{
    public class SiteMapProvider : XmlSiteMapProvider
    {
        public override bool IsAccessibleToUser(HttpContext context, SiteMapNode node)
        {
            if (node.Roles.Count == 0)
            {
                return base.IsAccessibleToUser(context, node);
            }
            else
            {
                return (from role in node.Roles.OfType<String>()
                         where context.User.IsInRole(role) || (role == "*")
                         select role).Count() > 0;
            }
        }
    }
}


Leave a Comment


Thanks May 12, 2011 by Chuck Snyder

Thanks for the info. I had discovered the inablility to use the sitemap roles for hiding entries with url's, but had no idea why it wouldn't work. Simple solution, and only 17 entries to do, should only take an hour or so to get it working. Thanks a lot for taking the time to post this. Chuck