I\'m working on an ASP.Net MVC 3 site. The _Layout master view contains a menu and I want to hide some of the items in the menu based on if you are logged in and what roles you
General advice about roles
The way I have done this is to create a custom principal and to store the extra required information in there. In your example, this would at least include the roles for the user. That way you avoid making lots of extra trips to the user store (which is likely a SQL database).
Have a look a this question of mine in which I give the code which I am using successfully: Is this Custom Principal in Base Controller ASP.NET MVC 3 terribly inefficient?
Note that I am storing the custom principal in the cache rather than in the session (just being paranoid about session hijacking).
I like this approach as it is very extensible. For example, I have since extended this to expose Facebook credentials for when the user logs in via Facebook.
Just remember that if you are caching data you need to remember to update it when it changes!
Answer to your question
Just to add, in your specific case, you should probably store this extra information in a ViewModel
and then your view would say things like:
@if(ShowReports) { <li id="ReportTab">@Html.ActionLink("Reports", "Index", "Reports")</li> }
@if(ShowDashboard) { <li id="DashboardTab">@Html.ActionLink("Dashboard", "Dashboard", "Admin")</li> }
@if(ShowAdmin { <li id="AdminTab">@Html.ActionLink("Admin", "Index", "Admin")</li> }
with the ViewModel Code saying something like:
public bool ShowReports {get;set;}
public bool ShowDashboard {get;set;}
public bool ShowAdmin {get;set;}
public void SetViewModel()
{
if (User.Identity.IsAuthenticated)
{
if (HttpContext.Current.User.IsInRole("Reporters"))
{
ShowReports = true;
}
if (HttpContext.Current.User.IsInRole("Administrators"))
{
ShowDashboard = true;
ShowAdmin = true;
}
}
}
I actually tend to take this one step further and create a ReportsLink
in my ViewModel
and set it to contain the link if the user is authorised or to be an empty string if they are not. Then the view just says:
@Model.ReportsLink
@Model.DashboardLink
@Model.AdminLink
In that case the pertinent part of the ViewModel might be like this:
ReportLink = new MvcHtmlString(HtmlHelper.GenerateLink(HttpContext.Current.Request.RequestContext, System.Web.Routing.RouteTable.Routes, "linktext", "routename", "actionname", "controllername", null, null));
Create a partial view and return the view from the controller:
LayoutViewModel.cs:
public class LayoutViewModel
{ ...
public bool ShowAdmin { get; set; }
}
LayoutController.cs:
public PartialViewResult GetAdminMenu()
{
LayoutViewModel model = new LayoutViewModel();
model.ShowAdmin = userHasPermission("Admin"); // change the code here
return PartialView("_AdminMenu", model);
}
_AdminMenu.cshtml (partial view):
@model DelegatePortal.ViewModels.LayoutViewModel
@if (@Model.ShowAdmin)
{
<!-- admin links here-->
}
_Layout.csthml (main view):
...
@Html.Action("GetAdminMenu", "Layout")
Here is what I ended up doing. I created a helper class called MenuSecurity with static boolean properties for each menu item showing which items should be visible. Each property looked like this
public static bool DashboardVisible
{
get
{
return
HttpContext.Current.User != null &&
HttpContext.Current.User.Identity.IsAuthenticated;
}
}
I then tidied up my menu partial view to look like this
@if (MenuSecurity.ReportsVisible){<li id="ReportTab">@Html.ActionLink("Reports", "Index", "Reports")</li>}
@if (MenuSecurity.DashboardVisible){<li id="DashboardTab">@Html.ActionLink("Dashboard", "Dashboard", "Admin")</li>}
@if (MenuSecurity.AdminVisible){<li id="AdminTab">@Html.ActionLink("Admin", "Index", "Admin")</li>}