1

We're working on an ASP.NET MVC 5 app in Visual Studio 2015 Update 3. Users are already being authenticated via Identity Framework and we're using authorization to limit who can access which controllers/actions. However, we now need something more fine-grained: On the actions, we need to limit what records a user can view and edit.

For example, a user goes to the GET version of MyController/Edit/5, we only want this user to be able to fetch items s/he has access to based on some rules, and not view records from other users by simply changing the URL to MyController/Edit/9999 (changing the ID in this case). In some cases, the rules are more complex, so it's not always the ID. And the same rules apply to POST actions.

We're using view models (VMs) to send and receive data to/from views. So one VM might be ItemVm. We want that customer to only be able to have access to their own ItemVms and not other users'. Here's a sample from a controller:

public async Task<ActionResult> Edit(int id)
{   
    // Only allow users to see "items" they have access to 
    // based on custom rules. If they enter a different ID,
    // prevent them from viewing/editing that information.
}

[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Edit(ItemVm itemVm){
    // Only allow users to edit their own "items".
}

This article talks about locking down based on resources, but wasn't sure if that's what is needed in this case. Would we do this via a custom authorize attribute? If so, how do you pass in the ItemVm and apply your rules? Of course, it's possible to add a check inside each and every action before proceeding; is there a best-practices way?

Your help is much appreciated. Thanks.

2
  • 1
    Normally I've added the check inside the action (or the model, depending on the design of the system) because I consider this to be more custom logic and less framework logic. Consider two orthogonal aspects of authorization: "Vertical" authorization is framework-based and determines what operations your users are allowed to perform. Standard identity stuff in the framework handles this easily. "Horizontal" authorization is more custom and can change with business logic. It determines what data (at the record level or even value level) your users are allowed to access. Commented Sep 14, 2017 at 15:59
  • 1
    Why don't you just filter the query to your datasource by user id? I mean, if you know who created the record, you can pass a filter (always) to the query that returns records , even though s/he changes the URL to MyController/Edit/9999, if s/he didn't entered that record, the query should't return anything. Commented Sep 14, 2017 at 16:26

2 Answers 2

1

The specifics would change, but might look something like this:

public async Task<ActionResult> Edit(int id)
{   
  // Only allow users to see "items" they have access to 
  // based on custom rules. If they enter a different ID,
  // prevent them from viewing/editing that information.
  string userId = User.Identity.GetUserId();
  var items = context.Items.Where(m => m.Userid == userId);
  return View(items);
}

[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Edit(ItemVm itemVm){
  // Only allow users to edit their own "items".
  string userId = User.Identity.GetUserId();
  var item = context.Items.Where(m => m.Id == itemVm.ItemId && m.UserId == userId);
  if (item != null)
  {
    //update item stuff
  }
  return View();
}
Sign up to request clarification or add additional context in comments.

Comments

1

Focusing on your last paragraph.

This article talks about locking down based on resources, but wasn't sure if that's what is needed in this case.

If your project is built using .NET core, then yes. Otherwise your project is built using .NET framework, which means the article is irrelevant.

Would we do this via a custom authorize attribute? If so, how do you pass in the ItemVm and apply your rules?

Assuming a .NET core project: A custom authorize attribute is not needed. Instead, you'll inject the IAuthorizationService dependency into the Controller (the article and this video (start at around 27:00) will have a lot more information)

Of course, it's possible to add a check inside each and every action before proceeding; is there a best-practices way?

Not sure what the best-practice would be, but I would create a parent controller which has a method for authorizing resources based on the current user.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.