views:

118

answers:

4

I'm developing a website in ASP.NET MVC where I would like to show different sections of a view for users with different security levels. In essence, the views are the same, but users with higher levels of security must be able to see sections that shouldn't be seen by users with security levels above of, for example, administrators.

I don't know how to do this in an object oriented way. I think this can be done with inheritance, but I don't know how to implement inheritance in the view.

Also, I know I can code a lots of ifs, doing something like

<% if (User has some security level) { %>
   <span>show this info</span>
<% } %>

but this doesn't smell well. The fact is that I don't know how to use object oriented principles or design for this task.

I think this is a common task, so I think there is a pattern to accomplish this task. For example, Stackoverflow does this when shows some options (edit, delete, etc) for the user who posted a question (or answer or comment) and hides the same options to everybody else.

A: 

Depending on the complexity of what you are doing, the if statement route may be sufficient. If not, then you could look at using partial views and write an HtmlHelper extension that allow you to render a partial based on a particular role. It might look something like this:

<% Html.RenderPartialWithRole( "AdminSection",
                               Model,
                               ViewData,
                               User,
                               "Administrator",
                               null ) %>


public static void RenderPartialWithRole( this HtmlHelper helper,
                                          string partialName,
                                          object model,
                                          ViewDataDictionary viewData,
                                          IPrincipal user,
                                          string role,
                                          object htmlAttributes )
{
     if (user != null && !string.IsNullOrEmpty(role) && user.IsInRole(role))
     {
          helper.RenderPartial( partialName, model, viewData, htmlAttributes );
     }
}
tvanfosson
A: 

Using a strongly typed view, have a Boolean property on your model like ShowSection (one for each section, logic to set in your controllor). Have the sections in divtags with good Ids. Then use JavaScript or jquery to set the div tags display style based on the Boolean property.

Carlton Jenke
This is not object oriented, and if the user has javascript disabled doesn't work
eKek0
I agree with eKek0. In addition, it is my strong belief that Views should have no logic whatsover. In other words, the code embedded in a view should have a Cyclomatic Complexity of 1; i.e. no 'if' statements.
Mark Seemann
I think you are too concerned about doing it OO - your view should be dumb and all logic be in the controller, just passing the info needed through a model
Carlton Jenke
A: 

You could have all the sections in partials, with views built to include the Sections available to the different permission levels. So one for admins, one for every level. Then your Controller has the logic to decide which view to use. So any OO part would be in the controller, not the view.

Carlton Jenke
A: 

This isn't an object-oriented approach, but it's related. The interesting part of this question is how to get ride of the if-statements. The usual method to get rid of if's or case's is to use a lookup table of criteria and effects. There are other techniques that use this same idea, such as data-directed programming (http://en.wikipedia.org/wiki/Data-directed___programming) and dispatch tables (http://en.wikipedia.org/wiki/Dispatch_table). Many language implementations use type dispatch tables to implement virtual method calls.

Assuming partial views are OK for this problem, the lookup table could be a list of pairs. The first element of the pair is a role name to check against the current user. The second element of the pair is the name of teh partial view to render when the role check succeeds.

We initialize the table in the controller (or whereever) and assign it to ViewData, then use Html.RenderViewByRole to choose and render the correct partial view:

<% Html.RenderPartialByRole(User, (List<Dispatch>)ViewData["rolePartial"]); %>

public static class MyHelper {
 public static void RenderPartialByRole(this HtmlHelper helper, IPrincipal user, List<Dispatch> rolePartial) {
  foreach (Dispatch d in rolePartial) {
   if (d.CheckRole(user)) {
    helper.RenderPartial(d.PartialName);
    break;
   }
  }
 }
}

public class Dispatch {
 string _roleName;
 string _partialName;

 public Dispatch(string roleName, string partialName) {
  _roleName = roleName;
  _partialName = partialName;
 }
 public bool CheckRole(IPrincipal user) {
  return user.IsInRole(_roleName);
 }
 public string PartialName {
  get { return _partialName; }
 }
}

public class HomeController : Controller {

 List<Dispatch> rolePartial = new List<Dispatch>();
 private void InitTable() {
  rolePartial.Add(new Dispatch("admin", "adminPartial"));
  rolePartial.Add(new Dispatch("report", "reportPartial"));
  rolePartial.Add(new Dispatch("guest", "guestPartial"));
 }

 public HomeController() {
  InitTable();
 }

 public ActionResult Index() {
  ViewData["rolePartial"] = rolePartial;
  return View();
 }
}
Keith Morgan