Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to generate sitemap for not current user? #444

Open
thedoctorde opened this issue Jun 10, 2016 · 5 comments
Open

How to generate sitemap for not current user? #444

thedoctorde opened this issue Jun 10, 2016 · 5 comments

Comments

@thedoctorde
Copy link

Hi! I got a question.
I use MvcSiteMapProvider on my site and it works fine! But i cant find solution for the problem: my site have 1 sitemap, but users with different roles (Identity roles) see sitemap in different ways due to security trimming (it is ok). Admin wants to know, what nodes of sitemap is shown to concrete user (with roles A, B, C) or group of users with same roles. Is there any way to render sitemap using only list of roles?

@thedoctorde thedoctorde changed the title How to generate sitemap for not curent user? How to generate sitemap for not current user? Jun 10, 2016
@NightOwl888
Copy link
Collaborator

You can configure the roles on the roles attribute/property of the node. This is generally only recommended for interop with ASP.NET because it essentially means you have to duplicate your roles (once in the AuthorizeAttribute and once in the SiteMap).

<mvcSiteMapNode title="About" controller="Home" action="About" roles="A,B,C">

It is recommended to instead configure the roles on AuthorizeAttribute so your security is defined in only one place in the application. Note that if you need to make advanced role logic, you can subclass AuthorizeAttribute.

[Authorize(Roles = "A,B,C")]
public ActionResult Index()
{
    return View();
}

Also see: http://stackoverflow.com/a/26558590/181087

@thedoctorde
Copy link
Author

thedoctorde commented Jul 21, 2016

Thank you for reply! Your link and issue #102 was very helpful!

I found the way how to generate and show sitemap which contains only nodes accessible for users with roles, which I send to Html Helper.
Implementation of my intention was forced me to turn off security trimming, create custom visibility provider, change sitemap:

  1. Web.config
<add key="MvcSiteMapProvider_SecurityTrimmingEnabled" value="false" />
<add key="MvcSiteMapProvider_DefaultSiteMapNodeVisibiltyProvider" value="MyCompanyName.Helpers.SiteMap.MyCustomVisibilityProvider, MyCompanyName" />
  1. Implement custom visibility provider
public class MyCustomVisibilityProvider : SiteMapNodeVisibilityProviderBase
    {
        private Type[] types;
        public MyCustomVisibilityProvider()
        {
            types = System.Reflection.Assembly.GetExecutingAssembly().GetTypes();
        }
        public override bool IsVisible(ISiteMapNode node, IDictionary<string, object> sourceMetadata)
        {
            try
            {
                string visibility = "";
                var vis = node.Attributes["visibility"];
                if (vis != null) visibility = (string) vis;
                if (string.IsNullOrEmpty(visibility))
                {
                    return true;
                }
                visibility = visibility.Trim();
                var mode = sourceMetadata["mode"] as string;
                switch (visibility)
                {
                    case "ClientConstructor":
                    {
                            if (mode == @"ClientConstructor")
                            {
                                var givenRoles = (sourceMetadata["roles"] as string).Split(',').ToList();
                                givenRoles.Add("");
                                var controller = types.FirstOrDefault(x => x.Name.Contains(node.Controller + "Controller"));
                                if (controller != null)
                                {
                                    var controllerAttributes = controller.GetCustomAttributes(typeof(AuthorizeAttribute), true);
                                    var controllerAttrs = (AuthorizeAttribute) controllerAttributes[0];
                                    var controllerRoles = controllerAttrs.Roles.Replace(" ", "").Split(',').ToList();
                                    if (givenRoles.Any(role => controllerRoles.Contains(role)))
                                    {
                                        if (node.Clickable)
                                        {
                                            return true;
                                        }
                                        var childNodes = node.ChildNodes;
                                        return childNodes == null || childNodes.Any(c => c.IsVisible(sourceMetadata));
                                    }
                                }
                                return false;
                            }      
                            return true;
                    }
                }   
            }
            catch {}
            return true;
        }

    }
  1. Add some changes to Mvc.sitemap (add attribute 'visibility="ClientConstructor"' to every node )
<mvcSiteMapNode title="Main" controller="Home" action="Index" visibility="ClientConstructor">
    <mvcSiteMapNode title="Node0" clickable="false" visibility="ClientConstructor">
      <mvcSiteMapNode title="Node1" action="Index" controller="Administrators" area="Admin" visibility="ClientConstructor"/>
      <mvcSiteMapNode title="Node2" action="Index" controller="Users" area="Admin" visibility="ClientConstructor"/>
      <mvcSiteMapNode title="Node3" action="Index" controller="Clients" area="Admin" visibility="ClientConstructor"/>
    </mvcSiteMapNode>
</mvcSiteMapNode>
  1. Configure html helper in View page (@)
@model string // model contains roles divided by ',' 
@Html.MvcSiteMap().Menu(new
{
    name = "MainMenuHelperModel",
    mode = "ClientConstructor",
    roles = @Model
})

So that way I dynamically change sitemap menu on site. One problem that i dont know how to solve: Is there a way to turn on security trimming in runtime? All pages of my site have such helper for building menu:
@Html.MvcSiteMap().Menu("MainMenuHelperModel")
And I want to make that helper construct sitemap with enabled security trimming.

@NightOwl888
Copy link
Collaborator

Is there a way to turn on security trimming in runtime?

No. And I really don't understand why you would want to, since when you have security trimming enabled it automatically runs the code in the AuthorizeAttribute to see if the user is authorized (which checks whether they are logged in/authenticated).

The way you have made your visibility provider is fine, but it basically means you are duplicating all of the code that is already in the AuthorizeAttribute. If you want to change your security in the future, you have to change both your visibility provider and make a custom AuthorizeAttribute and keep them in sync at all times.

Note that since you are reading the attribute instead of running the filter (which is what actually does the security check) that this approach won't work if you register AuthorizeAttribute globally (which is the recommended way to use AuthorizeAttribute for most applications since new action methods are secure by default). Also, you are missing the check for whether the user is authenticated.

bool isAuthenticated = HttpContext.Current.User.Identity.IsAuthenticated;

If you need Security Trimming to work without actually removing nodes from the API, see #355 for an alternate way to plug in the IAclModule by making it into a visibility provider. Also note that you can register multiple visibility providers per node so you can reuse individual visibility rules as separate pieces that can be combined together in different combinations.

@NightOwl888
Copy link
Collaborator

Is there a way to turn on security trimming in runtime?

No. And I really don't understand why you would want to, since when you have security trimming enabled it automatically runs the code in the AuthorizeAttribute to see if the user is authorized (which checks whether they are logged in/authenticated).

Actually, there is a way to turn it on and off at runtime - see option 2 in this comment on #102. The "special administrative mode" is basically the same thing as toggling security trimming on and off. You would need to set security trimming to true globally, and then you could use this runtime setting to switch on and off the behavior in a wrapper IAclModule.

@thedoctorde
Copy link
Author

And I really don't understand why you would want to, since when you have security trimming enabled it automatically runs the code in the AuthorizeAttribute to see if the user is authorized (which checks whether they are logged in/authenticated).

I try to explain it using a screenshot:
sitemap

But I found some bugs in my implementation (e.g method's authorize attributes are ignored). I will try to understand how to make "special administrative mode" in my provider.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants