MvcSiteMapProvider for MVC5 is not picking up the roles to show menu based on user role. When security trimming is enabled it shows only menu items which do not have any roles attribute defined.
As per the documentation, the roles attribute is not for use with MVC. It is for interop with ASP.NET pages.
It only functions when the security framework implements IPrincipal
and IIdentity
as Membership and Identity do.
I have ActionFilterAttribute implemented and used on controller for authorization. Controller correctly redirects the user to unauthorized page based on roles but menu is not picking the role attributes to hide the menu.
This is most likely your issue. Security trimming only looks for AuthorizeAttribute
and subclasses of AuthorizeAttribute
. If you have subclassed ActionFilterAttribute
, it will not be used to hide links from navigation.
Of course, for the standard AuthorizeAttribute
to work, you need to implement IPrincipal
and IIdentity
or use one of the pre-built security frameworks that does the same.
Alternatively, you could build your own IAclModule
or ISiteMapNodeVisibilityProvider
if you have completely custom security.
Also want to know where to look for roles attributes in SiteMapNodeModel. I am thinking of customizing to look for permission in the HtmlHelper while building the menu.
You would not need to look for roles from the SiteMapNodeModel
. Instead, you should get the roles from the current context and make the changes to the menu templates in /Views/Shared/DisplayTemplates/
accordingly.
If you are using a framework that supports IPrincipal
and IIdentity
, you could just use:
@if (User.IsInRole("SomeRole"))
{
...
}
Also see the following:
If you want to get the current roles that are configured for an action method, you can build an extension method to read the roles from the current AuthorizeAttribute
. Again, the roles
attribute is only for interoperability with ASP.NET and should not be used for pure MVC, since it means you need to duplicate your roles on AuthorizeAttribute
anyway.
public static class ControllerContextExtensions
{
public static IEnumerable<string> Roles(this ControllerContext controllerContext)
{
var controllerType = controllerContext.Controller.GetType();
var controllerDescriptor = new ReflectedControllerDescriptor(controllerType);
var actionName = controllerContext.RouteData.Values["action"] as string;
var actionDescriptor = controllerDescriptor.FindAction(controllerContext, actionName);
var authorizeAttribute = FilterProviders.Providers.GetFilters(controllerContext, actionDescriptor)
.Where(f => typeof(AuthorizeAttribute).IsAssignableFrom(f.Instance.GetType()))
.Select(f => f.Instance as AuthorizeAttribute).FirstOrDefault();
string[] roles = { };
if (authorizeAttribute != null && authorizeAttribute.Roles.Length > 0)
{
roles = Array.ConvertAll(authorizeAttribute.Roles.Split(','), r => r.Trim());
}
return roles;
}
}
Usage
In view:
{ var roles = this.ViewContext.Controller.ControllerContext.Roles(); }
In controller:
var roles = this.ControllerContext.Roles();
To get the roles from the SiteMapNodeModel
:
var siteMap = MvcSiteMapProvider.SiteMaps.Current;
var siteMapNode = siteMap.FindSiteMapNodeFromKey(SiteMapNodeModel.Key);
var roles = siteMapNode.Roles;