views:

445

answers:

2

Suppose a high-speed developer was tasked with building a banking application which would be accessed by many different people. Each person would want to access his or her own account information but would not want others to access it. I would like to know the best practice for restricting access in an MVC application so that only the user who owns the information (or an administrator) could access it.

The Authorize attribute allows us to restrict by role. While this is a starting point, it seems that any authenticated user could gain access to any other user's information.

ActionFilters seem to offer the option for a little more granular control and could probably be used to accomplish the task. However, I am unclear as to whether they would be the recommended approach.

Any guidance or ideas are welcome.

A: 

I think you have it about right, the ActionFilter approach is a sound one.

I'd create a set of custom action filters that inherited from AuthorizeAttribute.

In addition to the funnctionality of the Authorize attribute you could implement a more stringent owner only policy cleanly.

HTH,

Dan

Daniel Elliott
Thank you Dan! What are your suggestions for implementing a "more stringent owner only policy"? Is this something akin to what Mark has described above?
Exactly Anthony, the last paragraph in particular of Mark's answer. I would derive your custom filters from the Authorize filter as you are going to need an authorized IPrincipal for checking against the ACL.
Daniel Elliott
+2  A: 

ActionFilter is probably a good starting point, but depending on your architecture, you may want to consider whether perimeter defense is good enough.

If you are essentially building a single-layer ASP.NET MVC application (and there may be perfectly reasonable reasons to do this), an ActionFilter will provide defense that is good enough while at the same time being very simply to apply.

On the other hand, if your application is a multi-layer application, Defense in Depth is more appropriate. In that case, you should consider applying the authorization logic in the Domain Model, or perhaps even in the Data Access layer. This will ensure that if you ever develop another application based on the same Domain Model (e.g. a web service), the authorization logic would still apply.

No matter what you do, I strongly recommend that you base the actual authorization implementation on IPrincipal.

On a more specific note, what you are asking about here is best modeled with ACL-based authorization: Set an ACL on each user profile that by default grants access to only the user him/herself and the administrator. If/when you later need to expand the application to allow delegated access to other users' profiles (I don't know whether that's even remotely realistic in your specific case), you can simply do that by adding a new entry to the ACL.

In such a case, evaluating access involves retrieving the ACL for the requested resource and checking whether the current user (IPrincipal) is included in that ACL. Such an operation is very likely to involve out-of-process operations (looking up the ACL in a database), so having it as an implicit part of an application by hiding it behind an ActionFilter sounds like it could potentially hide some performance issues. In such a case, I would consider making the authorization model a bit more explict/visible.

Mark Seemann
When a user attempts to perform an action (on a controller), I check his roles to make sure he has "basic" access. If not, I redirect him. If he does have basic access, I call a more granular function to check his actual permissions against the specific object type and/or instance he is trying to access. Right now, I have not yet implemented this as an action filter but am just calling this within each action. I will refactor it shortly.
Glad that you could use the answer - you may also want to check out this answer that is somewhat related: http://stackoverflow.com/questions/1335315/access-control-in-asp-net-mvc-depending-on-input-parameters-service-layer/1336404#1336404
Mark Seemann