Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Claim based authorization design for conditional edit operation in ASP.NET MVC App

Designing an ASP.Net MVC application authorization using claim based model. Lets say that we have an object called - Product. Typically, there are 4 different actions - Create, Edit, Delete and View. Authorization is done using ClaimsAuthorize attribute.

[Authorize]
public class ProductController : Controller
{

     [ClaimsAuthorize("Product", "VIEW")]
     public List<Product> GetProducts()
     {
         // ....
     }

     [ClaimsAuthorize("Product", "CREATE")]
     public Product CreateNewProduct(Product product)
     {
         //....
     }
}

But in my case, I have to support different types of EDIT permissions:

  1. Some Users can Edit the product if the same user has created the Product originally

  2. Some users can Edit the product if the Product belongs to a specific category and the user also has access to the same category

  3. Some users can Edit all the products (this is the normal Product Edit operation)

How do you elegantly authorize all these Edit operations (preferably attribute driven as shown above) and at the same time I want to keep the authorization code separate from the normal MVC controller code and business logic.

[Above code sample is not syntactically correct, I just made it up for the purpose of explaining this question] Let me know your thoughts.

like image 675
Andy T Avatar asked Aug 06 '15 03:08

Andy T


1 Answers

For first part of your question, Claim based authorization, I have already answered it in this similar question. And I am not going to repeat here.

But for your another rules like products editable only by owner. You could write separate AuthorizeAttribute for each rule and apply them on your Actions consider this as an simple example:

using Microsoft.AspNet.Identity;
public class OwnerAuthorizeAttribute : AuthorizeAttribute
{
    private string _keyName;
    public bool IsPost { get; set; }

    public OwnerAuthorizeAttribute(string keyName)
    {
        _keyName = keyName;
    }

    protected override bool AuthorizeCore(HttpContextBase httpContext)
    {
        // imagine you have a service which could check owner of 
        // product based on userID and ProductID

        return httpContext.User.Identity.IsAuthenticated
            && this.ContainsKey
            && _productService.IsOwner(httpContext.User.Identity.GetUserId(),
                int.Parse(this.KeyValue.ToString()));
    }

    private bool ContainsKey
    {
        get
        {
            return IsPost
                ? HttpContext.Current.Request.Form.AllKeys.Contains(_keyName)
                // for simplicity I just check route data 
                // in real world you might need to check query string too 
                : ((MvcHandler)HttpContext.Current.Handler).RequestContext
                     .RouteData.Values.ContainsKey(_keyName);
        }
    }
    private object KeyValue
    {
        get
        {
            return IsPost
                ? HttpContext.Current.Request.Form[_keyName]
                // for simplicity I just check route data 
                // in real world you might need to check query string too 
                : ((MvcHandler)HttpContext.Current.Handler)
                    .RequestContext.RouteData.Values[_keyName];
        }
    }
}

You could repeat same pattern to your other rules too.

And you could simply apply your custom attributes to your actions:

[OwnerAuthorize("id")]
public ActionResult Edit(int id)
{
    // your code
}

[HttpPost]
// double checking in post back too 
[OwnerAuthorize("id", IsPost = true)]
public ActionResult Edit(Product product)
{
    // your code
}

It is obvious you could apply more then one AuthorizeAttribute to your actions. In this case all of them must return true.

[ClaimsAuthorize("Product", "EDIT")]
[OwnerAuthorize("id")]
[YetOtherAuthorize]
public ActionResult MyFancyAction(int id)
{
}
like image 97
Sam FarajpourGhamari Avatar answered Sep 24 '22 07:09

Sam FarajpourGhamari