It seems like forever ago that I started recommending Fredrik Normen's Permission Manager. Recently I was tasked to find a way to restrict nodes in a SiteMap based on Permission. Today I completed that task and thought I would share it with the world. First of all, I appoligize for writing the code in VB.NET (employers standard), but you can all compile it and reference it from C# so get over it.
What I did to create the PermissionXmlSiteMapProvider was to inherit directly from XmlSiteMapProvider and then extend 2 methods. First of all I extended the Initialize method so that I could verify that it was configured properly. Since it is dependent on the presence of Permission Manager and it properly working I added a simple check to verify that it has a provider.
The second method to override for the addtion of Permission to a SiteNode was IsAccessibleToUser. Thankfully the siteNode can have extra attributes on it without complaint. I used that to check for the permission attribute. If it is there then a call to Permission Manager is done for the named permission and the currently logged in user.
After configuring my applications to use my new Provider I restrict access to nodes as easy as this:
<
siteMapNode url="Edit.aspx" title="Edit" permission="Modify" />
Since I built on top of the XmlSiteMapProvider I also get the ability to do Roles restriction and a combination of Roles and permission:
<
siteMapNode url="Delete.aspx" title="Delete" Roles="Admins, Editors" permission="Delete" />
It is important to note a few things that I ran into while working on this solution. You must enable Security Trimming. If you will restrict based on Roles you also must enable RoleManager. Restricting via Roles is a little quirky as described in the MSDN Site Map Providers document. Specifically review the Security Trimming section.
Here is the siteMap section of Web.config that implements the PermssionXmlSiteMapProvider:
<
siteMap enabled="true" defaultProvider="PersmissionXmlSiteMapProvider">
<providers>
<add name="PersmissionXmlSiteMapProvider"
type="IDCL.Web.Security.PersmissionXmlSiteMapProvider, IDCL.Web"
description="Permission xml site map provider"
securityTrimmingEnabled="true"
siteMapFile="Web.sitemap"/>
providers>
siteMap>
Obviously you also need to have Permission Manager installed and configured or this will not work.
Here is the code for the provider:
Imports
System
Imports System.Web
Imports System.Configuration.Provider
Imports System.Web.Configuration
Imports Nsquared2.Security
Public Class PersmissionXmlSiteMapProvider
Inherits XmlSiteMapProvider
Public Overrides Sub Initialize(ByVal name As String, ByVal config As System.Collections.Specialized.NameValueCollection)
' Verify that config isn't null
If config Is Nothing Then
Throw New ArgumentNullException("config")
End If
' Verify that Permission Manager is configured
If PermissionManager.Provider Is Nothing Then
Throw New ProviderException("Permission Manager default provider missing.")
End If
' Add a default "description" attribute to config if the
' attribute doesn't exist or is empty
If String.IsNullOrEmpty(config("description")) Then
config.Remove("description")
config.Add("description", "PermissionXmlSiteMap provider")
End If
' Call the base class's Initialize method
MyBase.Initialize(name, config)
End Sub
Public Overrides Function IsAccessibleToUser(ByVal context As System.Web.HttpContext, ByVal node As System.Web.SiteMapNode) As Boolean
Dim authorized As Boolean = MyBase.IsAccessibleToUser(context, node)
'Pull the permission out of the sitemap node as an attribute
Dim Permission As String = node.Item("permission")
If Not Permission Is Nothing And authorized Then
If context.User.Identity.IsAuthenticated Then
' Only check permission for known users
Return PermissionManager.HasUserPermission(context.User.Identity.Name, Permission)
Else
' The presence of a permission requires a known user
Return False
End If
End If
Return authorized
End Function
End Class
The PermissionSiteMapProvider.zip file with this article includes the Provider solution and a sample web.config