Open thedoctorde opened 8 years ago
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();
}
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:
<add key="MvcSiteMapProvider_SecurityTrimmingEnabled" value="false" />
<add key="MvcSiteMapProvider_DefaultSiteMapNodeVisibiltyProvider" value="MyCompanyName.Helpers.SiteMap.MyCustomVisibilityProvider, MyCompanyName" />
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;
}
}
<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>
@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.
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.
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
.
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:
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.
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?