Custom UserInRole function ASP .Net C# Default Sitemap and Second Sitemap

Experts here helped me a while back to create a custom site map left navigation menu (see code below implemented).  I now have a requirement to filter the bulleted menu list by role as defined in the site map.  This question has two parts:

1. How to trim out sitemap items from where the user is NOT in a role specified in the site map (if a role is specified)
2. How to repeat this code to generate a second navigation from a second sitemap listed in the web.config referenced as "secondmenu"

Here are the details of the code in the master.cs file

Sample sitemap
<?xml version="1.0" encoding="utf-8"?>
<siteMap>
	<siteMapNode>
		<siteMapNode url="~/default.aspx" title="Home" description="This is the Home Page for the site" roles="" />
		<siteMapNode url="~/test.aspx" title="CPO" description="another test" roles="Test" />
	</siteMapNode>
</siteMap>

Open in new window


Code added below to working code, but this does not correctly remove the bullet item from the list
                    var noderoles = new List<string> { node["roles"] };
                    var userRoles = Roles.GetRolesForUser(HttpContext.Current.User.ToString());

                    if ((!(HttpContext.Current.User.Identity.IsAuthenticated) && node["roles"] != null && userRoles.Any(u => noderoles.Contains(u))))

Open in new window


Here is the working code (page load) to create the bulleted list from the site map with the code added above.  The bulleted list is still created, it just doesn't remove the menu items that the user's role list doesn't match for that item.  Security is added for each page so the user couldn't get to it if they wanted to, the point is to NOT list menu items that they don't have access to.

        if (SiteMap.Provider.CurrentNode != null) 

        {
            if (SiteMap.Provider.CurrentNode.ResourceKey == "parent")
            {
                HtmlGenericControl li = new HtmlGenericControl("li");
                HtmlGenericControl anchor = new HtmlGenericControl("a");

                if (SiteMap.Provider.CurrentNode.ParentNode.Title != "Home")
                {
                    li = new HtmlGenericControl("li");
                    navUL.Controls.Add(li);
                    anchor = new HtmlGenericControl("a");
                    anchor.Attributes.Add("href", SiteMap.Provider.CurrentNode.ParentNode.Url.Replace("/default.aspx", "/"));

                    if ((SiteMap.Provider.CurrentNode.ParentNode.Title != "Home") & (SiteMap.Provider.CurrentNode.ParentNode.Title != ""))
                    {
                        anchor.InnerText = SiteMap.Provider.CurrentNode.ParentNode.Title.Replace("Home", Banner().Replace("Main ","") + " Home");
                    }
                    else
                    {
                        anchor.InnerText = Banner().Replace("Main ","") + " Home";
                    }

                    li.Controls.Add(anchor);
                }


                li = new HtmlGenericControl("li");
                navUL.Controls.Add(li);
                anchor = new HtmlGenericControl("a");
                anchor.Attributes.Add("href", SiteMap.Provider.CurrentNode.Url.Replace("/default.aspx", "/"));
                anchor.InnerText = SiteMap.Provider.CurrentNode.Title.Replace("Home", Banner().Replace("Main ","") + " Home"); //not here
                anchor.Attributes.Add("class", "current");
                li.Controls.Add(anchor);


                foreach (SiteMapNode node in SiteMap.Provider.CurrentNode.ChildNodes)
                {

                    var noderoles = new List<string> { node["roles"] };
                    var userRoles = Roles.GetRolesForUser(HttpContext.Current.User.ToString());

                    if ((!(HttpContext.Current.User.Identity.IsAuthenticated) && node["roles"] != null && userRoles.Any(u => noderoles.Contains(u))))
                     //if ((!(HttpContext.Current.User.Identity.IsAuthenticated) && node["members"] != null && node["members"].Equals("true", StringComparison.CurrentCultureIgnoreCase)) | (node.ResourceKey != "hide"))
                        {


                        li = new HtmlGenericControl("li");
                        navUL.Controls.Add(li);
                        anchor = new HtmlGenericControl("a");
                        anchor.Attributes.Add("href", node.Url.Replace("/default.aspx", "/"));
                        anchor.InnerText = node.Title.Replace("Home", Banner().Replace("Main ","") + " Home");
                        if (node.Title == DateTime.Today.ToString("MMMMM"))
                            anchor.Attributes.Add("class", "current");
                            li.Controls.Add(anchor);
                    }
                }
            }

            else
            {
                HtmlGenericControl li = new HtmlGenericControl("li");
                HtmlGenericControl anchor = new HtmlGenericControl("a");
                if ((SiteMap.Provider.CurrentNode.ParentNode.Url != "") & (SiteMap.Provider.CurrentNode.ParentNode.Url != "~/default.aspx"))
                {
                    navUL.Controls.Add(li);
                    anchor.Attributes.Add("href", SiteMap.Provider.CurrentNode.ParentNode.Url.Replace("/default.aspx", "/"));
                    anchor.InnerText = SiteMap.Provider.CurrentNode.ParentNode.Title.Replace("Home", Banner().Replace("Main ","") + " Home");
                    li.Controls.Add(anchor);
                }
                foreach (SiteMapNode node in SiteMap.Provider.CurrentNode.ParentNode.ChildNodes)
                {
                    if (node.Title == "Home")
                    {
                        li = new HtmlGenericControl("li");
                        navUL.Controls.Add(li);
                        anchor = new HtmlGenericControl("a");
                        anchor.Attributes.Add("href", "../default.aspx");
                        anchor.InnerText = "Referring Page";
                        li.Controls.Add(anchor);
                    }

                    if ((!(HttpContext.Current.User.Identity.IsAuthenticated) && node["members"] != null && node["members"].Equals("true", StringComparison.CurrentCultureIgnoreCase)) | (node.ResourceKey != "hide"))
                    {
                        li = new HtmlGenericControl("li");
                        navUL.Controls.Add(li);
                        anchor = new HtmlGenericControl("a");
                        anchor.Attributes.Add("href", node.Url.Replace("/default.aspx", "/"));
                        anchor.InnerText = node.Title.Replace("Home", Banner().Replace("Main ","") + " Home").Replace("Secure Folder", Banner().Replace("Main ","") + " Secure Folder");//recent change
                        if (node.Url == SiteMap.Provider.CurrentNode.Url)
                            anchor.Attributes.Add("class", "current");
                            li.Controls.Add(anchor);
                    }
                }
            }
        }

        else
        {
            HtmlGenericControl li = new HtmlGenericControl("li");
            HtmlGenericControl anchor = new HtmlGenericControl("a");
            li = new HtmlGenericControl("li");
            navUL.Controls.Add(li);
            anchor = new HtmlGenericControl("a");
            anchor.Attributes.Add("href", "default.aspx");
            anchor.InnerText = Banner().Replace("Main ","") + " Home";
            li.Controls.Add(anchor);

        }

Open in new window

atljarmanAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

atljarmanAuthor Commented:
0
guru_samiCommented:
If you are using default XMLSiteMapProvider, you can set the securityTrimmingEnabled="true" like shown here: http://msdn.microsoft.com/en-us/library/ms178428.aspx
0
atljarmanAuthor Commented:
When I do that, I get this error:

Parser Error Message: The entry 'ThisSiteMap' has already been added.

Source Error:


Line 29:     <siteMap enabled="true" defaultProvider="ThisSiteMap"  >
Line 30:       <providers>
Line 31:         <add name="ThisSiteMap" type="System.Web.XmlSiteMapProvider" siteMapFile="web.sitemap"  securityTrimmingEnabled="true" />

I tried to remove the name (http://stackoverflow.com/questions/6679233/the-entry-has-already-been-added-error) but that didn't work.
0
Exploring SQL Server 2016: Fundamentals

Learn the fundamentals of Microsoft SQL Server, a relational database management system that stores and retrieves data when requested by other software applications.

guru_samiCommented:
Make sure there isn't any other provider with same name.
Here's how you clear other providers...
<siteMap enabled="true" defaultProvider="ThisSiteMap"  >
<providers> 
<clear />
<add name="ThisSiteMap" type="System.Web.XmlSiteMapProvider" siteMapFile="web.sitemap"  securityTrimmingEnabled="true" />
</siteMap>

Open in new window

0
atljarmanAuthor Commented:
No luck.  There isn't any other sitemap anywhere defined, so I'm not sure how it could be an error.

At any rate, I'm not necessarily trimming the sitemap per say.   I'm trying to collect the roles from the individual node then compare them to the current user's roles.  If the current user roles is not included in the role list for that sitemap node, then exclude the item from the menu.  I think that is a little different but similar to the security trimming.  One requirement is that users to no have access, for security trimming, and this way, even if the user has access should remove the item from the bulleted list and therefore the menu.
0
guru_samiCommented:
How are you managing/retrieving the roles? Do you have Roles provider enabled in web.config?
User.IsInRole should work if you are using asp.net roles provider or if you are adding the roles to UserPrincipal.
0
atljarmanAuthor Commented:
What I there are multiple roles in the sitemap node roles field?  Userinre only covers one role right?
0
atljarmanAuthor Commented:
Sorry. Autocorrect in iPhone.  UserInRole cover only one role.
0
guru_samiCommented:
Ok I see...with your code do you see these two  lists populated correctly?
var noderoles = new List<string> { node["roles"] };
var userRoles = Roles.GetRolesForUser(HttpContext.Current.User.ToString());
0
atljarmanAuthor Commented:
That's the part I can't seem to figure out.  When I'm not authenticated or using a user name that is not associated with the role for the site map node, the current code still includes the menu item. Unused to use a members variable and it it were true I wouldn't include it unless the user was a member of the site.  That if statement is commented out with //   So now the question is how to compare the user role list with the list of roles stored for the site map node.  I think it would be good to set the users roles once higher above so that the server isn't checking the users role for every node.  Could take a long time depending on how big the site map is.
0
atljarmanAuthor Commented:
Just trying to get it to work first
0
guru_samiCommented:
Correct but what I am trying to understand is what values are populated into noderoles and userRoles variables? Are there any values or empty /null?
Can you set breakpoints and debug through the code.
0
atljarmanAuthor Commented:
Not sure what you mean.   I guess I can create a server variable that could display the values.
0
guru_samiCommented:
-Set the breakpoint on line 44 in code in your very first post above i.e.
var noderoles = new List<string> { node["roles"] };
                    var userRoles = Roles.GetRolesForUser(HttpContext.Current.User.ToString());

Then run the website in debug mode in Visual Studio. The control will stop at line 44. At this point you can hover the mouse over the variables to check their values.
0
atljarmanAuthor Commented:
I added this and the server tag seems empty.
                    var noderoles = new List<string> { node["roles"] };
                    var userRoles = Roles.GetRolesForUser(HttpContext.Current.User.ToString());


                    sitemapmessage.Text =  sitemapmessage.Text+ " noderoles: " + noderoles + " userRoles: " + userRoles;

Open in new window


seems like I'm going at it all wrong.
0
atljarmanAuthor Commented:
Get the roles in the node, compare them to the user roles of the current user.  If one of the items is in the other list, then it should be true.  know multiple other languages to do this.
0
guru_samiCommented:
I am not sure of the exact problem but here are two ways  I would suggest to debug.
1: Modify your noderoles like below and use this code to output the values. Add the following code in the two foreach loops you have.
var noderoles = node.Roles;
foreach (string rol in noderoles)
{
     sitemapmessage.Text += rol + ",";
}

Open in new window

2: On on of your secured page i.e. the one requires roles add this code:
var userRoles = Roles.GetRolesForUser(HttpContext.Current.User.ToString());
foreach (string rol in userRoles)
 {
                        Label2.Text += rol + ",";
 }

Open in new window

The very intention is just to see if the roles are fetched correctly. Comparing roles won't work until the roles are fetched properly.
0
atljarmanAuthor Commented:
guru, I will try that tonight.  I agree 100% and will try your approach.  mine clearly wasn't working and hope yours will.
0
atljarmanAuthor Commented:
I got the node values , I added the 2 next to the value and a 3 next to the user roles.  

                        var noderoles = node.Roles;
                        foreach (string rol in noderoles)
                        {
                             sitemapmessage.Text += rol + "2 ,";
                        }
                        var userRoles = Roles.GetRolesForUser(HttpContext.Current.User.ToString());
                        foreach (string rol in userRoles)
                         {
                                                Label2.Text += rol + "3 ,";
                         }

Open in new window

0
atljarmanAuthor Commented:
ok.  This worked for both... what next...

                        var noderoles = node.Roles;
                        foreach (string rol in noderoles)
                        {
                             sitemapmessage.Text += rol + "2 ,";
                        }
                        var userRoles = Roles.GetRolesForUser();
                        foreach (string rol in userRoles)
                         {
                                                Label2.Text += rol + "3 ,";
                         }

Open in new window


thanks for your help so far.
0
guru_samiCommented:
I was able to implement something similar with following bold changes:

var noderoles = node.Roles;
var userRoles = Roles.GetRolesForUser(HttpContext.Current.User.ToString());

if ((HttpContext.Current.User.Identity.IsAuthenticated) && userRoles.Any(u => noderoles.Contains(u)))
0
atljarmanAuthor Commented:
Ok.  Tried that and it removes all the nodes.  Roles could be empty or missing such as for public nodes?  Any thoughts?
0
guru_samiCommented:
What happens to the node that has roles and when user is logged-in? Does it work for that?

Might need some adjustment to that if statement.Try this:
if (node.Roles==null ||((HttpContext.Current.User.Identity.IsAuthenticated) && userRoles.Any(u => noderoles.Contains(u)))) 

Open in new window

0
atljarmanAuthor Commented:
Ok.  I moved the var up to the top of the script for the user permissions so it was assigned once, ran the debug script and they were assigned.  I also added the node title to the debug script for each of the node permissions.  There are currently three nodes, one with 1 permission listed the others with no permissions.  Every node has the roles variable defined in the site map.  The output on the page correctly showed the current user permissions.  The output for the nodes showed the first two (with no role in the roles space) as not having a role, and the other correctly showing the role in the site map node.

Using the above that you provided, only the role that has the permission shows.  Just out of curiosity, I commented out the if statement all together just to verify that the nodes would show up and indeed they did.  So then I created a if statement just with if {node.Roles ==null} and that did NOT display the sitemap nodes in the menu.  My guess is there is some value assigned to node.Roles the null comparison is not firing correctly.
0
atljarmanAuthor Commented:
I'm feeling a bit at a loss right now how to correctly complete this if then sequence as changing the first if statement to the below shows them all:

                    if (node.Roles != null)

Open in new window


This, to me, means that under the current coding, there always has to be an assigned value in the roles variable in the sitemap node or the node will not show (or they all will need to).  I know there is logic to work around this, I'm just not coming up with it at the moment.
0
atljarmanAuthor Commented:
Now I changed my sitemap so that if the roles are not defined the variable roles does not appear in the site map. Still node.Roles != null does not work.
0
atljarmanAuthor Commented:
Ok.  Any idea on how to do the same thing with another registered site map?  This is using the default.

What worked is I went back to my xsl that created the sitemap and added a variable public.  If there weren't any roles, then I added a variable public='yes'   Then I changed the if then to this:

                    if (node["public"] != null || ((HttpContext.Current.User.Identity.IsAuthenticated) && userRoles.Any(u => noderoles.Contains(u))))          

Open in new window

0
guru_samiCommented:
I was away from computer to test it...node.Role won't be null any time. So you can check  if node.Roles.Count==0
 if (node.Roles.Count==0 || ((HttpContext.Current.User.Identity.IsAuthenticated) && userRoles.Any(u => noderoles.Contains(u))))  

Open in new window

0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
atljarmanAuthor Commented:
I will test shortly.  Did you have thoughts about how to go about doing the same menu generation for the horizontal nav using another named (in the web.config) sitemap file.  I think that this works becuase it is the default sitemap provider, just not sure if it will work on another sitemap provider or how to provide the instruction to do so.  I will report back on your last suggestion shortly.
0
guru_samiCommented:
In your web.config, you will have to declare another provider that points to your another .sitemap
<siteMap enabled="true" defaultProvider="ThisSiteMap"  >
<providers> 
<clear />
<add name="ThisSiteMap" type="System.Web.XmlSiteMapProvider" siteMapFile="web.sitemap"  securityTrimmingEnabled="true" />
<add name="SiteMapProvider2" type="System.Web.XmlSiteMapProvider" siteMapFile="web2.sitemap"  securityTrimmingEnabled="true" />
</siteMap>

Open in new window


Then you can access if by name: SiteMap.Providers["SiteMapProvider2"]
If you just use SiteMap.Provider, it will point to the defaultProvider"TheSiteMap"
0
atljarmanAuthor Commented:
I get object reference not set to an instance ...  on

if ((SiteMap.Providers["sm"].CurrentNode.ParentNode.Url != "") & (SiteMap.Providers["sm"].CurrentNode.ParentNode.Url != "~/default.aspx"))

Open in new window

0
guru_samiCommented:
Did the roles thing work. Let's deal with one thing at a time.
0
atljarmanAuthor Commented:
Sorry.... Yes.  That worked.  My solution worked and your solution worked.  Once we solve this problem I will go back and reconfigure my sitemap generator so it doesn't include public="yes" as it is redundant with using node.Roles.Count == 0

I'm giving you the credit given your assistance and a better solution.  Just trying to get to the end of this task.
0
guru_samiCommented:
It's either the CurrentNode is null or the ParentNode. Check for null on those two before accessing the Url.
CurrentNode: If no representative node exists for the page in the site map information, or if security trimming is enabled and the node cannot be returned for the current user, null is returned.

ParentNode: Returns the parent SiteMapNode; otherwise, null, if security trimming is enabled and the user cannot view the parent node.
0
atljarmanAuthor Commented:
I used this to generate the horizontal navigation: http://runtingsproper.blogspot.com/2009/11/i-bet-you-didn-know-that-adding-custom.html?m=1  from a secondary navigation.  I used the node code in the for each to remove the information based on roles just like in the code you provided.
0
atljarmanAuthor Commented:
Thanks for being patient and working through this issue with me.
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
ASP.NET

From novice to tech pro — start learning today.