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:
Some Users can Edit the product if the same user has created the Product originally
Some users can Edit the product if the Product belongs to a specific category and the user also has access to the same category
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.
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)
{
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With