Wednesday, March 28, 2012

Security Trimmed Top Navigation Menu Using Custom Sitemap Provider for SharePoint

Some time back shared my experience on creating consistent top navigation in SharePoint. Now, added with that, there is a requirement to make the Top navigation Security Trimmed!

Alright, to fulfill this requirement all we need to do is: Create our own sitemap provider which does the job of security trimming. Lets get started!

Brief: Create a Class project in Visual studio, Add references to SharePoint, System.web, System.Configuration. Place the following code in the .CS file, Strong Name it, Compile, Place the DLL to GAC, Update the web.config file. Place your own .sitemap file, Change your master page to link with the custom site map.

SharePoint Security Trimmed Navigation Provider - Source Code:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Utilities;
using Microsoft.SharePoint.Navigation;
using System.Web;
using System.Xml;

namespace BpCustomNavProvider
{
    public class CustomNavProvider : SPXmlContentMapProvider
    {

        public override bool IsAccessibleToUser(HttpContext context, SiteMapNode node)
        {
            try
            {
                if (node == null)
                {
                    throw new ArgumentNullException("node");
                }
                if (context == null)
                {
                    throw new ArgumentNullException("context");
                }
                if (!base.SecurityTrimmingEnabled)
                {
                    return true;
                }
                if (string.IsNullOrEmpty(node.Url))
                {
                    return this.IsGranted(context, node.ChildNodes);
                }
                return this.IsGranted(context, node);
            }
            catch
            {
                return false;
            }
        }


        private bool IsGranted(HttpContext context, SiteMapNode node)
        {
            bool isGranted = false;
            SPSecurity.RunWithElevatedPrivileges(delegate
            {
                using (SPWeb web = new SPSite(SPContext.Current.Site.MakeFullUrl(node.Url)).OpenWeb())
                {
                    SPUser user = web.AllUsers[context.User.Identity.Name];
                    if (user != null)
                    {
                        try
                        {
                            if ((node.Roles != null) && (node.Roles.Count > 0))
                            {
                                foreach (string str in node.Roles)
                                {
                                    isGranted = (str == "*") || (user.Groups[str] != null);
                                    if (isGranted)
                                    {
                                        break;
                                    }
                                }
                            }
                            isGranted = web.DoesUserHavePermissions(user.LoginName, SPBasePermissions.EmptyMask | SPBasePermissions.ViewPages);
                        }
                        catch
                        {
                        }
                    }
                }
            });
            return isGranted;
        }

        private bool IsGranted(HttpContext context, SiteMapNodeCollection childNodes)
        {
            bool flag = false;
            foreach (SiteMapNode node in childNodes)
            {
                flag = this.IsGranted(context, node);
                if (flag)
                {
                    return flag;
                }
                this.IsGranted(context, node.ChildNodes);
            }
            return false;
        }
    }
}

Web.config Change:
In web.config file, Under the <siteMap>   <providers> section add an entry to our custom site map provider:
<add name="BPCorpProvider" siteMapFile="_layouts/BPCorp.sitemap" type="BpCustomNavProvider.CustomNavProvider, BpCustomNavProvider , Version=1.0.0.0, Culture=neutral, PublicKeyToken=c7b63f4b9c6ca821" securityTrimmingEnabled="true"  />



You might also like:
SharePoint Usage Reports
Usage reports, collaboration and audit for SharePoint.
Document SharePoint Farm
Automatically generate SharePoint documentation.
*Sponsored


Check out these SharePoint products:

20 comments :

  1. I get error as:

    The DataSourceID of 'TopNavigationMenuV4' must be the ID of a control of type IHierarchicalDataSource. A control with ID 'topSiteMap' could not be found.

    after I add the second line on the webconfig file. please let me know if I am making any mistakes..

    ReplyDelete
    Replies
    1. Follow my First post step by step:
      http://salaudeen.blogspot.com/2012/01/custom-top-navigation-using-sitemap.html
      to bring custom Navigation control.



      and then build the above code as DLL, Add a Safecontrol Entry to Web.config
      file, That will fix your issue.

      e.g.

      <SafeControl Assembly="BpCustomNavProvider, Version=1.0.0.0,
      Culture=neutral, PublicKeyToken=fc34715927745d1a"
      Namespace="BpCustomNavProvider.CustomNavProvider" TypeName="*" Safe="True" />




      Please note: Your PublicKeyToken will vary!

      Delete
  2. Hi,

    I also receive the same error..

    I have had my own xml site map working for quite some time, wanted to add your security trimming, but it is not working, please help.

    ReplyDelete
    Replies
    1. Hi I got it working your code had to be changed to inherit from a spxmlcontentprovider instead.

      Delete
    2. Hi Chris, Let me know how you have worked it out? I followed all the steps but getting error. It was working before and extended to have security trimming and added following in Web.Config:



      But leading to error page.

      Delete
    3. Hi Chris/All,

      I was able to recreate the first part. when want to add security trimming, it is giving error.

      Here is the web.config tags:






      and code bpCustomNavProvider:

      using System;
      using System.Collections.Generic;
      using System.Linq;
      using System.Text;
      using Microsoft.SharePoint;
      using Microsoft.SharePoint.Utilities;
      using Microsoft.SharePoint.Navigation;
      using System.Web;
      using System.Xml;

      namespace WDCustomNavProvider
      {
      public class CustomNavProvider : XmlSiteMapProvider
      ...


      Tried both xmlsitemapprovider and spxmlcontentprovider but of no use. Pls let me how if I am making any mistake or steps to taken care off.

      Thanks
      Tom

      Delete
  3. Hi Rajack,

    Facing problem when implementing security trimming.
    I have added on top site collection and also on other site collections. Able to see the new topnavigationmenu but the urls are not appropriate.
    Top site collection urls are referenced good. [ example.com/] with other links to site collections.
    Added to the sub site collections as well. if the other site collection is of form example.com/xyz, it is referenced in topNavigationMenu as : example.com/xyz/xyz.
    Can you pls help on this?

    ReplyDelete
  4. Let me describe it again. if I have root site collection and /accounting, /IT, /Marketing etc.. There menu on the root : example.com/ is working fine. But when we are in the /accounting tab, the urls are arranged as example.com/accounting/accounting, example.com/accounting/IT, example.com/accounting/Marketing instead of example.com/accounting, example.com/IT, example.com/Marketing.
    Without Security Trimming, it works fine. but if I replace, it with my custom dll, then the urls are changed to above format.

    Please help me over here!!!

    ReplyDelete
  5. To fix the example.com/xyz/xyz issue use the XmlSiteMapProvider instead of SPXmlContentMapProvider

    ReplyDelete
  6. Thanks, I wish I had read not only the blog but even the replies. This has driven me crazy for a few days. I too inherited from SpXmlContentMapProvider and got those weird URL:s. As soon as I switched to XmlSiteMapProvider everything works as I wanted. And Salaudeen, thanks for a good article. This has really helped me.

    ReplyDelete
  7. I have implemented the dll but the nodes are still showing even when the users dont have any rights to the page/url, for example when they click the node it gives them a access denied. what am i doing wrong, do i have to implement roles

    ReplyDelete
    Replies
    1. Check whether user's granted access to any of the underlying sub-sites/Lists with unique permission! If Yes, That will add a "Limited Access" to Root site collection!

      Delete
  8. thank you for your reply, I checked and he doesnt have any unique permissions. I am adding a link to a calendar and the user i am testing with has no permission granted to this calendar.
    Can you tell me a way to check where it is failing

    ReplyDelete
    Replies
    1. Hi There,

      This method Works ONLY on SPWeb! Not on Individual Items!! So use your sitemap nodes to point SPWeb Objects not Files/Pages.

      Regards,
      Sal

      Delete
  9. Sorry i meant an SPlist which is used as an calendar which is part of the SPSite.

    ReplyDelete
  10. This worked perfectly, however I have some custom pages in the LAYOUTS folder which are being trimmed, anyway to get those displayed?

    ReplyDelete
  11. SPUser user = web.AllUsers[context.User.Identity.Name]; only displays the node if the user is added explicitly, it does not display if they have been added via domain group until they have first visited the site. How can I get the node to display without the user having to visit the site if they are granted access via domain group?

    ReplyDelete
  12. Ive followed these steps, but when I load the page none of my navigation items show up. They worked fine in step one before the security trimming was added.

    ReplyDelete
  13. This has been working really well for me. Recently, I got a request to add a navigation menu item for a department which has the same URL as that of an existing menu item corresponding to another department. But when I do this, the page throws an exception and I cannot even load the page. Can URLs not be repeated when extending SPXmlContentMapProvider?

    ReplyDelete
    Replies
    1. Yes Ashwin, That's the limitation in using this controls! We can't have duplicate URLs as I've stated in my another post: http://www.sharepointdiary.com/2012/01/custom-top-navigation-using-sitemap.html

      Delete

Please Login and comment to get your questions answered!

You might also like:

Related Posts Plugin for WordPress, Blogger...