Twitter Feed Popout byInfofru

OverrideThis.com

Adventures in .NET Software Craftsmanship!

Security Trimming an ASP.NET SiteMapCollection

Yes, I know what everybody is thinking right now, Don't SiteMaps already provide functionality for Security Trimming?   Well they do and they don't.  This functionality is available only if you are using an implementation of the ASP.NET MembershipProvider and RoleProvider.  In the applications that I have to work with in a day to day basis that is rarely the case, I always have to develop a custom security provider using forms or windows authentication that support some far fetched business requirements.   I still however love to use SiteMaps and have rarely seen the need to customize the base SiteMap provider for any other reason.    So, in order to have security trimming and still use and bind the information from my SiteMap I wrote the following block of code that just begged to be re-factored.

Exhibit A - Code that needs (begs) for refactoring.

// do security trimming.
List<SiteMapNode> nodes = new List<SiteMapNode>();
foreach (SiteMapNode node in SiteMap.RootNode.ChildNodes) {
    if (node.Roles.Count == 0) {
        // just add.
        nodes.Add(node);
        continue;
    } else {
        // validate security.
        foreach (var role in node.Roles) {
             if (this.Page.User.IsInRole(role.ToString())) {
                 nodes.Add(node);
                 break;
             }
        }
   }
}

// return list.
this.ListViewTopMenu.DataSource = SiteMap.RootNode.ChildNodes;
this.ListViewTopMenu.DataBind();


I decided that, because I was going to need to reuse this functionality in several places and I wanted to avoid creating a class that would perform this task specifically,  that I should add a new method to the SiteMapCollection class using a C# 3.0 Extension Method. 

Step 1 - Extension Method

public static class SiteMapNodeCollectionExtension {
	
	public static IList FilterByRoles(
		this SiteMapNodeCollection nodeColl, 
		IPrincipal user) {
		
		// collection used to store array values to return to browser
		List nodes = new List();

		// iterate through the node collection to return the valid nodes.
		foreach (SiteMapNode node in nodeColl) {
			if (node.Roles.Count == 0) {
				// just add.
				nodes.Add(node);
				continue;

			} else {
				// validate security.
				foreach (var role in node.Roles) {
					if (user.IsInRole(role.ToString())) {
						// add to collection.
						nodes.Add(node);
						break;
					}
				}
			}
		}
		return nodes;
	}
	
}

 

This blog is not intended to be a tutorial, but I would like to point out the facts that C# Extension methods have to be defined in a static class as a static method and the first parameter must be the class to be extended (Notice the use of the 'this' keyword on the fist parameter).

Step 2 - Refactoring of the client code.

// bind list.
this.ListViewTopMenu.DataSource = SiteMap.RootNode.ChildNodes.FilterByRoles(this.Page.User);
this.ListViewTopMenu.DataBind();

Thank you for reading,

Roberto Hernandez