Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MVC / ASP.Net Best Practice for Record-Level Authorization

Does anyone have any suggestions on a good methodology for enforcing record-level authorization while maintaining separation of concerns within an ASP.Net MVC web site?

With PrincipalPermission, you can decorate a method with:

PrincipalPermission(SecurityAction.Demand, Role = "GroupLeader")

to require that any visitor to that page be a member of the fixed role "GroupLeader".

Alternatively, you could decorate a method with:

[ClaimsPrincipalPermission(SecurityAction.Demand, Operation = "Manage", Resource = "Group")]

to require that any visitor to that page be allowed generically to manage a group.

However, neither of these really address the scenario in which a user might have permission to edit some groups, but not others. As far as I can tell, even if I implement a custom ClaimsAuthorizationManager, there is no way to get access to the method parameters in order to do conditional authorization.

Additionally, both of the methods above just throw a SecurityException if the user doesn't have access rather than allowing a method to gracefully redirect the user to a page that explains what happened and what they might do to gain the necessary authorization.

Obviously, I also realize that I could just code authorization logic into the method itself and redirect accordingly, but I would rather separate that out, if possible, and keep the method code clean to only handle actually processing the request and also to be able to work within a framework that can be more generically applied.

So, is there an "out-of-the-box" way to handle this scenario, or do I have to implement a custom IAuthorizationFilter? I think I could handle both the record-level authorization and the graceful redirect, but without any parameters at the method level, it would be essentially a massive if ... else if statement.

like image 701
Paul Haag Avatar asked Sep 23 '14 15:09

Paul Haag


1 Answers

The most straight forward way to handle this situation is creating a property on your entities to store the "owner" or "creator" of the entity. Then, when you query objects, you filter by this. For example:

public class Foo
{
    public int Id { get; set; }

    ...

    public string Creator { get; set; }
}

Then:

public ActionResult FooDetails(int id)
{
    var foo = db.Foos.SingleOrDefault(m => m.Id == id && m.Creator == User.Identity.Name);
    if (foo == null)
    {
        return new HttpNotFoundResult();
    }

    return View(foo);
}

It's not really appropriate to handle this via an authorization filter because you have to select the row to determine the user's permissions. Then, you must select the row again inside the action to send it to the view. Also, you'd have to do some acrobatics to get the filter to be able to know how it should select what from where so that you can even check the permissions in the first place. In the action, you're already selecting the entity, so just condition it based on whether the user "owns" it at the same time.

like image 50
Chris Pratt Avatar answered Nov 05 '22 12:11

Chris Pratt