Search code examples
asp.net-mvc-3asp.net-membership

ASP.Net MVC Hide/Show Menu Items Based On Security


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 are in.

This currently works using code like this

@if (HttpContext.Current.User.Identity.IsAuthenticated)
{
   <li id="MyLearningTab">@Html.ActionLink("My Learning", "MyLearning", "Learning")</li> 
   if (HttpContext.Current.User.IsInRole("Reporters"))
   {
      <li id="ReportTab">@Html.ActionLink("Reports", "Index", "Reports")</li>
   }
   if (HttpContext.Current.User.IsInRole("Administrators"))
   {
      <li id="DashboardTab">@Html.ActionLink("Dashboard", "Dashboard", "Admin")</li>
      <li id="AdminTab">@Html.ActionLink("Admin", "Index", "Admin")</li> 
   }
}

I'd like to refactor this in to something more readable and came up with something like this

@if ((bool)ViewData["MenuMyLearning"]){<li id="MyLearningTab">@Html.ActionLink("My Learning", "MyLearning", "Learning")</li> }    
@if((bool)ViewData["MenuReports"]){<li id="ReportTab">@Html.ActionLink("Reports", "Index", "Reports")</li>}
@if ((bool)ViewData["MenuDashboard"]){<li id="DashboardTab">@Html.ActionLink("Dashboard", "Dashboard", "Admin")</li>}
@if ((bool)ViewData["MenuAdmin"]){<li id="AdminTab">@Html.ActionLink("Admin", "Index", "Admin")</li>}

I originally added the following to my base controller constructor thinking I could setup the ViewData for these properties there

ViewData["MenuDashboard"] = User != null && User.Identity.IsAuthenticated && User.IsInRole("Administrators");
ViewData["MenuAdmin"] = User != null && User.Identity.IsAuthenticated && User.IsInRole("Administrators");
ViewData["MenuReports"] = User != null && User.Identity.IsAuthenticated && User.IsInRole("Reportors");
ViewData["MenuMyLearning"] = User != null && User.Identity.IsAuthenticated;

However it turns out the User object is null at this point in the lifecycle. I've also tried creating a custom global filter but the ViewData is then not accessable.

What is the recommended way of doing something like this? Should I just leave it how it was at first with all the HttpContext code in the view?


Solution

  • 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>}