Firstly, apologies for the rubbish question title.
Based on the example here, I have a menu which displays nav submenu links depending on the users role. This is a line from the mvc.sitemap file. 'userRole' is a custom attribute to indicate which role can see this url.
<mvcSiteMapNode title="Customers" controller="Home" action="Index"
area="Customers" userRole="Customers">
and in BootstrapMenuHelperModel.cshtml (again, based on the above example), I have a line which contains:
if ( User.IsInRole(node.Attributes["userRole"].ToString())
{
...show url
which determines whether the node/nav item item should be displayed.
This all works fine. However, the db is being accessed every time I call User.IsInRole(), and I'd like to avoid this by passing a collection of the user's roles to the View which I can then use to check. The model for the view @model MenuHelperModel
is in MvcSiteMapProvider.dll
which I can't modify as it's a part of a Nuget package.
So the question is: How can I access or somehow pass the current user's roles to the view bearing in mind I can't modify the view's model?
Security trimming is a built in feature of MvcSiteMapProvider. If you are using the AuthorizeAttribute to define which actions have, it will automatically hide the nodes that the user doesn't have access to.
Security trimming is disabled by default. The first thing you need to do is enable it.
Internal DI (web.config):
External DI (in MvcSiteMapProvider module):
Then you should put the MVC [Authorize] attribute on each of the action methods that you want to secure. In MVC4 or higher, it is recommended that you register [AuthoirizeAttribute] globally and then use the [AllowAnonymous] attribute to selectively allow action methods to be allowed by non-authenticated users. This will ensure you have a white list security rather than black list security.
There is nothing special you need to do with your node configuration - the security is done entirely on the Controllers and Actions.
Once this is set up, not only are your links hidden appropriately, but the user will also get the 401 status when navigating to a controller action they don't have access to and MVC will automatically redirect them to the login page.
If what you are doing is trying to reinvent security for MVC, there are other options. But keep in mind this will most likely not be as secure as using the AuthorizeAttribute.
Option 1
If you build the nodes in code (by implementing IDynamicNodeProvider or ISiteMapNodeProvider) rather than using XML to configure your nodes, you can attach any object type you want to the node as a custom attribute. Knowing this, you could potentially create an object that contains logic that is shared between nodes. So you could call a user cache or session object that contains your roles.
Then you can call the logic in your custom object by casting it back to your object type.
Option 2
Build a custom extension method to get the roles.
And then call it from your view.
However you do it, I think the part you are missing is the caching to ensure your database isn't hit on each request. But exactly how long you cache, where you cache, and whether to use cache or session depends on your requirements and application design.