1

In my application, I have two roles: Administrators and Users. Administrators can assign Users to allow them to perform specific functions to a specific object.

Since this goes beyond the scope of what the SimpleMembership Roles can provide, I have a static helper method that checks whether a user has access to a specific function:

public static class SecurityCheck
{
    public static bool UserHasAccess(int objectId, string functionName)
    {
        // Decorates the security provider -- gets logged in User ID and calls to a repository to query the database
        // ...
    }
}

Which I can then use in my views to determine whether or not a specific function should be rendered for that user based on the object's ID:

@foreach (var item in Model.FooData)
{
    <tr>
        <td>
            @Html.DisplayFor(modelItem => item.Name)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.Notes)
        </td>
        <td>
            @Html.ActionLink("View Data", "View", new { @id = item.Id })
            @if (SecurityCheck.UserHasAccess(item.id, "Edit Data"))
            {
                @Html.ActionLink("Edit Data", "Edit", new {@id = item.Id})
            }
            @if (SecurityCheck.UserHasAccess(item.id, "Delete"))
            {
                @Html.ActionLink("Delete", "Delete", new {@id = item.Id})
            }
        </td>
    </tr>
}

I have to believe there is a better way to do this, since each individual call to the static method involves a separate round-trip to the database, but I am stuck on where the best place would be to put the code. Some thoughts I have considered:

  1. Adding methods to my ViewModels to pass a list of functions to a repository, returning a list of the functions the user can perform for each object. Thinking about this further, I'm not even sure this is possible, as ugly as it would be.
  2. Keep the ViewModel dumb, and have my application layer service fetch the available functions. This would involve adding additional properties to my domain model objects, which I am not crazy about.
  3. Create a separate service that can be called from the controller that can populate the function list to the ViewModel. This would involve having multiple services injected into each controller -- also not crazy about this.

I'm leaning towards #2., but I still feel like I am overlooking what would be a more solid implementation. Has anyone dealt with something similar to this before?

bnice7
  • 45
  • 1
  • 7

2 Answers2

3

I think each ViewModel "knows" what can be done with it, isn't it? So we can make implicit explicit. The ViewModel can explicitly have properties such as CanEdit, CanDelete, etc.

The UI should not care why some operations are allowed or not, it simply checks these properties in a way:

@if (item.CanEdit)
{
   @Html.ActionLink("Edit Data", "Edit", new {@id = item.Id})
}

You can even come up with a helper that takes another boolean as a parameter to decide whether the control should be rendered (or enabled) or not, but it is minor:

@Html.SecureActionLink(item.CanEdit, "Edit Data", "Edit", new {@id = item.Id})

The idea is that it is not the responsibility of the UI to know how to figure out whether something is permitted due to some business rules or not. But it is definitely UI's responsibility to know how and what to render in one ViewModel is not Editable or another is ReadOnly (different things can have different states).

Also, since we are talking about DDD I would advice against modeling CRUD operations. In the end of the day DDD is about Ubiquitous Language, and "Create, Update, Delete" is hardly a language business really speaks.

So you will end up with more precise and meaningful properties/operations in your models, such as CanAccept (for order screens) or `CanMakeRefund" (for payments).

You resolve/set these properties when you build up your ViewModel and apply security context to it.

Hope it helps.

Alexey Raga
  • 7,457
  • 1
  • 31
  • 40
  • Understood -- and this makes perfect sense and is exactly inline with the approach I decided to go with. I will post my final code once I get it wrapped up. To add, I am already handling user access/validation as a **business rule**, along with some role checking in the form of action filter attributes, but one of the requirements for this project is to the hide functions from view to the users who cannot access them. So this is purely cosmetic. – bnice7 Dec 05 '13 at 14:48
0

Maybe you need to use SimpleMembership roles:

Assigning Roles with MVC SimpleMembership

In the standard MVC membership you can just use something like:

Roles.AddUserToRole(model.UserName, "Admin");

And in you View e.g.:

if (ViewContext.HttpContext.User.IsInRole("Admin"))
Community
  • 1
  • 1
Odrai
  • 2,163
  • 2
  • 31
  • 62
  • With the problem I am trying to solve, roles will not help since the user is assigned access to specific objects. Adminstrators will assign objects to Users. But I need a way to check whether someone in the Users role has access to perform a specific function to a given object. – bnice7 Dec 04 '13 at 19:16
  • Maybe this helps? http://stackoverflow.com/questions/13201250/how-to-get-custom-annotation-attributes-for-a-controller-action-in-asp-net-mvc-4 – Odrai Dec 04 '13 at 19:21
  • Thanks Odrai. The answer to that question looks very close to what I already have, except they are just using an Html helper method instead of calling the static method directly. I think this solution would result in the same problem I am trying to avoid, which is making multiple round-trip calls to the database. I think this would be fine if it were just a few actions I was trying to control on a given view, but my application will have several. – bnice7 Dec 04 '13 at 19:31