-
Notifications
You must be signed in to change notification settings - Fork 218
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
Security Trimmed Node is causing misrendered menu levels #355
Comments
This is an unfortunate side effect of copying the security trimming behavior from Microsoft's original ASP.NET sitemap provider design. In ASP.NET, security was configured using directories so it was not possible to access a page if the user didn't have access to one of the directories above it, so Microsoft decided to replicate this behavior throughout the sitemap provider. In MVC that is no longer the case - security is handled on each action individually, but the same limitation is now still there in MvcSiteMapProvider and the realization that it was over-restrictive for MVC wasn't reached until long after the v4 release. This behavior is something that cannot be changed until the next major version of MvcSiteMapProvider. However, as a workaround you can stop using security trimming and instead use the IAclModule in a custom visibility provider to take advantage of the VisibilityAffectsDescendants behavior. When you set VisibilityAffectsDescendants to false, it will toggle the visibility of each node on or off regardless of the visibility of the ancestor nodes. I have created a demo project showing how this can be done. Note that you must use external DI for this to work. First, create a visibility provider that accepts an IAclModule through its constructor. using System;
using System.Collections.Generic;
using MvcSiteMapProvider;
using MvcSiteMapProvider.Security;
using MvcSiteMapProvider.Web.Mvc;
using MvcSiteMapProvider.Web.Mvc.Filters;
public class AclModuleVisibilityProvider
: SiteMapNodeVisibilityProviderBase
{
public AclModuleVisibilityProvider(
IAclModule aclModule
)
{
if (aclModule == null)
throw new ArgumentNullException("aclModule");
this.aclModule = aclModule;
}
private readonly IAclModule aclModule;
public override bool IsVisible(ISiteMapNode node, IDictionary<string, object> sourceMetadata)
{
return this.aclModule.IsAccessibleToUser(node.SiteMap, node);
}
} Next, make sure both the bool enableLocalization = true;
string absoluteFileName = HostingEnvironment.MapPath("~/Mvc.sitemap");
TimeSpan absoluteCacheExpiration = TimeSpan.FromMinutes(5);
bool visibilityAffectsDescendants = false; // <- Set to false
bool useTitleIfDescriptionNotProvided = true;
bool securityTrimmingEnabled = false; // <- Set to false
string[] includeAssembliesForScan = new string[] { "MvcSiteMapProvider_355" }; Finally, use explicit DI to inject the visibility providers, so you can bundle each one with a AclModuleVisibilityProvider instance. This will ensure the IAclModule will still function everywhere. // Visibility Providers
// Explicitly set the visibility providers, using CompositeSiteMapNodeVisibilityProvider to combine the AclModuleVisibilityProvider
// with all other ISiteMapNodeVisibilityProvider implementations.
this.For<ISiteMapNodeVisibilityProviderStrategy>().Use<SiteMapNodeVisibilityProviderStrategy>()
.EnumerableOf<ISiteMapNodeVisibilityProvider>().Contains(x =>
{
x.Type<CompositeSiteMapNodeVisibilityProvider>()
.Ctor<string>("instanceName").Is("aclAndFilter")
.EnumerableOf<ISiteMapNodeVisibilityProvider>().Contains(y =>
{
y.Type<AclModuleVisibilityProvider>();
y.Type<FilteredSiteMapNodeVisibilityProvider>();
});
// TODO: Add additional combined visibility logic if using custom providers, as shown.
//x.Type<CompositeSiteMapNodeVisibilityProvider>()
// .Ctor<string>("instanceName").Is("aclAndMyCustom")
// .EnumerableOf<ISiteMapNodeVisibilityProvider>().Contains(y =>
// {
// y.Type<AclModuleVisibilityProvider>();
// y.Type<MyCustomVisibilityProvider>();
// });
})
.Ctor<string>("defaultProviderName").Is("aclAndFilter"); If using custom visibility providers, make sure you bundle each one with a AclModuleVisibilityProvider instance as shown above and give it a unique instanceName. You can then use the instanceName to override the defaultProviderName from your nodes. <mvcSiteMapNode title="About" controller="Home" action="About" visibilityProvider="aclAndMyCustom"/>
<mvcSiteMapNode title="Contact" controller="Home" action="Contact" visibilityProvider="aclAndFilter"/>
<!-- Implicitly use the default provider name (in this case aclAndFilter) -->
<mvcSiteMapNode title="Something" controller="Home" action="Something" /> |
Did this solution work for you? |
I have a sitemap with a 4 level structure.
The scenario that's causing trouble is:
Security Trimming excludes the level 2 node (Dictionaries).
The user accesses the page represented by the level 4 node (specifically the CREATE one).
The menu renders incorrectly.
I render the 2 levels of the menu with:
@Html.MvcSiteMap().Menu(1, 2, true)
and
@Html.MvcSiteMap().Menu(2,1,true)
Problem is, in the scenario above, the first statement renders level 4, and the 2nd statement renders nothing.
I would expect to see levels 1 and 2 (minus the trimmed node) as requested, regardless of where the user is currently sitting within the site.
This is similar to a previous issue I submitted, I think.
The text was updated successfully, but these errors were encountered: